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