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

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