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

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