source: libcaca/trunk/cucul/dither.c @ 2303

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