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

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