• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Blue noise generation using the void-and-cluster method as described in
2  *
3  *     The void-and-cluster method for dither array generation
4  *     Ulichney, Robert A (1993)
5  *
6  *     http://cv.ulichney.com/papers/1993-void-cluster.pdf
7  *
8  * Note that running with openmp (-DUSE_OPENMP) will trigger additional
9  * randomness due to computing reductions in parallel, and is not recommended
10  * unless generating very large dither arrays.
11  */
12 
13 #include <assert.h>
14 #include <stdlib.h>
15 #include <stdint.h>
16 #include <math.h>
17 #include <stdio.h>
18 
19 /* Booleans and utility functions */
20 
21 #ifndef TRUE
22 #   define TRUE 1
23 #endif
24 
25 #ifndef FALSE
26 #   define FALSE 0
27 #endif
28 
29 typedef int bool_t;
30 
31 int
imin(int x,int y)32 imin (int x, int y)
33 {
34     return x < y ? x : y;
35 }
36 
37 /* Memory allocation */
38 void *
malloc_abc(unsigned int a,unsigned int b,unsigned int c)39 malloc_abc (unsigned int a, unsigned int b, unsigned int c)
40 {
41     if (a >= INT32_MAX / b)
42 	return NULL;
43     else if (a * b >= INT32_MAX / c)
44 	return NULL;
45     else
46 	return malloc (a * b * c);
47 }
48 
49 /* Random number generation */
50 typedef uint32_t xorwow_state_t[5];
51 
52 uint32_t
xorwow_next(xorwow_state_t * state)53 xorwow_next (xorwow_state_t *state)
54 {
55     uint32_t s = (*state)[0],
56     t = (*state)[3];
57     (*state)[3] = (*state)[2];
58     (*state)[2] = (*state)[1];
59     (*state)[1] = s;
60 
61     t ^= t >> 2;
62     t ^= t << 1;
63     t ^= s ^ (s << 4);
64 
65     (*state)[0] = t;
66     (*state)[4] += 362437;
67 
68     return t + (*state)[4];
69 }
70 
71 float
xorwow_float(xorwow_state_t * s)72 xorwow_float (xorwow_state_t *s)
73 {
74     return (xorwow_next (s) >> 9) / (float)((1 << 23) - 1);
75 }
76 
77 /* Floating point matrices
78  *
79  * Used to cache the cluster sizes.
80  */
81 typedef struct matrix_t {
82     int width;
83     int height;
84     float *buffer;
85 } matrix_t;
86 
87 bool_t
matrix_init(matrix_t * matrix,int width,int height)88 matrix_init (matrix_t *matrix, int width, int height)
89 {
90     float *buffer;
91 
92     if (!matrix)
93 	return FALSE;
94 
95     buffer = malloc_abc (width, height, sizeof (float));
96 
97     if (!buffer)
98 	return FALSE;
99 
100     matrix->buffer = buffer;
101     matrix->width  = width;
102     matrix->height = height;
103 
104     return TRUE;
105 }
106 
107 bool_t
matrix_copy(matrix_t * dst,matrix_t const * src)108 matrix_copy (matrix_t *dst, matrix_t const *src)
109 {
110     float *srcbuf = src->buffer,
111 	  *srcend = src->buffer + src->width * src->height,
112 	  *dstbuf = dst->buffer;
113 
114     if (dst->width != src->width || dst->height != src->height)
115 	return FALSE;
116 
117     while (srcbuf < srcend)
118 	*dstbuf++ = *srcbuf++;
119 
120     return TRUE;
121 }
122 
123 float *
matrix_get(matrix_t * matrix,int x,int y)124 matrix_get (matrix_t *matrix, int x, int y)
125 {
126     return &matrix->buffer[y * matrix->width + x];
127 }
128 
129 void
matrix_destroy(matrix_t * matrix)130 matrix_destroy (matrix_t *matrix)
131 {
132     free (matrix->buffer);
133 }
134 
135 /* Binary patterns */
136 typedef struct pattern_t {
137     int width;
138     int height;
139     bool_t *buffer;
140 } pattern_t;
141 
142 bool_t
pattern_init(pattern_t * pattern,int width,int height)143 pattern_init (pattern_t *pattern, int width, int height)
144 {
145     bool_t *buffer;
146 
147     if (!pattern)
148 	return FALSE;
149 
150     buffer = malloc_abc (width, height, sizeof (bool_t));
151 
152     if (!buffer)
153 	return FALSE;
154 
155     pattern->buffer = buffer;
156     pattern->width  = width;
157     pattern->height = height;
158 
159     return TRUE;
160 }
161 
162 bool_t
pattern_copy(pattern_t * dst,pattern_t const * src)163 pattern_copy (pattern_t *dst, pattern_t const *src)
164 {
165     bool_t *srcbuf = src->buffer,
166 	   *srcend = src->buffer + src->width * src->height,
167 	   *dstbuf = dst->buffer;
168 
169     if (dst->width != src->width || dst->height != src->height)
170 	return FALSE;
171 
172     while (srcbuf < srcend)
173 	*dstbuf++ = *srcbuf++;
174 
175     return TRUE;
176 }
177 
178 bool_t *
pattern_get(pattern_t * pattern,int x,int y)179 pattern_get (pattern_t *pattern, int x, int y)
180 {
181     return &pattern->buffer[y * pattern->width + x];
182 }
183 
184 void
pattern_fill_white_noise(pattern_t * pattern,float fraction,xorwow_state_t * s)185 pattern_fill_white_noise (pattern_t *pattern, float fraction,
186 			  xorwow_state_t *s)
187 {
188     bool_t *buffer = pattern->buffer;
189     bool_t *end    = buffer + (pattern->width * pattern->height);
190 
191     while (buffer < end)
192 	*buffer++ = xorwow_float (s) < fraction;
193 }
194 
195 void
pattern_destroy(pattern_t * pattern)196 pattern_destroy (pattern_t *pattern)
197 {
198     free (pattern->buffer);
199 }
200 
201 /* Dither arrays */
202 typedef struct array_t {
203     int width;
204     int height;
205     uint32_t *buffer;
206 } array_t;
207 
208 bool_t
array_init(array_t * array,int width,int height)209 array_init (array_t *array, int width, int height)
210 {
211     uint32_t *buffer;
212 
213     if (!array)
214 	return FALSE;
215 
216     buffer = malloc_abc (width, height, sizeof (uint32_t));
217 
218     if (!buffer)
219 	return FALSE;
220 
221     array->buffer = buffer;
222     array->width  = width;
223     array->height = height;
224 
225     return TRUE;
226 }
227 
228 uint32_t *
array_get(array_t * array,int x,int y)229 array_get (array_t *array, int x, int y)
230 {
231     return &array->buffer[y * array->width + x];
232 }
233 
234 bool_t
array_save_ppm(array_t * array,const char * filename)235 array_save_ppm (array_t *array, const char *filename)
236 {
237     FILE *f = fopen(filename, "wb");
238 
239     int i   = 0;
240     int bpp = 2;
241     uint8_t buffer[1024];
242 
243     if (!f)
244 	return FALSE;
245 
246     if (array->width * array->height - 1 < 256)
247 	bpp = 1;
248 
249     fprintf(f, "P5 %d %d %d\n", array->width, array->height,
250 	    array->width * array->height - 1);
251     while (i < array->width * array->height)
252     {
253 	    int j = 0;
254 	    for (; j < 1024 / bpp && j < array->width * array->height; ++j)
255 	    {
256 		    uint32_t v = array->buffer[i + j];
257 		    if (bpp == 2)
258 		    {
259 			buffer[2 * j] = v & 0xff;
260 			buffer[2 * j + 1] = (v & 0xff00) >> 8;
261 		    } else {
262 			buffer[j] = v;
263 		    }
264 	    }
265 
266 	    fwrite((void *)buffer, bpp, j, f);
267 	    i += j;
268     }
269 
270     if (fclose(f) != 0)
271 	return FALSE;
272 
273     return TRUE;
274 }
275 
276 bool_t
array_save(array_t * array,const char * filename)277 array_save (array_t *array, const char *filename)
278 {
279     int x, y;
280     FILE *f = fopen(filename, "wb");
281 
282     if (!f)
283 	return FALSE;
284 
285     fprintf (f,
286 "/* WARNING: This file is generated by make-blue-noise.c\n"
287 " * Please edit that file instead of this one.\n"
288 " */\n"
289 "\n"
290 "#ifndef BLUE_NOISE_%dX%d_H\n"
291 "#define BLUE_NOISE_%dX%d_H\n"
292 "\n"
293 "#include <stdint.h>\n"
294 "\n", array->width, array->height, array->width, array->height);
295 
296     fprintf (f, "static const uint16_t dither_blue_noise_%dx%d[%d] = {\n",
297 	     array->width, array->height, array->width * array->height);
298 
299     for (y = 0; y < array->height; ++y)
300     {
301 	fprintf (f, "    ");
302 	for (x = 0; x < array->width; ++x)
303 	{
304 	    if (x != 0)
305 		fprintf (f, ", ");
306 
307 	    fprintf (f, "%d", *array_get (array, x, y));
308 	}
309 
310 	fprintf (f, ",\n");
311     }
312     fprintf (f, "};\n");
313 
314     fprintf (f, "\n#endif /* BLUE_NOISE_%dX%d_H */\n",
315 	     array->width, array->height);
316 
317     if (fclose(f) != 0)
318 	return FALSE;
319 
320     return TRUE;
321 }
322 
323 void
array_destroy(array_t * array)324 array_destroy (array_t *array)
325 {
326     free (array->buffer);
327 }
328 
329 /* Dither array generation */
330 bool_t
compute_cluster_sizes(pattern_t * pattern,matrix_t * matrix)331 compute_cluster_sizes (pattern_t *pattern, matrix_t *matrix)
332 {
333     int width  = pattern->width,
334 	height = pattern->height;
335 
336     if (matrix->width != width || matrix->height != height)
337 	return FALSE;
338 
339     int px, py, qx, qy, dx, dy;
340     float tsqsi = 2.f * 1.5f * 1.5f;
341 
342 #ifdef USE_OPENMP
343 #pragma omp parallel for default (none) \
344     private (py, px, qy, qx, dx, dy) \
345     shared (height, width, pattern, matrix, tsqsi)
346 #endif
347     for (py = 0; py < height; ++py)
348     {
349 	for (px = 0; px < width; ++px)
350 	{
351 	    bool_t pixel = *pattern_get (pattern, px, py);
352 	    float dist   = 0.f;
353 
354 	    for (qx = 0; qx < width; ++qx)
355 	    {
356 		dx = imin (abs (qx - px), width - abs (qx - px));
357 		dx = dx * dx;
358 
359 		for (qy = 0; qy < height; ++qy)
360 		{
361 		    dy = imin (abs (qy - py), height - abs (qy - py));
362 		    dy = dy * dy;
363 
364 		    dist += (pixel == *pattern_get (pattern, qx, qy))
365 			* expf (- (dx + dy) / tsqsi);
366 		}
367 	    }
368 
369 	    *matrix_get (matrix, px, py) = dist;
370 	}
371     }
372 
373     return TRUE;
374 }
375 
376 bool_t
swap_pixel(pattern_t * pattern,matrix_t * matrix,int x,int y)377 swap_pixel (pattern_t *pattern, matrix_t *matrix, int x, int y)
378 {
379     int width  = pattern->width,
380 	height = pattern->height;
381 
382     bool_t new;
383 
384     float f,
385           dist  = 0.f,
386 	  tsqsi = 2.f * 1.5f * 1.5f;
387 
388     int px, py, dx, dy;
389     bool_t b;
390 
391     new = !*pattern_get (pattern, x, y);
392     *pattern_get (pattern, x, y) = new;
393 
394     if (matrix->width != width || matrix->height != height)
395 	return FALSE;
396 
397 
398 #ifdef USE_OPENMP
399 #pragma omp parallel for reduction (+:dist) default (none) \
400     private (px, py, dx, dy, b, f) \
401     shared (x, y, width, height, pattern, matrix, new, tsqsi)
402 #endif
403     for (py = 0; py < height; ++py)
404     {
405 	dy = imin (abs (py - y), height - abs (py - y));
406 	dy = dy * dy;
407 
408 	for (px = 0; px < width; ++px)
409 	{
410 	    dx = imin (abs (px - x), width - abs (px - x));
411 	    dx = dx * dx;
412 
413 	    b = (*pattern_get (pattern, px, py) == new);
414 	    f = expf (- (dx + dy) / tsqsi);
415 	    *matrix_get (matrix, px, py) += (2 * b - 1) * f;
416 
417 	    dist += b * f;
418 	}
419     }
420 
421     *matrix_get (matrix, x, y) = dist;
422     return TRUE;
423 }
424 
425 void
largest_cluster(pattern_t * pattern,matrix_t * matrix,bool_t pixel,int * xmax,int * ymax)426 largest_cluster (pattern_t *pattern, matrix_t *matrix,
427 		 bool_t pixel, int *xmax, int *ymax)
428 {
429     int width       = pattern->width,
430 	height      = pattern->height;
431 
432     int   x, y;
433 
434     float vmax = -INFINITY;
435 
436 #ifdef USE_OPENMP
437 #pragma omp parallel default (none) \
438     private (x, y) \
439     shared (height, width, pattern, matrix, pixel, xmax, ymax, vmax)
440 #endif
441     {
442 	int xbest = -1,
443 	    ybest = -1;
444 
445 #ifdef USE_OPENMP
446 	float vbest = -INFINITY;
447 
448 #pragma omp for reduction (max: vmax) collapse (2)
449 #endif
450 	for (y = 0; y < height; ++y)
451 	{
452 	    for (x = 0; x < width; ++x)
453 	    {
454 		if (*pattern_get (pattern, x, y) != pixel)
455 		    continue;
456 
457 		if (*matrix_get (matrix, x, y) > vmax)
458 		{
459 		    vmax = *matrix_get (matrix, x, y);
460 #ifdef USE_OPENMP
461 		    vbest = vmax;
462 #endif
463 		    xbest = x;
464 		    ybest = y;
465 		}
466 	    }
467 	}
468 
469 #ifdef USE_OPENMP
470 #pragma omp barrier
471 #pragma omp critical
472 	{
473 	    if (vmax == vbest)
474 	    {
475 		*xmax = xbest;
476 		*ymax = ybest;
477 	    }
478 	}
479 #else
480 	*xmax = xbest;
481 	*ymax = ybest;
482 #endif
483     }
484 
485     assert (vmax > -INFINITY);
486 }
487 
488 void
generate_initial_binary_pattern(pattern_t * pattern,matrix_t * matrix)489 generate_initial_binary_pattern (pattern_t *pattern, matrix_t *matrix)
490 {
491     int xcluster = 0,
492 	ycluster = 0,
493 	xvoid    = 0,
494 	yvoid    = 0;
495 
496     for (;;)
497     {
498 	largest_cluster (pattern, matrix, TRUE, &xcluster, &ycluster);
499 	assert (*pattern_get (pattern, xcluster, ycluster) == TRUE);
500 	swap_pixel (pattern, matrix, xcluster, ycluster);
501 
502 	largest_cluster (pattern, matrix, FALSE, &xvoid, &yvoid);
503 	assert (*pattern_get (pattern, xvoid, yvoid) == FALSE);
504 	swap_pixel (pattern, matrix, xvoid, yvoid);
505 
506 	if (xcluster == xvoid && ycluster == yvoid)
507 	    return;
508     }
509 }
510 
511 bool_t
generate_dither_array(array_t * array,pattern_t const * prototype,matrix_t const * matrix,pattern_t * temp_pattern,matrix_t * temp_matrix)512 generate_dither_array (array_t *array,
513 		       pattern_t const *prototype, matrix_t const *matrix,
514 		       pattern_t *temp_pattern, matrix_t *temp_matrix)
515 {
516     int width        = prototype->width,
517 	height       = prototype->height;
518 
519     int x, y, rank;
520 
521     int initial_rank = 0;
522 
523     if (array->width != width || array->height != height)
524 	return FALSE;
525 
526     // Make copies of the prototype and associated sizes matrix since we will
527     // trash them
528     if (!pattern_copy (temp_pattern, prototype))
529 	return FALSE;
530 
531     if (!matrix_copy (temp_matrix, matrix))
532 	return FALSE;
533 
534     // Compute initial rank
535     for (y = 0; y < height; ++y)
536     {
537 	for (x = 0; x < width; ++x)
538 	{
539 	    if (*pattern_get (temp_pattern, x, y))
540 		initial_rank += 1;
541 
542 	    *array_get (array, x, y) = 0;
543 	}
544     }
545 
546     // Phase 1
547     for (rank = initial_rank; rank > 0; --rank)
548     {
549 	largest_cluster (temp_pattern, temp_matrix, TRUE, &x, &y);
550 	swap_pixel (temp_pattern, temp_matrix, x, y);
551 	*array_get (array, x, y) = rank - 1;
552     }
553 
554     // Make copies again for phases 2 & 3
555     if (!pattern_copy (temp_pattern, prototype))
556 	return FALSE;
557 
558     if (!matrix_copy (temp_matrix, matrix))
559 	return FALSE;
560 
561     // Phase 2 & 3
562     for (rank = initial_rank; rank < width * height; ++rank)
563     {
564 	largest_cluster (temp_pattern, temp_matrix, FALSE, &x, &y);
565 	swap_pixel (temp_pattern, temp_matrix, x, y);
566 	*array_get (array, x, y) = rank;
567     }
568 
569     return TRUE;
570 }
571 
572 bool_t
generate(int size,xorwow_state_t * s,char const * c_filename,char const * ppm_filename)573 generate (int size, xorwow_state_t *s,
574 	  char const *c_filename, char const *ppm_filename)
575 {
576     bool_t ok = TRUE;
577 
578     pattern_t prototype, temp_pattern;
579     array_t   array;
580     matrix_t  matrix, temp_matrix;
581 
582     printf ("Generating %dx%d blue noise...\n", size, size);
583 
584     if (!pattern_init (&prototype, size, size))
585 	return FALSE;
586 
587     if (!pattern_init (&temp_pattern, size, size))
588     {
589 	pattern_destroy (&prototype);
590 	return FALSE;
591     }
592 
593     if (!matrix_init (&matrix, size, size))
594     {
595 	pattern_destroy (&temp_pattern);
596 	pattern_destroy (&prototype);
597 	return FALSE;
598     }
599 
600     if (!matrix_init (&temp_matrix, size, size))
601     {
602 	matrix_destroy (&matrix);
603 	pattern_destroy (&temp_pattern);
604 	pattern_destroy (&prototype);
605 	return FALSE;
606     }
607 
608     if (!array_init (&array, size, size))
609     {
610 	matrix_destroy (&temp_matrix);
611 	matrix_destroy (&matrix);
612 	pattern_destroy (&temp_pattern);
613 	pattern_destroy (&prototype);
614 	return FALSE;
615     }
616 
617     printf("Filling initial binary pattern with white noise...\n");
618     pattern_fill_white_noise (&prototype, .1, s);
619 
620     printf("Initializing cluster sizes...\n");
621     if (!compute_cluster_sizes (&prototype, &matrix))
622     {
623 	fprintf (stderr, "Error while computing cluster sizes\n");
624 	ok = FALSE;
625 	goto out;
626     }
627 
628     printf("Generating initial binary pattern...\n");
629     generate_initial_binary_pattern (&prototype, &matrix);
630 
631     printf("Generating dither array...\n");
632     if (!generate_dither_array (&array, &prototype, &matrix,
633 			 &temp_pattern, &temp_matrix))
634     {
635 	fprintf (stderr, "Error while generating dither array\n");
636 	ok = FALSE;
637 	goto out;
638     }
639 
640     printf("Saving dither array...\n");
641     if (!array_save (&array, c_filename))
642     {
643 	fprintf (stderr, "Error saving dither array\n");
644 	ok = FALSE;
645 	goto out;
646     }
647 
648 #if SAVE_PPM
649     if (!array_save_ppm (&array, ppm_filename))
650     {
651 	fprintf (stderr, "Error saving dither array PPM\n");
652 	ok = FALSE;
653 	goto out;
654     }
655 #else
656     (void)ppm_filename;
657 #endif
658 
659     printf("All done!\n");
660 
661 out:
662     array_destroy (&array);
663     matrix_destroy (&temp_matrix);
664     matrix_destroy (&matrix);
665     pattern_destroy (&temp_pattern);
666     pattern_destroy (&prototype);
667     return ok;
668 }
669 
670 int
main(void)671 main (void)
672 {
673     xorwow_state_t s = {1185956906, 12385940, 983948, 349208051, 901842};
674 
675     if (!generate (64, &s, "blue-noise-64x64.h", "blue-noise-64x64.ppm"))
676 	return -1;
677 
678     return 0;
679 }
680