source: libcaca/trunk/src/bitmap.c @ 378

Last change on this file since 378 was 378, checked in by Sam Hocevar, 18 years ago
  • src/bitmap.c: dark background works again with floyd-steinberg.
  • Property svn:keywords set to Id
File size: 25.3 KB
Line 
1/*
2 *  libcaca       ASCII-Art library
3 *  Copyright (c) 2002, 2003 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2 of the License, or (at your option) any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 *  02111-1307  USA
20 */
21
22/** \file bitmap.c
23 *  \version \$Id: bitmap.c 378 2004-11-05 14:57:50Z sam $
24 *  \author Sam Hocevar <sam@zoy.org>
25 *  \brief Bitmap blitting
26 *
27 *  This file contains bitmap blitting functions.
28 */
29
30#include "config.h"
31
32#if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME)
33#   include <inttypes.h>
34#else
35typedef unsigned char uint8_t;
36typedef unsigned short uint16_t;
37typedef unsigned int uint32_t;
38#endif
39
40#if defined(HAVE_ENDIAN_H)
41#   include <endian.h>
42#endif
43
44#include <stdio.h>
45#include <stdlib.h>
46#include <limits.h>
47#include <string.h>
48
49#include "caca.h"
50#include "caca_internals.h"
51
52/*
53 * Global variables
54 */
55#if !defined(_DOXYGEN_SKIP_ME)
56enum caca_feature _caca_background;
57enum caca_feature _caca_dithering;
58enum caca_feature _caca_antialiasing;
59#endif
60
61/*
62 * Local variables
63 */
64#if !defined(_DOXYGEN_SKIP_ME)
65#   define LOOKUP_VAL 32
66#   define LOOKUP_SAT 32
67#   define LOOKUP_HUE 16
68#endif
69static unsigned char hsv_distances[LOOKUP_VAL][LOOKUP_SAT][LOOKUP_HUE];
70static enum caca_color lookup_colors[8];
71
72static int const hsv_palette[] =
73{
74    /* weight, hue, saturation, value */
75    4,    0x0,    0x0,    0x0,   /* black */
76    5,    0x0,    0x0,    0x5ff, /* 30% */
77    5,    0x0,    0x0,    0x9ff, /* 70% */
78    4,    0x0,    0x0,    0xfff, /* white */
79    3,    0x1000, 0xfff,  0x5ff, /* dark yellow */
80    2,    0x1000, 0xfff,  0xfff, /* light yellow */
81    3,    0x0,    0xfff,  0x5ff, /* dark red */
82    2,    0x0,    0xfff,  0xfff  /* light red */
83};
84
85/* RGB palette for the new colour picker */
86static int rgb_palette[] =
87{
88    0x0,   0x0,   0x0,
89    0x0,   0x0,   0x7ff,
90    0x0,   0x7ff, 0x0,
91    0x0,   0x7ff, 0x7ff,
92    0x7ff, 0x0,   0x0,
93    0x7ff, 0x0,   0x7ff,
94    0x7ff, 0x7ff, 0x0,
95    0xaaa, 0xaaa, 0xaaa,
96    0x555, 0x555, 0x555,
97    0x000, 0x000, 0xfff,
98    0x000, 0xfff, 0x000,
99    0x000, 0xfff, 0xfff,
100    0xfff, 0x000, 0x000,
101    0xfff, 0x000, 0xfff,
102    0xfff, 0xfff, 0x000,
103    0xfff, 0xfff, 0xfff,
104};
105
106static int rgb_weight[] =
107{
108    //2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2
109    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
110};
111
112#if !defined(_DOXYGEN_SKIP_ME)
113#define HSV_XRATIO 6
114#define HSV_YRATIO 3
115#define HSV_HRATIO 3
116
117#define HSV_DISTANCE(h, s, v, index) \
118    (hsv_palette[index * 4] \
119     * ((HSV_XRATIO * ((v) - hsv_palette[index * 4 + 3]) \
120                    * ((v) - hsv_palette[index * 4 + 3])) \
121       + (hsv_palette[index * 4 + 3] \
122           ? (HSV_YRATIO * ((s) - hsv_palette[index * 4 + 2]) \
123                         * ((s) - hsv_palette[index * 4 + 2])) \
124           : 0) \
125       + (hsv_palette[index * 4 + 2] \
126           ? (HSV_HRATIO * ((h) - hsv_palette[index * 4 + 1]) \
127                         * ((h) - hsv_palette[index * 4 + 1])) \
128           : 0)))
129#endif
130
131/*
132 * Local prototypes
133 */
134static void mask2shift(unsigned int, int *, int *);
135
136static void get_rgba_default(struct caca_bitmap const *, uint8_t *, int, int,
137                             unsigned int *, unsigned int *, unsigned int *,
138                             unsigned int *);
139static inline void rgb2hsv_default(int, int, int, int *, int *, int *);
140static inline int sq(int);
141
142/* Dithering methods */
143static void init_no_dither(int);
144static unsigned int get_no_dither(void);
145static void increment_no_dither(void);
146
147static void init_ordered2_dither(int);
148static unsigned int get_ordered2_dither(void);
149static void increment_ordered2_dither(void);
150
151static void init_ordered4_dither(int);
152static unsigned int get_ordered4_dither(void);
153static void increment_ordered4_dither(void);
154
155static void init_ordered8_dither(int);
156static unsigned int get_ordered8_dither(void);
157static void increment_ordered8_dither(void);
158
159static void init_random_dither(int);
160static unsigned int get_random_dither(void);
161static void increment_random_dither(void);
162
163#if !defined(_DOXYGEN_SKIP_ME)
164struct caca_bitmap
165{
166    int bpp, has_palette, has_alpha;
167    int w, h, pitch;
168    int rmask, gmask, bmask, amask;
169    int rright, gright, bright, aright;
170    int rleft, gleft, bleft, aleft;
171    void (*get_hsv)(struct caca_bitmap *, char *, int, int);
172    int red[256], green[256], blue[256], alpha[256];
173};
174#endif
175
176static void mask2shift(unsigned int mask, int *right, int *left)
177{
178    int rshift = 0, lshift = 0;
179
180    if(!mask)
181    {
182        *right = *left = 0;
183        return;
184    }
185
186    while(!(mask & 1))
187    {
188        mask >>= 1;
189        rshift++;
190    }
191    *right = rshift;
192
193    while(mask & 1)
194    {
195        mask >>= 1;
196        lshift++;
197    }
198    *left = 12 - lshift;
199}
200
201/**
202 * \brief Create an internal bitmap object.
203 *
204 * Create a bitmap structure from its coordinates (depth, width, height and
205 * pitch) and pixel mask values. If the depth is 8 bits per pixel, the mask
206 * values are ignored and the colour palette should be set using the
207 * caca_set_bitmap_palette() function. For depths greater than 8 bits per
208 * pixel, a zero alpha mask causes the alpha values to be ignored.
209 *
210 * \param bpp Bitmap depth in bits per pixel.
211 * \param w Bitmap width in pixels.
212 * \param h Bitmap height in pixels.
213 * \param pitch Bitmap pitch in bytes.
214 * \param rmask Bitmask for red values.
215 * \param gmask Bitmask for green values.
216 * \param bmask Bitmask for blue values.
217 * \param amask Bitmask for alpha values.
218 * \return Bitmap object, or NULL upon error.
219 */
220struct caca_bitmap *caca_create_bitmap(unsigned int bpp, unsigned int w,
221                                       unsigned int h, unsigned int pitch,
222                                       unsigned int rmask, unsigned int gmask,
223                                       unsigned int bmask, unsigned int amask)
224{
225    struct caca_bitmap *bitmap;
226
227    /* Minor sanity test */
228    if(!w || !h || !pitch || bpp > 32 || bpp < 8)
229        return NULL;
230
231    bitmap = malloc(sizeof(struct caca_bitmap));
232    if(!bitmap)
233        return NULL;
234
235    bitmap->bpp = bpp;
236    bitmap->has_palette = 0;
237    bitmap->has_alpha = amask ? 1 : 0;
238
239    bitmap->w = w;
240    bitmap->h = h;
241    bitmap->pitch = pitch;
242
243    bitmap->rmask = rmask;
244    bitmap->gmask = gmask;
245    bitmap->bmask = bmask;
246    bitmap->amask = amask;
247
248    /* Load bitmasks */
249    if(rmask || gmask || bmask || amask)
250    {
251        mask2shift(rmask, &bitmap->rright, &bitmap->rleft);
252        mask2shift(gmask, &bitmap->gright, &bitmap->gleft);
253        mask2shift(bmask, &bitmap->bright, &bitmap->bleft);
254        mask2shift(amask, &bitmap->aright, &bitmap->aleft);
255    }
256
257    /* In 8 bpp mode, default to a grayscale palette */
258    if(bpp == 8)
259    {
260        int i;
261        bitmap->has_palette = 1;
262        bitmap->has_alpha = 0;
263        for(i = 0; i < 256; i++)
264        {
265            bitmap->red[i] = i * 0xfff / 256;
266            bitmap->green[i] = i * 0xfff / 256;
267            bitmap->blue[i] = i * 0xfff / 256;
268        }
269    }
270
271    return bitmap;
272}
273
274/**
275 * \brief Set the palette of an 8bpp bitmap object.
276 *
277 * Set the palette of an 8 bits per pixel bitmap. Values should be between
278 * 0 and 4095 (0xfff).
279 *
280 * \param bitmap Bitmap object.
281 * \param red Array of 256 red values.
282 * \param green Array of 256 green values.
283 * \param blue Array of 256 blue values.
284 * \param alpha Array of 256 alpha values.
285 */
286void caca_set_bitmap_palette(struct caca_bitmap *bitmap,
287                             unsigned int red[], unsigned int green[],
288                             unsigned int blue[], unsigned int alpha[])
289{
290    int i, has_alpha = 0;
291
292    if(bitmap->bpp != 8)
293        return;
294
295    for(i = 0; i < 256; i++)
296    {
297        if(red[i] >= 0 && red[i] < 0x1000 &&
298           green[i] >= 0 && green[i] < 0x1000 &&
299           blue[i] >= 0 && blue[i] < 0x1000 &&
300           alpha[i] >= 0 && alpha[i] < 0x1000)
301        {
302            bitmap->red[i] = red[i];
303            bitmap->green[i] = green[i];
304            bitmap->blue[i] = blue[i];
305            if(alpha[i])
306            {
307                bitmap->alpha[i] = alpha[i];
308                has_alpha = 1;
309            }
310        }
311    }
312
313    bitmap->has_alpha = has_alpha;
314}
315
316/**
317 * \brief Free the memory associated with a bitmap.
318 *
319 * Free the memory allocated by caca_create_bitmap().
320 *
321 * \param bitmap Bitmap object.
322 */
323void caca_free_bitmap(struct caca_bitmap *bitmap)
324{
325    if(!bitmap)
326        return;
327
328    free(bitmap);
329}
330
331static void get_rgba_default(struct caca_bitmap const *bitmap, uint8_t *pixels,
332                             int x, int y, unsigned int *r, unsigned int *g,
333                             unsigned int *b, unsigned int *a)
334{
335    uint32_t bits;
336
337    pixels += (bitmap->bpp / 8) * x + bitmap->pitch * y;
338
339    switch(bitmap->bpp / 8)
340    {
341        case 4:
342            bits = *(uint32_t *)pixels;
343            break;
344        case 3:
345        {
346#if defined(HAVE_ENDIAN_H)
347            if(__BYTE_ORDER == __BIG_ENDIAN)
348#else
349            /* This is compile-time optimised with at least -O1 or -Os */
350            uint32_t const rmask = 0x12345678;
351            if(*(uint8_t const *)&rmask == 0x12)
352#endif
353                bits = ((uint32_t)pixels[0] << 16) |
354                       ((uint32_t)pixels[1] << 8) |
355                       ((uint32_t)pixels[2]);
356            else
357                bits = ((uint32_t)pixels[2] << 16) |
358                       ((uint32_t)pixels[1] << 8) |
359                       ((uint32_t)pixels[0]);
360            break;
361        }
362        case 2:
363            bits = *(uint16_t *)pixels;
364            break;
365        case 1:
366        default:
367            bits = pixels[0];
368            break;
369    }
370
371    if(bitmap->has_palette)
372    {
373        *r += bitmap->red[bits];
374        *g += bitmap->green[bits];
375        *b += bitmap->blue[bits];
376        *a += bitmap->alpha[bits];
377    }
378    else
379    {
380        *r += ((bits & bitmap->rmask) >> bitmap->rright) << bitmap->rleft;
381        *g += ((bits & bitmap->gmask) >> bitmap->gright) << bitmap->gleft;
382        *b += ((bits & bitmap->bmask) >> bitmap->bright) << bitmap->bleft;
383        *a += ((bits & bitmap->amask) >> bitmap->aright) << bitmap->aleft;
384    }
385}
386
387static inline void rgb2hsv_default(int r, int g, int b,
388                                   int *hue, int *sat, int *val)
389{
390    int min, max, delta;
391
392    min = r; max = r;
393    if(min > g) min = g; if(max < g) max = g;
394    if(min > b) min = b; if(max < b) max = b;
395
396    delta = max - min; /* 0 - 0xfff */
397    *val = max; /* 0 - 0xfff */
398
399    if(delta)
400    {
401        *sat = 0xfff * delta / max; /* 0 - 0xfff */
402
403        /* Generate *hue between 0 and 0x5fff */
404        if( r == max )
405            *hue = 0x1000 + 0x1000 * (g - b) / delta;
406        else if( g == max )
407            *hue = 0x3000 + 0x1000 * (b - r) / delta;
408        else
409            *hue = 0x5000 + 0x1000 * (r - g) / delta;
410    }
411    else
412    {
413        *sat = 0;
414        *hue = 0;
415    }
416}
417
418static inline int sq(int x)
419{
420    return x * x;
421}
422
423/**
424 * \brief Draw a bitmap on the screen.
425 *
426 * Draw a bitmap at the given coordinates. The bitmap can be of any size and
427 * will be stretched to the text area.
428 *
429 * \param x1 X coordinate of the upper-left corner of the drawing area.
430 * \param y1 Y coordinate of the upper-left corner of the drawing area.
431 * \param x2 X coordinate of the lower-right corner of the drawing area.
432 * \param y2 Y coordinate of the lower-right corner of the drawing area.
433 * \param bitmap Bitmap object to be drawn.
434 * \param pixels Bitmap's pixels.
435 */
436void caca_draw_bitmap(int x1, int y1, int x2, int y2,
437                      struct caca_bitmap const *bitmap, void *pixels)
438{
439    /* Current dithering method */
440    void (*_init_dither) (int);
441    unsigned int (*_get_dither) (void);
442    void (*_increment_dither) (void);
443
444    int *floyd_steinberg, *fs_r, *fs_g, *fs_b;
445    int fs_length;
446
447    /* Only used when background is black */
448    static int const white_colors[] =
449    {
450        CACA_COLOR_BLACK,
451        CACA_COLOR_DARKGRAY,
452        CACA_COLOR_LIGHTGRAY,
453        CACA_COLOR_WHITE
454    };
455
456    static int const light_colors[] =
457    {
458        CACA_COLOR_LIGHTMAGENTA,
459        CACA_COLOR_LIGHTRED,
460        CACA_COLOR_YELLOW,
461        CACA_COLOR_LIGHTGREEN,
462        CACA_COLOR_LIGHTCYAN,
463        CACA_COLOR_LIGHTBLUE,
464        CACA_COLOR_LIGHTMAGENTA
465    };
466
467    static int const dark_colors[] =
468    {
469        CACA_COLOR_MAGENTA,
470        CACA_COLOR_RED,
471        CACA_COLOR_BROWN,
472        CACA_COLOR_GREEN,
473        CACA_COLOR_CYAN,
474        CACA_COLOR_BLUE,
475        CACA_COLOR_MAGENTA
476    };
477
478    /* FIXME: choose better characters! */
479#if !defined(_DOXYGEN_SKIP_ME)
480#   define DCHMAX ((sizeof(density_chars)/sizeof(char const)/4)-1)
481#endif
482    static char const density_chars[] =
483        "    "
484        "...."
485        "::::"
486        ";=;="
487        "tftf"
488        "%$%$"
489        "SK&Z"
490        "XWGM"
491        "@@@@"
492        "8888"
493        "####"
494        "????";
495
496    int x, y, w, h, pitch, deltax, deltay;
497
498    if(!bitmap || !pixels)
499        return;
500
501    w = bitmap->w;
502    h = bitmap->h;
503    pitch = bitmap->pitch;
504
505    if(x1 > x2)
506    {
507        int tmp = x2; x2 = x1; x1 = tmp;
508    }
509
510    if(y1 > y2)
511    {
512        int tmp = y2; y2 = y1; y1 = tmp;
513    }
514
515    deltax = x2 - x1 + 1;
516    deltay = y2 - y1 + 1;
517
518    switch(_caca_dithering)
519    {
520    case CACA_DITHERING_NONE:
521        _init_dither = init_no_dither;
522        _get_dither = get_no_dither;
523        _increment_dither = increment_no_dither;
524        break;
525
526    case CACA_DITHERING_ORDERED2:
527        _init_dither = init_ordered2_dither;
528        _get_dither = get_ordered2_dither;
529        _increment_dither = increment_ordered2_dither;
530        break;
531
532    case CACA_DITHERING_ORDERED4:
533        _init_dither = init_ordered4_dither;
534        _get_dither = get_ordered4_dither;
535        _increment_dither = increment_ordered4_dither;
536        break;
537
538    case CACA_DITHERING_ORDERED8:
539        _init_dither = init_ordered8_dither;
540        _get_dither = get_ordered8_dither;
541        _increment_dither = increment_ordered8_dither;
542        break;
543
544    case CACA_DITHERING_RANDOM:
545        _init_dither = init_random_dither;
546        _get_dither = get_random_dither;
547        _increment_dither = increment_random_dither;
548        break;
549
550    case CACA_DITHERING_FSTEIN:
551        _init_dither = init_no_dither;
552        _get_dither = get_no_dither;
553        _increment_dither = increment_no_dither;
554        break;
555
556    default:
557        /* Something wicked happened! */
558        return;
559    }
560
561    fs_length = ((int)_caca_width <= x2 ? (int)_caca_width : x2) + 1;
562    floyd_steinberg = malloc(3 * (fs_length + 2) * sizeof(int));
563    memset(floyd_steinberg, 0, 3 * (fs_length + 2) * sizeof(int));
564    fs_r = floyd_steinberg + 1;
565    fs_g = fs_r + fs_length + 2;
566    fs_b = fs_g + fs_length + 2;
567
568    for(y = y1 > 0 ? y1 : 0; y <= y2 && y <= (int)_caca_height; y++)
569    {
570        int remain_r = 0, remain_g = 0, remain_b = 0;
571
572        for(x = x1 > 0 ? x1 : 0, _init_dither(y);
573            x <= x2 && x <= (int)_caca_width;
574            x++)
575    {
576        unsigned int i;
577        int ch = 0, distmin;
578        int r, g, b, a, fg_r = 0, fg_g = 0, fg_b = 0, bg_r, bg_g, bg_b;
579        int fromx, fromy, tox, toy, myx, myy, dots, dist;
580        int error[3];
581
582        enum caca_color outfg = 0, outbg = 0;
583        char outch;
584
585        r = g = b = a = 0;
586
587        /* First get RGB */
588        if(_caca_antialiasing == CACA_ANTIALIASING_PREFILTER)
589        {
590            fromx = (x - x1) * w / deltax;
591            fromy = (y - y1) * h / deltay;
592            tox = (x - x1 + 1) * w / deltax;
593            toy = (y - y1 + 1) * h / deltay;
594
595            /* We want at least one pixel */
596            if(tox == fromx) tox++;
597            if(toy == fromy) toy++;
598
599            dots = 0;
600
601            for(myx = fromx; myx < tox; myx++)
602                for(myy = fromy; myy < toy; myy++)
603            {
604                dots++;
605                get_rgba_default(bitmap, pixels, myx, myy, &r, &g, &b, &a);
606            }
607
608            /* Normalize */
609            r /= dots;
610            g /= dots;
611            b /= dots;
612            a /= dots;
613        }
614        else
615        {
616            fromx = (x - x1) * w / deltax;
617            fromy = (y - y1) * h / deltay;
618            tox = (x - x1 + 1) * w / deltax;
619            toy = (y - y1 + 1) * h / deltay;
620
621            /* tox and toy can overflow the screen, but they cannot overflow
622             * when averaged with fromx and fromy because these are guaranteed
623             * to be within the pixel boundaries. */
624            myx = (fromx + tox) / 2;
625            myy = (fromy + toy) / 2;
626
627            get_rgba_default(bitmap, pixels, myx, myy, &r, &g, &b, &a);
628        }
629
630        if(bitmap->has_alpha && a < 0x800)
631        {
632            remain_r = remain_g = remain_b = 0;
633            fs_r[x] = 0;
634            fs_g[x] = 0;
635            fs_b[x] = 0;
636            continue;
637        }
638
639        if(_caca_dithering == CACA_DITHERING_FSTEIN)
640        {
641            r += remain_r;
642            g += remain_g;
643            b += remain_b;
644        }
645        else
646        {
647            r += (_get_dither() - 0x80) * 4;
648            g += (_get_dither() - 0x80) * 4;
649            b += (_get_dither() - 0x80) * 4;
650        }
651
652        distmin = INT_MAX;
653        for(i = 0; i < 16; i++)
654        {
655            dist = sq(r - rgb_palette[i * 3])
656                 + sq(g - rgb_palette[i * 3 + 1])
657                 + sq(b - rgb_palette[i * 3 + 2]);
658            dist *= rgb_weight[i];
659            if(dist < distmin)
660            {
661                outbg = i;
662                distmin = dist;
663            }
664        }
665        bg_r = rgb_palette[outbg * 3];
666        bg_g = rgb_palette[outbg * 3 + 1];
667        bg_b = rgb_palette[outbg * 3 + 2];
668
669        if(_caca_background == CACA_BACKGROUND_SOLID)
670        {
671            distmin = INT_MAX;
672            for(i = 0; i < 16; i++)
673            {
674                if(i == outbg)
675                    continue;
676                dist = sq(r - rgb_palette[i * 3])
677                     + sq(g - rgb_palette[i * 3 + 1])
678                     + sq(b - rgb_palette[i * 3 + 2]);
679                dist *= rgb_weight[i];
680                if(dist < distmin)
681                {
682                    outfg = i;
683                    distmin = dist;
684                }
685            }
686            fg_r = rgb_palette[outfg * 3];
687            fg_g = rgb_palette[outfg * 3 + 1];
688            fg_b = rgb_palette[outfg * 3 + 2];
689
690            distmin = INT_MAX;
691            for(i = 0; i < DCHMAX - 1; i++)
692            {
693                int newr = i * fg_r + ((2*DCHMAX-1) - i) * bg_r;
694                int newg = i * fg_g + ((2*DCHMAX-1) - i) * bg_g;
695                int newb = i * fg_b + ((2*DCHMAX-1) - i) * bg_b;
696                dist = abs(r * (2*DCHMAX-1) - newr)
697                     + abs(g * (2*DCHMAX-1) - newg)
698                     + abs(b * (2*DCHMAX-1) - newb);
699
700                if(dist < distmin)
701                {
702                    ch = i;
703                    distmin = dist;
704                }
705            }
706            outch = density_chars[4 * ch];
707
708            if(_caca_dithering == CACA_DITHERING_FSTEIN)
709            {
710                error[0] = r - (fg_r * ch + bg_r * ((2*DCHMAX-1) - ch)) / (2*DCHMAX-1);
711                error[1] = g - (fg_g * ch + bg_g * ((2*DCHMAX-1) - ch)) / (2*DCHMAX-1);
712                error[2] = b - (fg_b * ch + bg_b * ((2*DCHMAX-1) - ch)) / (2*DCHMAX-1);
713            }
714        }
715        else
716        {
717            int lum = r; if(g > lum) lum = g; if(b > lum) lum = b;
718            outfg = outbg;
719            outbg = CACA_COLOR_BLACK;
720
721            ch = lum * DCHMAX / 0x1000;
722            if(ch < 0)
723                ch = 0;
724            else if(ch > (int)(DCHMAX - 1))
725                ch = DCHMAX - 1;
726            outch = density_chars[4 * ch];
727
728            if(_caca_dithering == CACA_DITHERING_FSTEIN)
729            {
730                error[0] = r - bg_r * ch / (DCHMAX-1);
731                error[1] = g - bg_g * ch / (DCHMAX-1);
732                error[2] = b - bg_b * ch / (DCHMAX-1);
733            }
734        }
735
736        if(_caca_dithering == CACA_DITHERING_FSTEIN)
737        {
738            remain_r = fs_r[x+1] + 7 * error[0] / 16;
739            remain_g = fs_g[x+1] + 7 * error[1] / 16;
740            remain_b = fs_b[x+1] + 7 * error[2] / 16;
741            fs_r[x-1] += 3 * error[0] / 16;
742            fs_g[x-1] += 3 * error[1] / 16;
743            fs_b[x-1] += 3 * error[2] / 16;
744            fs_r[x] = 5 * error[0] / 16;
745            fs_g[x] = 5 * error[1] / 16;
746            fs_b[x] = 5 * error[2] / 16;
747            fs_r[x+1] = 1 * error[0] / 16;
748            fs_g[x+1] = 1 * error[1] / 16;
749            fs_b[x+1] = 1 * error[2] / 16;
750        }
751
752        /* Now output the character */
753        caca_set_color(outfg, outbg);
754        caca_putchar(x, y, outch);
755
756        _increment_dither();
757    }
758        /* end loop */
759    }
760
761    free(floyd_steinberg);
762}
763
764#if !defined(_DOXYGEN_SKIP_ME)
765int _caca_init_bitmap(void)
766{
767    unsigned int v, s, h;
768
769    /* These ones are constant */
770    lookup_colors[0] = CACA_COLOR_BLACK;
771    lookup_colors[1] = CACA_COLOR_DARKGRAY;
772    lookup_colors[2] = CACA_COLOR_LIGHTGRAY;
773    lookup_colors[3] = CACA_COLOR_WHITE;
774
775    /* These ones will be overwritten */
776    lookup_colors[4] = CACA_COLOR_MAGENTA;
777    lookup_colors[5] = CACA_COLOR_LIGHTMAGENTA;
778    lookup_colors[6] = CACA_COLOR_RED;
779    lookup_colors[7] = CACA_COLOR_LIGHTRED;
780
781    for(v = 0; v < LOOKUP_VAL; v++)
782        for(s = 0; s < LOOKUP_SAT; s++)
783            for(h = 0; h < LOOKUP_HUE; h++)
784    {
785        int i, distbg, distfg, dist;
786        int val, sat, hue;
787        unsigned char outbg, outfg;
788
789        val = 0xfff * v / (LOOKUP_VAL - 1);
790        sat = 0xfff * s / (LOOKUP_SAT - 1);
791        hue = 0xfff * h / (LOOKUP_HUE - 1);
792
793        /* Initialise distances to the distance between pure black HSV
794         * coordinates and our white colour (3) */
795        outbg = outfg = 3;
796        distbg = distfg = HSV_DISTANCE(0, 0, 0, 3);
797
798        /* Calculate distances to eight major colour values and store the
799         * two nearest points in our lookup table. */
800        for(i = 0; i < 8; i++)
801        {
802            dist = HSV_DISTANCE(hue, sat, val, i);
803            if(dist <= distbg)
804            {
805                outfg = outbg;
806                distfg = distbg;
807                outbg = i;
808                distbg = dist;
809            }
810            else if(dist <= distfg)
811            {
812                outfg = i;
813                distfg = dist;
814            }
815        }
816
817        hsv_distances[v][s][h] = (outfg << 4) | outbg;
818    }
819
820    return 0;
821}
822
823int _caca_end_bitmap(void)
824{
825    return 0;
826}
827#endif /* _DOXYGEN_SKIP_ME */
828
829/*
830 * XXX: The following functions are local.
831 */
832
833/*
834 * No dithering
835 */
836static void init_no_dither(int line)
837{
838    ;
839}
840
841static unsigned int get_no_dither(void)
842{
843    return 0x80;
844}
845
846static void increment_no_dither(void)
847{
848    return;
849}
850
851/*
852 * Ordered 2 dithering
853 */
854static unsigned int *ordered2_table;
855static unsigned int ordered2_index;
856
857static void init_ordered2_dither(int line)
858{
859    static unsigned int dither2x2[] =
860    {
861        0x00, 0x80,
862        0xc0, 0x40,
863    };
864
865    ordered2_table = dither2x2 + (line % 2) * 2;
866    ordered2_index = 0;
867}
868
869static unsigned int get_ordered2_dither(void)
870{
871    return ordered2_table[ordered2_index];
872}
873
874static void increment_ordered2_dither(void)
875{
876    ordered2_index = (ordered2_index + 1) % 2;
877}
878
879/*
880 * Ordered 4 dithering
881 */
882/*static int dither4x4[] = { 5,  0,  1,  6,
883                          -1, -6, -5,  2,
884                          -2, -7, -8,  3,
885                           4, -3, -4, -7};*/
886static unsigned int *ordered4_table;
887static unsigned int ordered4_index;
888
889static void init_ordered4_dither(int line)
890{
891    static unsigned int dither4x4[] =
892    {
893        0x00, 0x80, 0x20, 0xa0,
894        0xc0, 0x40, 0xe0, 0x60,
895        0x30, 0xb0, 0x10, 0x90,
896        0xf0, 0x70, 0xd0, 0x50
897    };
898
899    ordered4_table = dither4x4 + (line % 4) * 4;
900    ordered4_index = 0;
901}
902
903static unsigned int get_ordered4_dither(void)
904{
905    return ordered4_table[ordered4_index];
906}
907
908static void increment_ordered4_dither(void)
909{
910    ordered4_index = (ordered4_index + 1) % 4;
911}
912
913/*
914 * Ordered 8 dithering
915 */
916static unsigned int *ordered8_table;
917static unsigned int ordered8_index;
918
919static void init_ordered8_dither(int line)
920{
921    static unsigned int dither8x8[] =
922    {
923        0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8,
924        0xc0, 0x40, 0xe0, 0x60, 0xc8, 0x48, 0xe8, 0x68,
925        0x30, 0xb0, 0x10, 0x90, 0x38, 0xb8, 0x18, 0x98,
926        0xf0, 0x70, 0xd0, 0x50, 0xf8, 0x78, 0xd8, 0x58,
927        0x0c, 0x8c, 0x2c, 0xac, 0x04, 0x84, 0x24, 0xa4,
928        0xcc, 0x4c, 0xec, 0x6c, 0xc4, 0x44, 0xe4, 0x64,
929        0x3c, 0xbc, 0x1c, 0x9c, 0x34, 0xb4, 0x14, 0x94,
930        0xfc, 0x7c, 0xdc, 0x5c, 0xf4, 0x74, 0xd4, 0x54,
931    };
932
933    ordered8_table = dither8x8 + (line % 8) * 8;
934    ordered8_index = 0;
935}
936
937static unsigned int get_ordered8_dither(void)
938{
939    return ordered8_table[ordered8_index];
940}
941
942static void increment_ordered8_dither(void)
943{
944    ordered8_index = (ordered8_index + 1) % 8;
945}
946
947/*
948 * Random dithering
949 */
950static void init_random_dither(int line)
951{
952    ;
953}
954
955static unsigned int get_random_dither(void)
956{
957    return caca_rand(0x00, 0xff);
958}
959
960static void increment_random_dither(void)
961{
962    return;
963}
964
Note: See TracBrowser for help on using the repository browser.