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

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