source: libcaca/trunk/cucul/bitmap.c @ 691

Last change on this file since 691 was 691, checked in by Jean-Yves Lamoureux, 16 years ago
  • Ok, invert works. OK OK SAM THAT WAS MY FAULT

AHAHAH _

_._ _..._ .-', _.._(`))

'-. ` ' /-._.-' ',/

) MAIS \ '.

/ _ _ | JE \

| a a / SUIS |
\ .-. ;

'-( ).-' ,' UN ;

'-; | .'

\ VIEUX \ /
| 7 . _.-\ \
| | | / /` /

/,_| | /,_/ /

/,_/ '`-'

POOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOORC!

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