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

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