source: libcaca/trunk/caca/dither.c @ 2826

Last change on this file since 2826 was 2826, checked in by sam, 6 years ago

Continue the libcaca/libcucul merge. Source and binary compatibility
are almost there.

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