"Fossies" - the Fresh Open Source Software Archive 
Member "libcaca-0.99.beta20/caca/dither.c" (27 Feb 2019, 42194 Bytes) of package /linux/privat/libcaca-0.99.beta20.tar.bz2:
1 /*
2 * libcaca Colour ASCII-Art library
3 * Copyright © 2002—2018 Sam Hocevar <sam@hocevar.net>
4 * All Rights Reserved
5 *
6 * This library is free software. It comes without any warranty, to
7 * the extent permitted by applicable law. You can redistribute it
8 * and/or modify it under the terms of the Do What the Fuck You Want
9 * to Public License, Version 2, as published by the WTFPL Task Force.
10 * See http://www.wtfpl.net/ for more details.
11 */
12
13 /*
14 * This file contains bitmap dithering functions.
15 */
16
17 #include "config.h"
18
19 #if !defined(__KERNEL__)
20 # if defined(HAVE_ENDIAN_H)
21 # include <endian.h>
22 # endif
23 # include <stdio.h>
24 # include <stdlib.h>
25 # include <limits.h>
26 # include <string.h>
27 #endif
28
29 #include "caca.h"
30 #include "caca_internals.h"
31
32 #define CP437 0
33
34 /*
35 * Local variables
36 */
37 #if !defined(_DOXYGEN_SKIP_ME)
38 # define LOOKUP_VAL 32
39 # define LOOKUP_SAT 32
40 # define LOOKUP_HUE 16
41 #endif
42 static uint8_t hsv_distances[LOOKUP_VAL][LOOKUP_SAT][LOOKUP_HUE];
43 static uint16_t lookup_colors[8];
44 static int lookup_initialised = 0;
45
46 static int const hsv_palette[] =
47 {
48 /* weight, hue, saturation, value */
49 4, 0x0, 0x0, 0x0, /* black */
50 5, 0x0, 0x0, 0x5ff, /* 30% */
51 5, 0x0, 0x0, 0x9ff, /* 70% */
52 4, 0x0, 0x0, 0xfff, /* white */
53 3, 0x1000, 0xfff, 0x5ff, /* dark yellow */
54 2, 0x1000, 0xfff, 0xfff, /* light yellow */
55 3, 0x0, 0xfff, 0x5ff, /* dark red */
56 2, 0x0, 0xfff, 0xfff /* light red */
57 };
58
59 /* RGB palette for the new colour picker */
60 static int const rgb_palette[] =
61 {
62 0x0, 0x0, 0x0,
63 0x0, 0x0, 0x7ff,
64 0x0, 0x7ff, 0x0,
65 0x0, 0x7ff, 0x7ff,
66 0x7ff, 0x0, 0x0,
67 0x7ff, 0x0, 0x7ff,
68 0x7ff, 0x7ff, 0x0,
69 0xaaa, 0xaaa, 0xaaa,
70 0x555, 0x555, 0x555,
71 0x000, 0x000, 0xfff,
72 0x000, 0xfff, 0x000,
73 0x000, 0xfff, 0xfff,
74 0xfff, 0x000, 0x000,
75 0xfff, 0x000, 0xfff,
76 0xfff, 0xfff, 0x000,
77 0xfff, 0xfff, 0xfff,
78 };
79
80 static int const rgb_weight[] =
81 {
82 /* 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2 */
83 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
84 };
85
86 /* List of glyphs */
87 static uint32_t ascii_glyphs[] =
88 {
89 ' ', '.', ':', ';', 't', '%', 'S', 'X', '@', '8', '?'
90 };
91
92 static uint32_t shades_glyphs[] =
93 {
94 /* ' '. '·', '░', '▒', '?' */
95 ' ', 0xb7, 0x2591, 0x2592, '?'
96 };
97
98 static uint32_t blocks_glyphs[] =
99 {
100 /* ' ', '▘', '▚', '?' */
101 ' ', 0x2598, 0x259a, '?'
102 };
103
104 #if !defined(_DOXYGEN_SKIP_ME)
105 enum color_mode
106 {
107 COLOR_MODE_MONO,
108 COLOR_MODE_GRAY,
109 COLOR_MODE_8,
110 COLOR_MODE_16,
111 COLOR_MODE_FULLGRAY,
112 COLOR_MODE_FULL8,
113 COLOR_MODE_FULL16
114 };
115
116 struct caca_dither
117 {
118 int bpp, has_palette, has_alpha;
119 size_t w, h, pitch;
120 int rmask, gmask, bmask, amask;
121 int rright, gright, bright, aright;
122 int rleft, gleft, bleft, aleft;
123 void (*get_hsv)(caca_dither_t *, char *, int, int);
124 int red[256], green[256], blue[256], alpha[256];
125
126 /* Colour features */
127 float gamma, brightness, contrast;
128 int gammatab[4097];
129
130 /* Dithering features */
131 char const *antialias_name;
132 int antialias;
133
134 char const *color_name;
135 enum color_mode color;
136
137 char const *algo_name;
138 void (*init_dither) (int);
139 int (*get_dither) (void);
140 void (*increment_dither) (void);
141
142 char const *glyph_name;
143 uint32_t const * glyphs;
144 int glyph_count;
145
146 int invert;
147 };
148
149 #define HSV_XRATIO 6
150 #define HSV_YRATIO 3
151 #define HSV_HRATIO 3
152
153 #define HSV_DISTANCE(h, s, v, index) \
154 (hsv_palette[index * 4] \
155 * ((HSV_XRATIO * ((v) - hsv_palette[index * 4 + 3]) \
156 * ((v) - hsv_palette[index * 4 + 3])) \
157 + (hsv_palette[index * 4 + 3] \
158 ? (HSV_YRATIO * ((s) - hsv_palette[index * 4 + 2]) \
159 * ((s) - hsv_palette[index * 4 + 2])) \
160 : 0) \
161 + (hsv_palette[index * 4 + 2] \
162 ? (HSV_HRATIO * ((h) - hsv_palette[index * 4 + 1]) \
163 * ((h) - hsv_palette[index * 4 + 1])) \
164 : 0)))
165 #endif
166
167 /*
168 * Local prototypes
169 */
170 static void mask2shift(uint32_t, int *, int *);
171 static float gammapow(float x, float y);
172
173 static void get_rgba_default(caca_dither_t const *, uint8_t const *, int, int,
174 unsigned int *);
175 static int init_lookup(void);
176
177 /* Dithering algorithms */
178 static void init_no_dither(int);
179 static int get_no_dither(void);
180 static void increment_no_dither(void);
181
182 static void init_fstein_dither(int);
183 static int get_fstein_dither(void);
184 static void increment_fstein_dither(void);
185
186 static void init_ordered2_dither(int);
187 static int get_ordered2_dither(void);
188 static void increment_ordered2_dither(void);
189
190 static void init_ordered4_dither(int);
191 static int get_ordered4_dither(void);
192 static void increment_ordered4_dither(void);
193
194 static void init_ordered8_dither(int);
195 static int get_ordered8_dither(void);
196 static void increment_ordered8_dither(void);
197
198 static void init_random_dither(int);
199 static int get_random_dither(void);
200 static void increment_random_dither(void);
201
202 static inline int sq(int x)
203 {
204 return x * x;
205 }
206
207 static inline void rgb2hsv_default(int r, int g, int b,
208 int *hue, int *sat, int *val)
209 {
210 int min, max, delta;
211
212 min = r; max = r;
213 if(min > g) min = g; if(max < g) max = g;
214 if(min > b) min = b; if(max < b) max = b;
215
216 delta = max - min; /* 0 - 0xfff */
217 *val = max; /* 0 - 0xfff */
218
219 if(delta)
220 {
221 *sat = 0xfff * delta / max; /* 0 - 0xfff */
222
223 /* Generate *hue between 0 and 0x5fff */
224 if( r == max )
225 *hue = 0x1000 + 0x1000 * (g - b) / delta;
226 else if( g == max )
227 *hue = 0x3000 + 0x1000 * (b - r) / delta;
228 else
229 *hue = 0x5000 + 0x1000 * (r - g) / delta;
230 }
231 else
232 {
233 *sat = 0;
234 *hue = 0;
235 }
236 }
237
238 /** \brief Create an internal dither object.
239 *
240 * Create a dither structure from its coordinates (depth, width, height and
241 * pitch) and pixel mask values. If the depth is 8 bits per pixel, the mask
242 * values are ignored and the colour palette should be set using the
243 * caca_set_dither_palette() function. For depths greater than 8 bits per
244 * pixel, a zero alpha mask causes the alpha values to be ignored.
245 *
246 * If an error occurs, NULL is returned and \b errno is set accordingly:
247 * - \c EINVAL Requested width, height, pitch or bits per pixel value was
248 * invalid.
249 * - \c ENOMEM Not enough memory to allocate dither structure.
250 *
251 * \param bpp Bitmap depth in bits per pixel.
252 * \param w Bitmap width in pixels.
253 * \param h Bitmap height in pixels.
254 * \param pitch Bitmap pitch in bytes.
255 * \param rmask Bitmask for red values.
256 * \param gmask Bitmask for green values.
257 * \param bmask Bitmask for blue values.
258 * \param amask Bitmask for alpha values.
259 * \return Dither object upon success, NULL if an error occurred.
260 */
261 caca_dither_t *caca_create_dither(int bpp, int w, int h, int pitch,
262 uint32_t rmask, uint32_t gmask,
263 uint32_t bmask, uint32_t amask)
264 {
265 caca_dither_t *d;
266 int i;
267
268 /* Minor sanity test */
269 if(w < 0 || h < 0 || pitch < 0 || bpp > 32 || bpp < 8)
270 {
271 seterrno(EINVAL);
272 return NULL;
273 }
274
275 d = malloc(sizeof(caca_dither_t));
276 if(!d)
277 {
278 seterrno(ENOMEM);
279 return NULL;
280 }
281
282 if(!lookup_initialised)
283 {
284 /* XXX: because we do not wish to be thread-safe, there is a slight
285 * chance that the following code will be executed twice. It is
286 * totally harmless. */
287 init_lookup();
288 lookup_initialised = 1;
289 }
290
291 d->bpp = bpp;
292 d->has_palette = 0;
293 d->has_alpha = amask ? 1 : 0;
294
295 d->w = w;
296 d->h = h;
297 d->pitch = pitch;
298
299 d->rmask = rmask;
300 d->gmask = gmask;
301 d->bmask = bmask;
302 d->amask = amask;
303
304 /* Load bitmasks */
305 if(rmask || gmask || bmask || amask)
306 {
307 mask2shift(rmask, &d->rright, &d->rleft);
308 mask2shift(gmask, &d->gright, &d->gleft);
309 mask2shift(bmask, &d->bright, &d->bleft);
310 mask2shift(amask, &d->aright, &d->aleft);
311 }
312
313 /* In 8 bpp mode, default to a grayscale palette */
314 if(bpp == 8)
315 {
316 d->has_palette = 1;
317 d->has_alpha = 0;
318 for(i = 0; i < 256; i++)
319 {
320 d->red[i] = i * 0xfff / 256;
321 d->green[i] = i * 0xfff / 256;
322 d->blue[i] = i * 0xfff / 256;
323 }
324 }
325
326 /* Default gamma value */
327 d->gamma = 1.0;
328 for(i = 0; i < 4096; i++)
329 d->gammatab[i] = i;
330
331 /* Default colour properties */
332 d->brightness = 1.0;
333 d->contrast = 1.0;
334
335 /* Default features */
336 d->antialias_name = "prefilter";
337 d->antialias = 1;
338
339 d->color_name = "full16";
340 d->color = COLOR_MODE_FULL16;
341
342 d->glyph_name = "ascii";
343 d->glyphs = ascii_glyphs;
344 d->glyph_count = sizeof(ascii_glyphs) / sizeof(*ascii_glyphs);
345
346 d->algo_name = "fstein";
347 d->init_dither = init_fstein_dither;
348 d->get_dither = get_fstein_dither;
349 d->increment_dither = increment_fstein_dither;
350
351 d->invert = 0;
352
353 return d;
354 }
355
356 /** \brief Set the palette of an 8bpp dither object.
357 *
358 * Set the palette of an 8 bits per pixel bitmap. Values should be between
359 * 0 and 4095 (0xfff).
360 *
361 * If an error occurs, -1 is returned and \b errno is set accordingly:
362 * - \c EINVAL Dither bits per pixel value is not 8, or one of the pixel
363 * values was outside the range 0 - 4095.
364 *
365 * \param d Dither object.
366 * \param red Array of 256 red values.
367 * \param green Array of 256 green values.
368 * \param blue Array of 256 blue values.
369 * \param alpha Array of 256 alpha values.
370 * \return 0 in case of success, -1 if an error occurred.
371 */
372 int caca_set_dither_palette(caca_dither_t *d,
373 uint32_t red[], uint32_t green[],
374 uint32_t blue[], uint32_t alpha[])
375 {
376 int i, has_alpha = 0;
377
378 if(d->bpp != 8)
379 {
380 seterrno(EINVAL);
381 return -1;
382 }
383
384 for(i = 0; i < 256; i++)
385 {
386 if((red[i] | green[i] | blue[i] | alpha[i]) >= 0x1000)
387 {
388 seterrno(EINVAL);
389 return -1;
390 }
391 }
392
393 for(i = 0; i < 256; i++)
394 {
395 d->red[i] = red[i];
396 d->green[i] = green[i];
397 d->blue[i] = blue[i];
398 if(alpha[i])
399 {
400 d->alpha[i] = alpha[i];
401 has_alpha = 1;
402 }
403 }
404
405 d->has_alpha = has_alpha;
406
407 return 0;
408 }
409
410 /** \brief Set the brightness of a dither object.
411 *
412 * Set the brightness of dither.
413 *
414 * If an error occurs, -1 is returned and \b errno is set accordingly:
415 * - \c EINVAL Brightness value was out of range.
416 *
417 * \param d Dither object.
418 * \param brightness brightness value.
419 * \return 0 in case of success, -1 if an error occurred.
420 */
421 int caca_set_dither_brightness(caca_dither_t *d, float brightness)
422 {
423 /* FIXME */
424 d->brightness = brightness;
425
426 return 0;
427 }
428
429 /** \brief Get the brightness of a dither object.
430 *
431 * Get the brightness of the given dither object.
432 *
433 * This function never fails.
434 *
435 * \param d Dither object.
436 * \return Brightness value.
437 */
438 float caca_get_dither_brightness(caca_dither_t const *d)
439 {
440 return d->brightness;
441 }
442
443 /** \brief Set the gamma of a dither object.
444 *
445 * Set the gamma of the given dither object. A negative value causes
446 * colour inversion.
447 *
448 * If an error occurs, -1 is returned and \b errno is set accordingly:
449 * - \c EINVAL Gamma value was out of range.
450 *
451 * \param d Dither object.
452 * \param gamma Gamma value.
453 * \return 0 in case of success, -1 if an error occurred.
454 */
455 int caca_set_dither_gamma(caca_dither_t *d, float gamma)
456 {
457 /* FIXME: we don't need 4096 calls to gammapow(), we could just compute
458 * a few of them and do linear interpolation for the rest. This will
459 * probably speed up things a lot. */
460 int i;
461
462 if(gamma < 0.0)
463 {
464 d->invert = 1;
465 gamma = -gamma;
466 }
467 else if(gamma == 0.0)
468 {
469 seterrno(EINVAL);
470 return -1;
471 }
472
473 d->gamma = gamma;
474
475 for(i = 0; i < 4096; i++)
476 d->gammatab[i] = 4096.0 * gammapow((float)i / 4096.0, 1.0 / gamma);
477
478 return 0;
479 }
480
481 /** \brief Get the gamma of a dither object.
482 *
483 * Get the gamma of the given dither object.
484 *
485 * This function never fails.
486 *
487 * \param d Dither object.
488 * \return Gamma value.
489 */
490 float caca_get_dither_gamma(caca_dither_t const *d)
491 {
492 return d->gamma;
493 }
494
495 /** \brief Set the contrast of a dither object.
496 *
497 * Set the contrast of dither.
498 *
499 * If an error occurs, -1 is returned and \b errno is set accordingly:
500 * - \c EINVAL Contrast value was out of range.
501 *
502 * \param d Dither object.
503 * \param contrast contrast value.
504 * \return 0 in case of success, -1 if an error occurred.
505 */
506 int caca_set_dither_contrast(caca_dither_t *d, float contrast)
507 {
508 /* FIXME */
509 d->contrast = contrast;
510
511 return 0;
512 }
513
514 /** \brief Get the contrast of a dither object.
515 *
516 * Get the contrast of the given dither object.
517 *
518 * This function never fails.
519 *
520 * \param d Dither object.
521 * \return Contrast value.
522 */
523 float caca_get_dither_contrast(caca_dither_t const *d)
524 {
525 return d->contrast;
526 }
527
528 /** \brief Set dither antialiasing
529 *
530 * Tell the renderer whether to antialias the dither. Antialiasing smoothens
531 * the rendered image and avoids the commonly seen staircase effect.
532 * - \c "none": no antialiasing.
533 * - \c "prefilter" or \c "default": simple prefilter antialiasing. This
534 * is the default value.
535 *
536 * If an error occurs, -1 is returned and \b errno is set accordingly:
537 * - \c EINVAL Invalid antialiasing mode.
538 *
539 * \param d Dither object.
540 * \param str A string describing the antialiasing method that will be used
541 * for the dithering.
542 * \return 0 in case of success, -1 if an error occurred.
543 */
544 int caca_set_dither_antialias(caca_dither_t *d, char const *str)
545 {
546 if(!strcasecmp(str, "none"))
547 {
548 d->antialias_name = "none";
549 d->antialias = 0;
550 }
551 else if(!strcasecmp(str, "prefilter") || !strcasecmp(str, "default"))
552 {
553 d->antialias_name = "prefilter";
554 d->antialias = 1;
555 }
556 else
557 {
558 seterrno(EINVAL);
559 return -1;
560 }
561
562 return 0;
563 }
564
565 /** \brief Get available antialiasing methods
566 *
567 * Return a list of available antialiasing methods for a given dither. The
568 * list is a NULL-terminated array of strings, interleaving a string
569 * containing the internal value for the antialiasing method to be used with
570 * caca_set_dither_antialias(), and a string containing the natural
571 * language description for that antialiasing method.
572 *
573 * This function never fails.
574 *
575 * \param d Dither object.
576 * \return An array of strings.
577 */
578 char const * const *
579 caca_get_dither_antialias_list(caca_dither_t const *d)
580 {
581 static char const * const list[] =
582 {
583 "none", "No antialiasing",
584 "prefilter", "Prefilter antialiasing",
585 NULL, NULL
586 };
587
588 return list;
589 }
590
591 /** \brief Get current antialiasing method
592 *
593 * Return the given dither's current antialiasing method.
594 *
595 * This function never fails.
596 *
597 * \param d Dither object.
598 * \return A static string.
599 */
600 char const * caca_get_dither_antialias(caca_dither_t const *d)
601 {
602 return d->antialias_name;
603 }
604
605 /** \brief Choose colours used for dithering
606 *
607 * Tell the renderer which colours should be used to render the
608 * bitmap. Valid values for \c str are:
609 * - \c "mono": use light gray on a black background.
610 * - \c "gray": use white and two shades of gray on a black background.
611 * - \c "8": use the 8 ANSI colours on a black background.
612 * - \c "16": use the 16 ANSI colours on a black background.
613 * - \c "fullgray": use black, white and two shades of gray for both the
614 * characters and the background.
615 * - \c "full8": use the 8 ANSI colours for both the characters and the
616 * background.
617 * - \c "full16" or \c "default": use the 16 ANSI colours for both the
618 * characters and the background. This is the default value.
619 *
620 * If an error occurs, -1 is returned and \b errno is set accordingly:
621 * - \c EINVAL Invalid colour set.
622 *
623 * \param d Dither object.
624 * \param str A string describing the colour set that will be used
625 * for the dithering.
626 * \return 0 in case of success, -1 if an error occurred.
627 */
628 int caca_set_dither_color(caca_dither_t *d, char const *str)
629 {
630 if(!strcasecmp(str, "mono"))
631 {
632 d->color_name = "mono";
633 d->color = COLOR_MODE_MONO;
634 }
635 else if(!strcasecmp(str, "gray"))
636 {
637 d->color_name = "gray";
638 d->color = COLOR_MODE_GRAY;
639 }
640 else if(!strcasecmp(str, "8"))
641 {
642 d->color_name = "8";
643 d->color = COLOR_MODE_8;
644 }
645 else if(!strcasecmp(str, "16"))
646 {
647 d->color_name = "16";
648 d->color = COLOR_MODE_16;
649 }
650 else if(!strcasecmp(str, "fullgray"))
651 {
652 d->color_name = "fullgray";
653 d->color = COLOR_MODE_FULLGRAY;
654 }
655 else if(!strcasecmp(str, "full8"))
656 {
657 d->color_name = "full8";
658 d->color = COLOR_MODE_FULL8;
659 }
660 else if(!strcasecmp(str, "full16") || !strcasecmp(str, "default"))
661 {
662 d->color_name = "full16";
663 d->color = COLOR_MODE_FULL16;
664 }
665 else
666 {
667 seterrno(EINVAL);
668 return -1;
669 }
670
671 return 0;
672 }
673
674 /** \brief Get available colour modes
675 *
676 * Return a list of available colour modes for a given dither. The list
677 * is a NULL-terminated array of strings, interleaving a string containing
678 * the internal value for the colour mode, to be used with
679 * caca_set_dither_color(), and a string containing the natural
680 * language description for that colour mode.
681 *
682 * This function never fails.
683 *
684 * \param d Dither object.
685 * \return An array of strings.
686 */
687 char const * const *
688 caca_get_dither_color_list(caca_dither_t const *d)
689 {
690 static char const * const list[] =
691 {
692 "mono", "white on black",
693 "gray", "grayscale on black",
694 "8", "8 colours on black",
695 "16", "16 colours on black",
696 "fullgray", "full grayscale",
697 "full8", "full 8 colours",
698 "full16", "full 16 colours",
699 NULL, NULL
700 };
701
702 return list;
703 }
704
705 /** \brief Get current colour mode
706 *
707 * Return the given dither's current colour mode.
708 *
709 * This function never fails.
710 *
711 * \param d Dither object.
712 * \return A static string.
713 */
714 char const * caca_get_dither_color(caca_dither_t const *d)
715 {
716 return d->color_name;
717 }
718
719 /** \brief Choose characters used for dithering
720 *
721 * Tell the renderer which characters should be used to render the
722 * dither. Valid values for \c str are:
723 * - \c "ascii" or \c "default": use only ASCII characters. This is the
724 * default value.
725 * - \c "shades": use Unicode characters "U+2591 LIGHT SHADE", "U+2592
726 * MEDIUM SHADE" and "U+2593 DARK SHADE". These characters are also
727 * present in the CP437 codepage available on DOS and VGA.
728 * - \c "blocks": use Unicode quarter-cell block combinations. These
729 * characters are only found in the Unicode set.
730 *
731 * If an error occurs, -1 is returned and \b errno is set accordingly:
732 * - \c EINVAL Invalid character set.
733 *
734 * \param d Dither object.
735 * \param str A string describing the characters that need to be used
736 * for the dithering.
737 * \return 0 in case of success, -1 if an error occurred.
738 */
739 int caca_set_dither_charset(caca_dither_t *d, char const *str)
740 {
741 if(!strcasecmp(str, "shades"))
742 {
743 d->glyph_name = "shades";
744 d->glyphs = shades_glyphs;
745 d->glyph_count = sizeof(shades_glyphs) / sizeof(*shades_glyphs);
746 }
747 else if(!strcasecmp(str, "blocks"))
748 {
749 d->glyph_name = "blocks";
750 d->glyphs = blocks_glyphs;
751 d->glyph_count = sizeof(blocks_glyphs) / sizeof(*blocks_glyphs);
752 }
753 else if(!strcasecmp(str, "ascii") || !strcasecmp(str, "default"))
754 {
755 d->glyph_name = "ascii";
756 d->glyphs = ascii_glyphs;
757 d->glyph_count = sizeof(ascii_glyphs) / sizeof(*ascii_glyphs);
758 }
759 else
760 {
761 seterrno(EINVAL);
762 return -1;
763 }
764
765 return 0;
766 }
767
768 /** \brief Get available dither character sets
769 *
770 * Return a list of available character sets for a given dither. The list
771 * is a NULL-terminated array of strings, interleaving a string containing
772 * the internal value for the character set, to be used with
773 * caca_set_dither_charset(), and a string containing the natural
774 * language description for that character set.
775 *
776 * This function never fails.
777 *
778 * \param d Dither object.
779 * \return An array of strings.
780 */
781 char const * const * caca_get_dither_charset_list(caca_dither_t const *d)
782 {
783 static char const * const list[] =
784 {
785 "ascii", "plain ASCII",
786 "shades", "CP437 shades",
787 "blocks", "Unicode blocks",
788 NULL, NULL
789 };
790
791 return list;
792 }
793
794 /** \brief Get current character set
795 *
796 * Return the given dither's current character set.
797 *
798 * This function never fails.
799 *
800 * \param d Dither object.
801 * \return A static string.
802 */
803 char const * caca_get_dither_charset(caca_dither_t const *d)
804 {
805 return d->glyph_name;
806 }
807
808 /** \brief Set dithering algorithm
809 *
810 * Tell the renderer which dithering algorithm should be used. Dithering is
811 * necessary because the picture being rendered has usually far more colours
812 * than the available palette. Valid values for \c str are:
813 * - \c "none": no dithering is used, the nearest matching colour is used.
814 * - \c "ordered2": use a 2x2 Bayer matrix for dithering.
815 * - \c "ordered4": use a 4x4 Bayer matrix for dithering.
816 * - \c "ordered8": use a 8x8 Bayer matrix for dithering.
817 * - \c "random": use random dithering.
818 * - \c "fstein": use Floyd-Steinberg dithering. This is the default value.
819 *
820 * If an error occurs, -1 is returned and \b errno is set accordingly:
821 * - \c EINVAL Unknown dithering mode.
822 *
823 * \param d Dither object.
824 * \param str A string describing the algorithm that needs to be used
825 * for the dithering.
826 * \return 0 in case of success, -1 if an error occurred.
827 */
828 int caca_set_dither_algorithm(caca_dither_t *d, char const *str)
829 {
830 if(!strcasecmp(str, "none"))
831 {
832 d->algo_name = "none";
833 d->init_dither = init_no_dither;
834 d->get_dither = get_no_dither;
835 d->increment_dither = increment_no_dither;
836 }
837 else if(!strcasecmp(str, "ordered2"))
838 {
839 d->algo_name = "ordered2";
840 d->init_dither = init_ordered2_dither;
841 d->get_dither = get_ordered2_dither;
842 d->increment_dither = increment_ordered2_dither;
843 }
844 else if(!strcasecmp(str, "ordered4"))
845 {
846 d->algo_name = "ordered4";
847 d->init_dither = init_ordered4_dither;
848 d->get_dither = get_ordered4_dither;
849 d->increment_dither = increment_ordered4_dither;
850 }
851 else if(!strcasecmp(str, "ordered8"))
852 {
853 d->algo_name = "ordered8";
854 d->init_dither = init_ordered8_dither;
855 d->get_dither = get_ordered8_dither;
856 d->increment_dither = increment_ordered8_dither;
857 }
858 else if(!strcasecmp(str, "random"))
859 {
860 d->algo_name = "random";
861 d->init_dither = init_random_dither;
862 d->get_dither = get_random_dither;
863 d->increment_dither = increment_random_dither;
864 }
865 else if(!strcasecmp(str, "fstein") || !strcasecmp(str, "default"))
866 {
867 d->algo_name = "fstein";
868 d->init_dither = init_fstein_dither;
869 d->get_dither = get_fstein_dither;
870 d->increment_dither = increment_fstein_dither;
871 }
872 else
873 {
874 seterrno(EINVAL);
875 return -1;
876 }
877
878 return 0;
879 }
880
881 /** \brief Get dithering algorithms
882 *
883 * Return a list of available dithering algorithms for a given dither. The
884 * list is a NULL-terminated array of strings, interleaving a string
885 * containing the internal value for the dithering algorithm, to be used
886 * with caca_set_dither_dithering(), and a string containing the natural
887 * language description for that algorithm.
888 *
889 * This function never fails.
890 *
891 * \param d Dither object.
892 * \return An array of strings.
893 */
894 char const * const * caca_get_dither_algorithm_list(caca_dither_t const *d)
895 {
896 static char const * const list[] =
897 {
898 "none", "no dithering",
899 "ordered2", "2x2 ordered dithering",
900 "ordered4", "4x4 ordered dithering",
901 "ordered8", "8x8 ordered dithering",
902 "random", "random dithering",
903 "fstein", "Floyd-Steinberg dithering",
904 NULL, NULL
905 };
906
907 return list;
908 }
909
910 /** \brief Get current dithering algorithm
911 *
912 * Return the given dither's current dithering algorithm.
913 *
914 * This function never fails.
915 *
916 * \param d Dither object.
917 * \return A static string.
918 */
919 char const * caca_get_dither_algorithm(caca_dither_t const *d)
920 {
921 return d->algo_name;
922 }
923
924 /** \brief Dither a bitmap on the canvas.
925 *
926 * Dither a bitmap at the given coordinates. The dither can be of any size
927 * and will be stretched to the text area.
928 *
929 * This function never fails.
930 *
931 * \param cv A handle to the libcaca canvas.
932 * \param x X coordinate of the upper-left corner of the drawing area.
933 * \param y Y coordinate of the upper-left corner of the drawing area.
934 * \param w Width of the drawing area.
935 * \param h Height of the drawing area.
936 * \param d Dither object to be drawn.
937 * \param pixels Bitmap's pixels.
938 * \return This function always returns 0.
939 */
940 int caca_dither_bitmap(caca_canvas_t *cv, int x, int y, int w, int h,
941 caca_dither_t const *d, void const *pixels)
942 {
943 int *floyd_steinberg, *fs_r, *fs_g, *fs_b;
944 uint32_t savedattr;
945 int fs_length;
946 int x1, y1, x2, y2, pitch, deltax, deltay, dchmax;
947
948 if(!d || !pixels)
949 return 0;
950
951 savedattr = caca_get_attr(cv, -1, -1);
952
953 x1 = x; x2 = x + w - 1;
954 y1 = y; y2 = y + h - 1;
955
956 /* FIXME: do not overwrite arguments */
957 w = d->w;
958 h = d->h;
959 pitch = d->pitch;
960
961 deltax = x2 - x1 + 1;
962 deltay = y2 - y1 + 1;
963 dchmax = d->glyph_count;
964
965 fs_length = ((int)cv->width <= x2 ? (int)cv->width : x2) + 1;
966 floyd_steinberg = malloc(3 * (fs_length + 2) * sizeof(int));
967 memset(floyd_steinberg, 0, 3 * (fs_length + 2) * sizeof(int));
968 fs_r = floyd_steinberg + 1;
969 fs_g = fs_r + fs_length + 2;
970 fs_b = fs_g + fs_length + 2;
971
972 for(y = y1 > 0 ? y1 : 0; y <= y2 && y <= (int)cv->height; y++)
973 {
974 int remain_r = 0, remain_g = 0, remain_b = 0;
975
976 for(x = x1 > 0 ? x1 : 0, d->init_dither(y);
977 x <= x2 && x <= (int)cv->width;
978 x++)
979 {
980 unsigned int rgba[4];
981 int error[3];
982 int i, ch = 0, distmin;
983 int fg_r = 0, fg_g = 0, fg_b = 0, bg_r, bg_g, bg_b;
984 int fromx, fromy, tox, toy, myx, myy, dots, dist;
985
986 int outfg = 0, outbg = 0;
987 uint32_t outch;
988
989 rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
990
991 /* First get RGB */
992 if(d->antialias)
993 {
994 fromx = (uint64_t)(x - x1) * w / deltax;
995 fromy = (uint64_t)(y - y1) * h / deltay;
996 tox = (uint64_t)(x - x1 + 1) * w / deltax;
997 toy = (uint64_t)(y - y1 + 1) * h / deltay;
998
999 /* We want at least one pixel */
1000 if(tox == fromx) tox++;
1001 if(toy == fromy) toy++;
1002
1003 dots = 0;
1004
1005 for(myx = fromx; myx < tox; myx++)
1006 for(myy = fromy; myy < toy; myy++)
1007 {
1008 dots++;
1009 get_rgba_default(d, pixels, myx, myy, rgba);
1010 }
1011
1012 /* Normalize */
1013 rgba[0] /= dots;
1014 rgba[1] /= dots;
1015 rgba[2] /= dots;
1016 rgba[3] /= dots;
1017 }
1018 else
1019 {
1020 fromx = (uint64_t)(x - x1) * w / deltax;
1021 fromy = (uint64_t)(y - y1) * h / deltay;
1022 tox = (uint64_t)(x - x1 + 1) * w / deltax;
1023 toy = (uint64_t)(y - y1 + 1) * h / deltay;
1024
1025 /* tox and toy can overflow the canvas, but they cannot overflow
1026 * when averaged with fromx and fromy because these are guaranteed
1027 * to be within the pixel boundaries. */
1028 myx = (fromx + tox) / 2;
1029 myy = (fromy + toy) / 2;
1030
1031 get_rgba_default(d, pixels, myx, myy, rgba);
1032 }
1033
1034 /* FIXME: hack to force greyscale */
1035 if(d->color == COLOR_MODE_FULLGRAY)
1036 {
1037 unsigned int gray = (3 * rgba[0] + 4 * rgba[1] + rgba[2] + 4) / 8;
1038 rgba[0] = rgba[1] = rgba[2] = gray;
1039 }
1040
1041 if(d->has_alpha && rgba[3] < 0x800)
1042 {
1043 remain_r = remain_g = remain_b = 0;
1044 fs_r[x] = 0;
1045 fs_g[x] = 0;
1046 fs_b[x] = 0;
1047 continue;
1048 }
1049
1050 /* XXX: OMG HAX */
1051 if(d->init_dither == init_fstein_dither)
1052 {
1053 rgba[0] += remain_r;
1054 rgba[1] += remain_g;
1055 rgba[2] += remain_b;
1056 }
1057 else
1058 {
1059 rgba[0] += (d->get_dither() - 0x80) * 4;
1060 rgba[1] += (d->get_dither() - 0x80) * 4;
1061 rgba[2] += (d->get_dither() - 0x80) * 4;
1062 }
1063
1064 distmin = INT_MAX;
1065 for(i = 0; i < 16; i++)
1066 {
1067 if(d->color == COLOR_MODE_FULLGRAY
1068 && (rgb_palette[i * 3] != rgb_palette[i * 3 + 1]
1069 || rgb_palette[i * 3] != rgb_palette[i * 3 + 2]))
1070 continue;
1071 dist = sq(rgba[0] - rgb_palette[i * 3])
1072 + sq(rgba[1] - rgb_palette[i * 3 + 1])
1073 + sq(rgba[2] - rgb_palette[i * 3 + 2]);
1074 dist *= rgb_weight[i];
1075 if(dist < distmin)
1076 {
1077 outbg = i;
1078 distmin = dist;
1079 }
1080 }
1081 bg_r = rgb_palette[outbg * 3];
1082 bg_g = rgb_palette[outbg * 3 + 1];
1083 bg_b = rgb_palette[outbg * 3 + 2];
1084
1085 /* FIXME: we currently only honour "full16" */
1086 if(d->color == COLOR_MODE_FULL16 || d->color == COLOR_MODE_FULLGRAY)
1087 {
1088 distmin = INT_MAX;
1089 for(i = 0; i < 16; i++)
1090 {
1091 if(i == outbg)
1092 continue;
1093 if(d->color == COLOR_MODE_FULLGRAY
1094 && (rgb_palette[i * 3] != rgb_palette[i * 3 + 1]
1095 || rgb_palette[i * 3] != rgb_palette[i * 3 + 2]))
1096 continue;
1097 dist = sq(rgba[0] - rgb_palette[i * 3])
1098 + sq(rgba[1] - rgb_palette[i * 3 + 1])
1099 + sq(rgba[2] - rgb_palette[i * 3 + 2]);
1100 dist *= rgb_weight[i];
1101 if(dist < distmin)
1102 {
1103 outfg = i;
1104 distmin = dist;
1105 }
1106 }
1107 fg_r = rgb_palette[outfg * 3];
1108 fg_g = rgb_palette[outfg * 3 + 1];
1109 fg_b = rgb_palette[outfg * 3 + 2];
1110
1111 distmin = INT_MAX;
1112 for(i = 0; i < dchmax - 1; i++)
1113 {
1114 int newr = i * fg_r + ((2*dchmax-1) - i) * bg_r;
1115 int newg = i * fg_g + ((2*dchmax-1) - i) * bg_g;
1116 int newb = i * fg_b + ((2*dchmax-1) - i) * bg_b;
1117 dist = abs(rgba[0] * (2*dchmax-1) - newr)
1118 + abs(rgba[1] * (2*dchmax-1) - newg)
1119 + abs(rgba[2] * (2*dchmax-1) - newb);
1120
1121 if(dist < distmin)
1122 {
1123 ch = i;
1124 distmin = dist;
1125 }
1126 }
1127 outch = d->glyphs[ch];
1128
1129 /* XXX: OMG HAX */
1130 if(d->init_dither == init_fstein_dither)
1131 {
1132 error[0] = rgba[0] - (fg_r * ch + bg_r * ((2*dchmax-1) - ch)) / (2*dchmax-1);
1133 error[1] = rgba[1] - (fg_g * ch + bg_g * ((2*dchmax-1) - ch)) / (2*dchmax-1);
1134 error[2] = rgba[2] - (fg_b * ch + bg_b * ((2*dchmax-1) - ch)) / (2*dchmax-1);
1135 }
1136 }
1137 else
1138 {
1139 unsigned int lum = rgba[0];
1140 if(rgba[1] > lum) lum = rgba[1];
1141 if(rgba[2] > lum) lum = rgba[2];
1142 outfg = outbg;
1143 outbg = CACA_BLACK;
1144
1145 ch = lum * dchmax / 0x1000;
1146 if(ch < 0)
1147 ch = 0;
1148 else if(ch > (int)(dchmax - 1))
1149 ch = dchmax - 1;
1150 outch = d->glyphs[ch];
1151
1152 /* XXX: OMG HAX */
1153 if(d->init_dither == init_fstein_dither)
1154 {
1155 error[0] = rgba[0] - bg_r * ch / (dchmax-1);
1156 error[1] = rgba[1] - bg_g * ch / (dchmax-1);
1157 error[2] = rgba[2] - bg_b * ch / (dchmax-1);
1158 }
1159 }
1160
1161 /* XXX: OMG HAX */
1162 if(d->init_dither == init_fstein_dither)
1163 {
1164 remain_r = fs_r[x+1] + 7 * error[0] / 16;
1165 remain_g = fs_g[x+1] + 7 * error[1] / 16;
1166 remain_b = fs_b[x+1] + 7 * error[2] / 16;
1167 fs_r[x-1] += 3 * error[0] / 16;
1168 fs_g[x-1] += 3 * error[1] / 16;
1169 fs_b[x-1] += 3 * error[2] / 16;
1170 fs_r[x] = 5 * error[0] / 16;
1171 fs_g[x] = 5 * error[1] / 16;
1172 fs_b[x] = 5 * error[2] / 16;
1173 fs_r[x+1] = 1 * error[0] / 16;
1174 fs_g[x+1] = 1 * error[1] / 16;
1175 fs_b[x+1] = 1 * error[2] / 16;
1176 }
1177
1178 if(d->invert)
1179 {
1180 outfg = 15 - outfg;
1181 outbg = 15 - outbg;
1182 }
1183
1184 /* Now output the character */
1185 caca_set_color_ansi(cv, outfg, outbg);
1186 caca_put_char(cv, x, y, outch);
1187
1188 d->increment_dither();
1189 }
1190 /* end loop */
1191 }
1192
1193 free(floyd_steinberg);
1194
1195 caca_set_attr(cv, savedattr);
1196
1197 return 0;
1198 }
1199
1200 /** \brief Free the memory associated with a dither.
1201 *
1202 * Free the memory allocated by caca_create_dither().
1203 *
1204 * This function never fails.
1205 *
1206 * \param d Dither object.
1207 * \return This function always returns 0.
1208 */
1209 int caca_free_dither(caca_dither_t *d)
1210 {
1211 if(!d)
1212 return 0;
1213
1214 free(d);
1215
1216 return 0;
1217 }
1218
1219 /*
1220 * XXX: The following functions are local.
1221 */
1222
1223 /* Convert a mask, eg. 0x0000ff00, to shift values, eg. 8 and -4. */
1224 static void mask2shift(uint32_t mask, int *right, int *left)
1225 {
1226 int rshift = 0, lshift = 0;
1227
1228 if(!mask)
1229 {
1230 *right = *left = 0;
1231 return;
1232 }
1233
1234 while(!(mask & 1))
1235 {
1236 mask >>= 1;
1237 rshift++;
1238 }
1239 *right = rshift;
1240
1241 while(mask & 1)
1242 {
1243 mask >>= 1;
1244 lshift++;
1245 }
1246 *left = 12 - lshift;
1247 }
1248
1249 /* Compute x^y without relying on the math library */
1250 static float gammapow(float x, float y)
1251 {
1252 #ifdef HAVE_FLDLN2
1253 register double logx;
1254 register long double v, e;
1255 #else
1256 register float tmp, t, t2, r;
1257 int i;
1258 #endif
1259
1260 if(x == 0.0)
1261 return y == 0.0 ? 1.0 : 0.0;
1262
1263 #ifdef HAVE_FLDLN2
1264 /* FIXME: this can be optimised by directly calling fyl2x for x and y */
1265 asm volatile("fldln2; fxch; fyl2x"
1266 : "=t" (logx) : "0" (x) : "st(1)");
1267
1268 asm volatile("fldl2e\n\t"
1269 "fmul %%st(1)\n\t"
1270 "fst %%st(1)\n\t"
1271 "frndint\n\t"
1272 "fxch\n\t"
1273 "fsub %%st(1)\n\t"
1274 "f2xm1\n\t"
1275 : "=t" (v), "=u" (e) : "0" (y * logx));
1276 v += 1.0;
1277 asm volatile("fscale"
1278 : "=t" (v) : "0" (v), "u" (e));
1279 return v;
1280 #else
1281 /* Compute ln(x) for x ∈ ]0,1]
1282 * ln(x) = 2 * (t + t^3/3 + t^5/5 + ...) with t = (x-1)/(x+1)
1283 * The convergence is a bit slow, especially when x is near 0. */
1284 t = (x - 1.0) / (x + 1.0);
1285 t2 = t * t;
1286 tmp = r = t;
1287 for(i = 3; i < 20; i += 2)
1288 {
1289 r *= t2;
1290 tmp += r / i;
1291 }
1292
1293 /* Compute -y*ln(x) */
1294 tmp = - y * 2.0 * tmp;
1295
1296 /* Compute x^-y as e^t where t = -y*ln(x):
1297 * e^t = 1 + t/1! + t^2/2! + t^3/3! + t^4/4! + t^5/5! ...
1298 * The convergence is quite faster here, thanks to the factorial. */
1299 r = t = tmp;
1300 tmp = 1.0 + t;
1301 for(i = 2; i < 16; i++)
1302 {
1303 r = r * t / i;
1304 tmp += r;
1305 }
1306
1307 /* Return x^y as 1/(x^-y) */
1308 return 1.0 / tmp;
1309 #endif
1310 }
1311
1312 static void get_rgba_default(caca_dither_t const *d, uint8_t const *pixels,
1313 int x, int y, unsigned int *rgba)
1314 {
1315 uint32_t bits;
1316
1317 pixels += (d->bpp / 8) * x + d->pitch * y;
1318
1319 switch(d->bpp / 8)
1320 {
1321 case 4:
1322 bits = *(uint32_t const *)pixels;
1323 break;
1324 case 3:
1325 {
1326 #if defined(HAVE_ENDIAN_H)
1327 if(__BYTE_ORDER == __BIG_ENDIAN)
1328 #else
1329 /* This is compile-time optimised with at least -O1 or -Os */
1330 uint32_t const tmp = 0x12345678;
1331 if(*(uint8_t const *)&tmp == 0x12)
1332 #endif
1333 bits = ((uint32_t)pixels[0] << 16) |
1334 ((uint32_t)pixels[1] << 8) |
1335 ((uint32_t)pixels[2]);
1336 else
1337 bits = ((uint32_t)pixels[2] << 16) |
1338 ((uint32_t)pixels[1] << 8) |
1339 ((uint32_t)pixels[0]);
1340 break;
1341 }
1342 case 2:
1343 bits = *(uint16_t const *)pixels;
1344 break;
1345 case 1:
1346 default:
1347 bits = pixels[0];
1348 break;
1349 }
1350
1351 if(d->has_palette)
1352 {
1353 rgba[0] += d->gammatab[d->red[bits]];
1354 rgba[1] += d->gammatab[d->green[bits]];
1355 rgba[2] += d->gammatab[d->blue[bits]];
1356 rgba[3] += d->alpha[bits];
1357 }
1358 else
1359 {
1360 rgba[0] += d->gammatab[((bits & d->rmask) >> d->rright) << d->rleft];
1361 rgba[1] += d->gammatab[((bits & d->gmask) >> d->gright) << d->gleft];
1362 rgba[2] += d->gammatab[((bits & d->bmask) >> d->bright) << d->bleft];
1363 rgba[3] += ((bits & d->amask) >> d->aright) << d->aleft;
1364 }
1365 }
1366
1367 /*
1368 * No dithering
1369 */
1370 static void init_no_dither(int line)
1371 {
1372 ;
1373 }
1374
1375 static int get_no_dither(void)
1376 {
1377 return 0x80;
1378 }
1379
1380 static void increment_no_dither(void)
1381 {
1382 return;
1383 }
1384
1385 /*
1386 * Floyd-Steinberg dithering
1387 */
1388 static void init_fstein_dither(int line)
1389 {
1390 ;
1391 }
1392
1393 static int get_fstein_dither(void)
1394 {
1395 return 0x80;
1396 }
1397
1398 static void increment_fstein_dither(void)
1399 {
1400 return;
1401 }
1402
1403 /*
1404 * Ordered 2 dithering
1405 */
1406 static int const *ordered2_table;
1407 static int ordered2_index;
1408
1409 static void init_ordered2_dither(int line)
1410 {
1411 static int const dither2x2[] =
1412 {
1413 0x00, 0x80,
1414 0xc0, 0x40,
1415 };
1416
1417 ordered2_table = dither2x2 + (line % 2) * 2;
1418 ordered2_index = 0;
1419 }
1420
1421 static int get_ordered2_dither(void)
1422 {
1423 return ordered2_table[ordered2_index];
1424 }
1425
1426 static void increment_ordered2_dither(void)
1427 {
1428 ordered2_index = (ordered2_index + 1) % 2;
1429 }
1430
1431 /*
1432 * Ordered 4 dithering
1433 */
1434 /*static int dither4x4[] = { 5, 0, 1, 6,
1435 -1, -6, -5, 2,
1436 -2, -7, -8, 3,
1437 4, -3, -4, -7};*/
1438 static int const *ordered4_table;
1439 static int ordered4_index;
1440
1441 static void init_ordered4_dither(int line)
1442 {
1443 static int const dither4x4[] =
1444 {
1445 0x00, 0x80, 0x20, 0xa0,
1446 0xc0, 0x40, 0xe0, 0x60,
1447 0x30, 0xb0, 0x10, 0x90,
1448 0xf0, 0x70, 0xd0, 0x50
1449 };
1450
1451 ordered4_table = dither4x4 + (line % 4) * 4;
1452 ordered4_index = 0;
1453 }
1454
1455 static int get_ordered4_dither(void)
1456 {
1457 return ordered4_table[ordered4_index];
1458 }
1459
1460 static void increment_ordered4_dither(void)
1461 {
1462 ordered4_index = (ordered4_index + 1) % 4;
1463 }
1464
1465 /*
1466 * Ordered 8 dithering
1467 */
1468 static int const *ordered8_table;
1469 static int ordered8_index;
1470
1471 static void init_ordered8_dither(int line)
1472 {
1473 static int const dither8x8[] =
1474 {
1475 0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8,
1476 0xc0, 0x40, 0xe0, 0x60, 0xc8, 0x48, 0xe8, 0x68,
1477 0x30, 0xb0, 0x10, 0x90, 0x38, 0xb8, 0x18, 0x98,
1478 0xf0, 0x70, 0xd0, 0x50, 0xf8, 0x78, 0xd8, 0x58,
1479 0x0c, 0x8c, 0x2c, 0xac, 0x04, 0x84, 0x24, 0xa4,
1480 0xcc, 0x4c, 0xec, 0x6c, 0xc4, 0x44, 0xe4, 0x64,
1481 0x3c, 0xbc, 0x1c, 0x9c, 0x34, 0xb4, 0x14, 0x94,
1482 0xfc, 0x7c, 0xdc, 0x5c, 0xf4, 0x74, 0xd4, 0x54,
1483 };
1484
1485 ordered8_table = dither8x8 + (line % 8) * 8;
1486 ordered8_index = 0;
1487 }
1488
1489 static int get_ordered8_dither(void)
1490 {
1491 return ordered8_table[ordered8_index];
1492 }
1493
1494 static void increment_ordered8_dither(void)
1495 {
1496 ordered8_index = (ordered8_index + 1) % 8;
1497 }
1498
1499 /*
1500 * Random dithering
1501 */
1502 static void init_random_dither(int line)
1503 {
1504 ;
1505 }
1506
1507 static int get_random_dither(void)
1508 {
1509 return caca_rand(0x00, 0x100);
1510 }
1511
1512 static void increment_random_dither(void)
1513 {
1514 return;
1515 }
1516
1517 /*
1518 * Lookup tables
1519 */
1520 static int init_lookup(void)
1521 {
1522 int v, s, h;
1523
1524 /* These ones are constant */
1525 lookup_colors[0] = CACA_BLACK;
1526 lookup_colors[1] = CACA_DARKGRAY;
1527 lookup_colors[2] = CACA_LIGHTGRAY;
1528 lookup_colors[3] = CACA_WHITE;
1529
1530 /* These ones will be overwritten */
1531 lookup_colors[4] = CACA_MAGENTA;
1532 lookup_colors[5] = CACA_LIGHTMAGENTA;
1533 lookup_colors[6] = CACA_RED;
1534 lookup_colors[7] = CACA_LIGHTRED;
1535
1536 for(v = 0; v < LOOKUP_VAL; v++)
1537 for(s = 0; s < LOOKUP_SAT; s++)
1538 for(h = 0; h < LOOKUP_HUE; h++)
1539 {
1540 int i, distbg, distfg, dist;
1541 int val, sat, hue;
1542 uint8_t outbg, outfg;
1543
1544 val = 0xfff * v / (LOOKUP_VAL - 1);
1545 sat = 0xfff * s / (LOOKUP_SAT - 1);
1546 hue = 0xfff * h / (LOOKUP_HUE - 1);
1547
1548 /* Initialise distances to the distance between pure black HSV
1549 * coordinates and our white colour (3) */
1550 outbg = outfg = 3;
1551 distbg = distfg = HSV_DISTANCE(0, 0, 0, 3);
1552
1553 /* Calculate distances to eight major colour values and store the
1554 * two nearest points in our lookup table. */
1555 for(i = 0; i < 8; i++)
1556 {
1557 dist = HSV_DISTANCE(hue, sat, val, i);
1558 if(dist <= distbg)
1559 {
1560 outfg = outbg;
1561 distfg = distbg;
1562 outbg = i;
1563 distbg = dist;
1564 }
1565 else if(dist <= distfg)
1566 {
1567 outfg = i;
1568 distfg = dist;
1569 }
1570 }
1571
1572 hsv_distances[v][s][h] = (outfg << 4) | outbg;
1573 }
1574
1575 return 0;
1576 }
1577