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

Last change on this file since 4148 was 4148, checked in by Sam Hocevar, 10 years ago

Update my e-mail address everywhere.

  • Property svn:keywords set to Id
File size: 43.7 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id: dither.c 4148 2009-12-19 14:38:38Z 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 "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 const *, 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 const *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        /* FIXME: hack to force greyscale */
1037        if(d->color == COLOR_MODE_FULLGRAY)
1038        {
1039            unsigned int gray = (3 * rgba[0] + 4 * rgba[1] + rgba[2] + 4) / 8;
1040            rgba[0] = rgba[1] = rgba[2] = gray;
1041        }
1042
1043        if(d->has_alpha && rgba[3] < 0x800)
1044        {
1045            remain_r = remain_g = remain_b = 0;
1046            fs_r[x] = 0;
1047            fs_g[x] = 0;
1048            fs_b[x] = 0;
1049            continue;
1050        }
1051
1052        /* XXX: OMG HAX */
1053        if(d->init_dither == init_fstein_dither)
1054        {
1055            rgba[0] += remain_r;
1056            rgba[1] += remain_g;
1057            rgba[2] += remain_b;
1058        }
1059        else
1060        {
1061            rgba[0] += (d->get_dither() - 0x80) * 4;
1062            rgba[1] += (d->get_dither() - 0x80) * 4;
1063            rgba[2] += (d->get_dither() - 0x80) * 4;
1064        }
1065
1066        distmin = INT_MAX;
1067        for(i = 0; i < 16; i++)
1068        {
1069            if(d->color == COLOR_MODE_FULLGRAY
1070                && (rgb_palette[i * 3] != rgb_palette[i * 3 + 1]
1071                     || rgb_palette[i * 3] != rgb_palette[i * 3 + 2]))
1072                continue;
1073            dist = sq(rgba[0] - rgb_palette[i * 3])
1074                 + sq(rgba[1] - rgb_palette[i * 3 + 1])
1075                 + sq(rgba[2] - rgb_palette[i * 3 + 2]);
1076            dist *= rgb_weight[i];
1077            if(dist < distmin)
1078            {
1079                outbg = i;
1080                distmin = dist;
1081            }
1082        }
1083        bg_r = rgb_palette[outbg * 3];
1084        bg_g = rgb_palette[outbg * 3 + 1];
1085        bg_b = rgb_palette[outbg * 3 + 2];
1086
1087        /* FIXME: we currently only honour "full16" */
1088        if(d->color == COLOR_MODE_FULL16 || d->color == COLOR_MODE_FULLGRAY)
1089        {
1090            distmin = INT_MAX;
1091            for(i = 0; i < 16; i++)
1092            {
1093                if(i == outbg)
1094                    continue;
1095                if(d->color == COLOR_MODE_FULLGRAY
1096                    && (rgb_palette[i * 3] != rgb_palette[i * 3 + 1]
1097                         || rgb_palette[i * 3] != rgb_palette[i * 3 + 2]))
1098                    continue;
1099                dist = sq(rgba[0] - rgb_palette[i * 3])
1100                     + sq(rgba[1] - rgb_palette[i * 3 + 1])
1101                     + sq(rgba[2] - rgb_palette[i * 3 + 2]);
1102                dist *= rgb_weight[i];
1103                if(dist < distmin)
1104                {
1105                    outfg = i;
1106                    distmin = dist;
1107                }
1108            }
1109            fg_r = rgb_palette[outfg * 3];
1110            fg_g = rgb_palette[outfg * 3 + 1];
1111            fg_b = rgb_palette[outfg * 3 + 2];
1112
1113            distmin = INT_MAX;
1114            for(i = 0; i < dchmax - 1; i++)
1115            {
1116                int newr = i * fg_r + ((2*dchmax-1) - i) * bg_r;
1117                int newg = i * fg_g + ((2*dchmax-1) - i) * bg_g;
1118                int newb = i * fg_b + ((2*dchmax-1) - i) * bg_b;
1119                dist = abs(rgba[0] * (2*dchmax-1) - newr)
1120                     + abs(rgba[1] * (2*dchmax-1) - newg)
1121                     + abs(rgba[2] * (2*dchmax-1) - newb);
1122
1123                if(dist < distmin)
1124                {
1125                    ch = i;
1126                    distmin = dist;
1127                }
1128            }
1129            outch = d->glyphs[ch];
1130
1131            /* XXX: OMG HAX */
1132            if(d->init_dither == init_fstein_dither)
1133            {
1134                error[0] = rgba[0] - (fg_r * ch + bg_r * ((2*dchmax-1) - ch)) / (2*dchmax-1);
1135                error[1] = rgba[1] - (fg_g * ch + bg_g * ((2*dchmax-1) - ch)) / (2*dchmax-1);
1136                error[2] = rgba[2] - (fg_b * ch + bg_b * ((2*dchmax-1) - ch)) / (2*dchmax-1);
1137            }
1138        }
1139        else
1140        {
1141            unsigned int lum = rgba[0];
1142            if(rgba[1] > lum) lum = rgba[1];
1143            if(rgba[2] > lum) lum = rgba[2];
1144            outfg = outbg;
1145            outbg = CACA_BLACK;
1146
1147            ch = lum * dchmax / 0x1000;
1148            if(ch < 0)
1149                ch = 0;
1150            else if(ch > (int)(dchmax - 1))
1151                ch = dchmax - 1;
1152            outch = d->glyphs[ch];
1153
1154            /* XXX: OMG HAX */
1155            if(d->init_dither == init_fstein_dither)
1156            {
1157                error[0] = rgba[0] - bg_r * ch / (dchmax-1);
1158                error[1] = rgba[1] - bg_g * ch / (dchmax-1);
1159                error[2] = rgba[2] - bg_b * ch / (dchmax-1);
1160            }
1161        }
1162
1163        /* XXX: OMG HAX */
1164        if(d->init_dither == init_fstein_dither)
1165        {
1166            remain_r = fs_r[x+1] + 7 * error[0] / 16;
1167            remain_g = fs_g[x+1] + 7 * error[1] / 16;
1168            remain_b = fs_b[x+1] + 7 * error[2] / 16;
1169            fs_r[x-1] += 3 * error[0] / 16;
1170            fs_g[x-1] += 3 * error[1] / 16;
1171            fs_b[x-1] += 3 * error[2] / 16;
1172            fs_r[x] = 5 * error[0] / 16;
1173            fs_g[x] = 5 * error[1] / 16;
1174            fs_b[x] = 5 * error[2] / 16;
1175            fs_r[x+1] = 1 * error[0] / 16;
1176            fs_g[x+1] = 1 * error[1] / 16;
1177            fs_b[x+1] = 1 * error[2] / 16;
1178        }
1179
1180        if(d->invert)
1181        {
1182            outfg = 15 - outfg;
1183            outbg = 15 - outbg;
1184        }
1185
1186        /* Now output the character */
1187        caca_set_color_ansi(cv, outfg, outbg);
1188        caca_put_char(cv, x, y, outch);
1189
1190        d->increment_dither();
1191    }
1192        /* end loop */
1193    }
1194
1195    free(floyd_steinberg);
1196
1197    caca_set_attr(cv, savedattr);
1198
1199    return 0;
1200}
1201
1202/** \brief Free the memory associated with a dither.
1203 *
1204 *  Free the memory allocated by caca_create_dither().
1205 *
1206 *  This function never fails.
1207 *
1208 *  \param d Dither object.
1209 *  \return This function always returns 0.
1210 */
1211int caca_free_dither(caca_dither_t *d)
1212{
1213    if(!d)
1214        return 0;
1215
1216    free(d);
1217
1218    return 0;
1219}
1220
1221/*
1222 * XXX: The following functions are local.
1223 */
1224
1225/* Convert a mask, eg. 0x0000ff00, to shift values, eg. 8 and -4. */
1226static void mask2shift(uint32_t mask, int *right, int *left)
1227{
1228    int rshift = 0, lshift = 0;
1229
1230    if(!mask)
1231    {
1232        *right = *left = 0;
1233        return;
1234    }
1235
1236    while(!(mask & 1))
1237    {
1238        mask >>= 1;
1239        rshift++;
1240    }
1241    *right = rshift;
1242
1243    while(mask & 1)
1244    {
1245        mask >>= 1;
1246        lshift++;
1247    }
1248    *left = 12 - lshift;
1249}
1250
1251/* Compute x^y without relying on the math library */
1252static float gammapow(float x, float y)
1253{
1254#ifdef HAVE_FLDLN2
1255    register double logx;
1256    register long double v, e;
1257#else
1258    register float tmp, t, t2, r;
1259    int i;
1260#endif
1261
1262    if(x == 0.0)
1263        return y == 0.0 ? 1.0 : 0.0;
1264
1265#ifdef HAVE_FLDLN2
1266    /* FIXME: this can be optimised by directly calling fyl2x for x and y */
1267    asm volatile("fldln2; fxch; fyl2x"
1268                 : "=t" (logx) : "0" (x) : "st(1)");
1269
1270    asm volatile("fldl2e\n\t"
1271                 "fmul %%st(1)\n\t"
1272                 "fst %%st(1)\n\t"
1273                 "frndint\n\t"
1274                 "fxch\n\t"
1275                 "fsub %%st(1)\n\t"
1276                 "f2xm1\n\t"
1277                 : "=t" (v), "=u" (e) : "0" (y * logx));
1278    v += 1.0;
1279    asm volatile("fscale"
1280                 : "=t" (v) : "0" (v), "u" (e));
1281    return v;
1282#else
1283    /* Compute ln(x) for x ∈ ]0,1]
1284     *   ln(x) = 2 * (t + t^3/3 + t^5/5 + ...) with t = (x-1)/(x+1)
1285     * The convergence is a bit slow, especially when x is near 0. */
1286    t = (x - 1.0) / (x + 1.0);
1287    t2 = t * t;
1288    tmp = r = t;
1289    for(i = 3; i < 20; i += 2)
1290    {
1291        r *= t2;
1292        tmp += r / i;
1293    }
1294
1295    /* Compute -y*ln(x) */
1296    tmp = - y * 2.0 * tmp;
1297
1298    /* Compute x^-y as e^t where t = -y*ln(x):
1299     *   e^t = 1 + t/1! + t^2/2! + t^3/3! + t^4/4! + t^5/5! ...
1300     * The convergence is quite faster here, thanks to the factorial. */
1301    r = t = tmp;
1302    tmp = 1.0 + t;
1303    for(i = 2; i < 16; i++)
1304    {
1305        r = r * t / i;
1306        tmp += r;
1307    }
1308
1309    /* Return x^y as 1/(x^-y) */
1310    return 1.0 / tmp;
1311#endif
1312}
1313
1314static void get_rgba_default(caca_dither_t const *d, uint8_t const *pixels,
1315                             int x, int y, unsigned int *rgba)
1316{
1317    uint32_t bits;
1318
1319    pixels += (d->bpp / 8) * x + d->pitch * y;
1320
1321    switch(d->bpp / 8)
1322    {
1323        case 4:
1324            bits = *(uint32_t const *)pixels;
1325            break;
1326        case 3:
1327        {
1328#if defined(HAVE_ENDIAN_H)
1329            if(__BYTE_ORDER == __BIG_ENDIAN)
1330#else
1331            /* This is compile-time optimised with at least -O1 or -Os */
1332            uint32_t const tmp = 0x12345678;
1333            if(*(uint8_t const *)&tmp == 0x12)
1334#endif
1335                bits = ((uint32_t)pixels[0] << 16) |
1336                       ((uint32_t)pixels[1] << 8) |
1337                       ((uint32_t)pixels[2]);
1338            else
1339                bits = ((uint32_t)pixels[2] << 16) |
1340                       ((uint32_t)pixels[1] << 8) |
1341                       ((uint32_t)pixels[0]);
1342            break;
1343        }
1344        case 2:
1345            bits = *(uint16_t const *)pixels;
1346            break;
1347        case 1:
1348        default:
1349            bits = pixels[0];
1350            break;
1351    }
1352
1353    if(d->has_palette)
1354    {
1355        rgba[0] += d->gammatab[d->red[bits]];
1356        rgba[1] += d->gammatab[d->green[bits]];
1357        rgba[2] += d->gammatab[d->blue[bits]];
1358        rgba[3] += d->alpha[bits];
1359    }
1360    else
1361    {
1362        rgba[0] += d->gammatab[((bits & d->rmask) >> d->rright) << d->rleft];
1363        rgba[1] += d->gammatab[((bits & d->gmask) >> d->gright) << d->gleft];
1364        rgba[2] += d->gammatab[((bits & d->bmask) >> d->bright) << d->bleft];
1365        rgba[3] += ((bits & d->amask) >> d->aright) << d->aleft;
1366    }
1367}
1368
1369/*
1370 * No dithering
1371 */
1372static void init_no_dither(int line)
1373{
1374    ;
1375}
1376
1377static int get_no_dither(void)
1378{
1379    return 0x80;
1380}
1381
1382static void increment_no_dither(void)
1383{
1384    return;
1385}
1386
1387/*
1388 * Floyd-Steinberg dithering
1389 */
1390static void init_fstein_dither(int line)
1391{
1392    ;
1393}
1394
1395static int get_fstein_dither(void)
1396{
1397    return 0x80;
1398}
1399
1400static void increment_fstein_dither(void)
1401{
1402    return;
1403}
1404
1405/*
1406 * Ordered 2 dithering
1407 */
1408static int const *ordered2_table;
1409static int ordered2_index;
1410
1411static void init_ordered2_dither(int line)
1412{
1413    static int const dither2x2[] =
1414    {
1415        0x00, 0x80,
1416        0xc0, 0x40,
1417    };
1418
1419    ordered2_table = dither2x2 + (line % 2) * 2;
1420    ordered2_index = 0;
1421}
1422
1423static int get_ordered2_dither(void)
1424{
1425    return ordered2_table[ordered2_index];
1426}
1427
1428static void increment_ordered2_dither(void)
1429{
1430    ordered2_index = (ordered2_index + 1) % 2;
1431}
1432
1433/*
1434 * Ordered 4 dithering
1435 */
1436/*static int dither4x4[] = { 5,  0,  1,  6,
1437                          -1, -6, -5,  2,
1438                          -2, -7, -8,  3,
1439                           4, -3, -4, -7};*/
1440static int const *ordered4_table;
1441static int ordered4_index;
1442
1443static void init_ordered4_dither(int line)
1444{
1445    static int const dither4x4[] =
1446    {
1447        0x00, 0x80, 0x20, 0xa0,
1448        0xc0, 0x40, 0xe0, 0x60,
1449        0x30, 0xb0, 0x10, 0x90,
1450        0xf0, 0x70, 0xd0, 0x50
1451    };
1452
1453    ordered4_table = dither4x4 + (line % 4) * 4;
1454    ordered4_index = 0;
1455}
1456
1457static int get_ordered4_dither(void)
1458{
1459    return ordered4_table[ordered4_index];
1460}
1461
1462static void increment_ordered4_dither(void)
1463{
1464    ordered4_index = (ordered4_index + 1) % 4;
1465}
1466
1467/*
1468 * Ordered 8 dithering
1469 */
1470static int const *ordered8_table;
1471static int ordered8_index;
1472
1473static void init_ordered8_dither(int line)
1474{
1475    static int const dither8x8[] =
1476    {
1477        0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8,
1478        0xc0, 0x40, 0xe0, 0x60, 0xc8, 0x48, 0xe8, 0x68,
1479        0x30, 0xb0, 0x10, 0x90, 0x38, 0xb8, 0x18, 0x98,
1480        0xf0, 0x70, 0xd0, 0x50, 0xf8, 0x78, 0xd8, 0x58,
1481        0x0c, 0x8c, 0x2c, 0xac, 0x04, 0x84, 0x24, 0xa4,
1482        0xcc, 0x4c, 0xec, 0x6c, 0xc4, 0x44, 0xe4, 0x64,
1483        0x3c, 0xbc, 0x1c, 0x9c, 0x34, 0xb4, 0x14, 0x94,
1484        0xfc, 0x7c, 0xdc, 0x5c, 0xf4, 0x74, 0xd4, 0x54,
1485    };
1486
1487    ordered8_table = dither8x8 + (line % 8) * 8;
1488    ordered8_index = 0;
1489}
1490
1491static int get_ordered8_dither(void)
1492{
1493    return ordered8_table[ordered8_index];
1494}
1495
1496static void increment_ordered8_dither(void)
1497{
1498    ordered8_index = (ordered8_index + 1) % 8;
1499}
1500
1501/*
1502 * Random dithering
1503 */
1504static void init_random_dither(int line)
1505{
1506    ;
1507}
1508
1509static int get_random_dither(void)
1510{
1511    return caca_rand(0x00, 0x100);
1512}
1513
1514static void increment_random_dither(void)
1515{
1516    return;
1517}
1518
1519/*
1520 * Lookup tables
1521 */
1522static int init_lookup(void)
1523{
1524    int v, s, h;
1525
1526    /* These ones are constant */
1527    lookup_colors[0] = CACA_BLACK;
1528    lookup_colors[1] = CACA_DARKGRAY;
1529    lookup_colors[2] = CACA_LIGHTGRAY;
1530    lookup_colors[3] = CACA_WHITE;
1531
1532    /* These ones will be overwritten */
1533    lookup_colors[4] = CACA_MAGENTA;
1534    lookup_colors[5] = CACA_LIGHTMAGENTA;
1535    lookup_colors[6] = CACA_RED;
1536    lookup_colors[7] = CACA_LIGHTRED;
1537
1538    for(v = 0; v < LOOKUP_VAL; v++)
1539        for(s = 0; s < LOOKUP_SAT; s++)
1540            for(h = 0; h < LOOKUP_HUE; h++)
1541    {
1542        int i, distbg, distfg, dist;
1543        int val, sat, hue;
1544        uint8_t outbg, outfg;
1545
1546        val = 0xfff * v / (LOOKUP_VAL - 1);
1547        sat = 0xfff * s / (LOOKUP_SAT - 1);
1548        hue = 0xfff * h / (LOOKUP_HUE - 1);
1549
1550        /* Initialise distances to the distance between pure black HSV
1551         * coordinates and our white colour (3) */
1552        outbg = outfg = 3;
1553        distbg = distfg = HSV_DISTANCE(0, 0, 0, 3);
1554
1555        /* Calculate distances to eight major colour values and store the
1556         * two nearest points in our lookup table. */
1557        for(i = 0; i < 8; i++)
1558        {
1559            dist = HSV_DISTANCE(hue, sat, val, i);
1560            if(dist <= distbg)
1561            {
1562                outfg = outbg;
1563                distfg = distbg;
1564                outbg = i;
1565                distbg = dist;
1566            }
1567            else if(dist <= distfg)
1568            {
1569                outfg = i;
1570                distfg = dist;
1571            }
1572        }
1573
1574        hsv_distances[v][s][h] = (outfg << 4) | outbg;
1575    }
1576
1577    return 0;
1578}
1579
1580/*
1581 * XXX: The following functions are aliases.
1582 */
1583
1584cucul_dither_t *cucul_create_dither(int, int, int, int, uint32_t, uint32_t,
1585                                    uint32_t, uint32_t)
1586         CACA_ALIAS(caca_create_dither);
1587int cucul_set_dither_palette(cucul_dither_t *, uint32_t r[], uint32_t g[],
1588                             uint32_t b[], uint32_t a[])
1589         CACA_ALIAS(caca_set_dither_palette);
1590int cucul_set_dither_brightness(cucul_dither_t *, float)
1591         CACA_ALIAS(caca_set_dither_brightness);
1592float cucul_get_dither_brightness(cucul_dither_t const *)
1593         CACA_ALIAS(caca_get_dither_brightness);
1594int cucul_set_dither_gamma(cucul_dither_t *, float)
1595         CACA_ALIAS(caca_set_dither_gamma);
1596float cucul_get_dither_gamma(cucul_dither_t const *)
1597         CACA_ALIAS(caca_get_dither_gamma);
1598int cucul_set_dither_contrast(cucul_dither_t *, float)
1599         CACA_ALIAS(caca_set_dither_contrast);
1600float cucul_get_dither_contrast(cucul_dither_t const *)
1601         CACA_ALIAS(caca_get_dither_contrast);
1602int cucul_set_dither_antialias(cucul_dither_t *, char const *)
1603         CACA_ALIAS(caca_set_dither_antialias);
1604char const * const * cucul_get_dither_antialias_list(cucul_dither_t const *)
1605         CACA_ALIAS(caca_get_dither_antialias_list);
1606char const * cucul_get_dither_antialias(cucul_dither_t const *)
1607         CACA_ALIAS(caca_get_dither_antialias);
1608int cucul_set_dither_color(cucul_dither_t *, char const *)
1609         CACA_ALIAS(caca_set_dither_color);
1610char const * const * cucul_get_dither_color_list(cucul_dither_t const *)
1611         CACA_ALIAS(caca_get_dither_color_list);
1612char const * cucul_get_dither_color(cucul_dither_t const *)
1613         CACA_ALIAS(caca_get_dither_color);
1614int cucul_set_dither_charset(cucul_dither_t *, char const *)
1615         CACA_ALIAS(caca_set_dither_charset);
1616char const * const * cucul_get_dither_charset_list(cucul_dither_t const *)
1617         CACA_ALIAS(caca_get_dither_charset_list);
1618char const * cucul_get_dither_charset(cucul_dither_t const *)
1619         CACA_ALIAS(caca_get_dither_charset);
1620int cucul_set_dither_algorithm(cucul_dither_t *, char const *)
1621         CACA_ALIAS(caca_set_dither_algorithm);
1622char const * const * cucul_get_dither_algorithm_list(cucul_dither_t const *)
1623         CACA_ALIAS(caca_get_dither_algorithm_list);
1624char const * cucul_get_dither_algorithm(cucul_dither_t const *)
1625         CACA_ALIAS(caca_get_dither_algorithm);
1626int cucul_dither_bitmap(cucul_canvas_t *, int, int, int, int,
1627                        cucul_dither_t const *, void *)
1628         CACA_ALIAS(caca_dither_bitmap);
1629int cucul_free_dither(cucul_dither_t *) CACA_ALIAS(caca_free_dither);
1630
Note: See TracBrowser for help on using the repository browser.