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

Last change on this file since 657 was 657, checked in by Sam Hocevar, 16 years ago
  • Added optional support for CP437 gray blocks in the bitmap renderer.
  • Use a custom pow() function for gamma computation.
  • Property svn:keywords set to Id
File size: 26.1 KB
Line 
1/*
2 *  libcucul      Unicode canvas library
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 657 2006-03-22 13:12:54Z sam $
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};
153#endif
154
155static void mask2shift(unsigned int mask, int *right, int *left)
156{
157    int rshift = 0, lshift = 0;
158
159    if(!mask)
160    {
161        *right = *left = 0;
162        return;
163    }
164
165    while(!(mask & 1))
166    {
167        mask >>= 1;
168        rshift++;
169    }
170    *right = rshift;
171
172    while(mask & 1)
173    {
174        mask >>= 1;
175        lshift++;
176    }
177    *left = 12 - lshift;
178}
179
180static float gammapow(float x, float y)
181{
182#ifdef HAVE_FLDLN2
183    register double logx;
184    register long double v, e;
185#else
186    register float tmp, t, r;
187    int i;
188#endif
189
190    if(x == 0.0)
191        return y == 0.0 ? 1.0 : 0.0;
192
193#ifdef HAVE_FLDLN2
194    /* FIXME: this can be optimised by directly calling fyl2x for x and y */
195    asm volatile("fldln2; fxch; fyl2x"
196                 : "=t" (logx) : "0" (x) : "st(1)");
197
198    asm volatile("fldl2e\n\t"
199                 "fmul %%st(1)\n\t"
200                 "fst %%st(1)\n\t"
201                 "frndint\n\t"
202                 "fxch\n\t"
203                 "fsub %%st(1)\n\t"
204                 "f2xm1\n\t"
205                 : "=t" (v), "=u" (e) : "0" (y * logx));
206    v += 1.0;
207    asm volatile("fscale"
208                 : "=t" (v) : "0" (v), "u" (e));
209    return v;
210#else
211    /* Compute ln(x) as ln(1+t) where t = x - 1, x ∈ ]0,1[
212     *   ln(1+t) = t - t^2/2 + t^3/3 - t^4/4 + t^5/5 ...
213     * The convergence is quite slow, especially when x is near 0. */
214    tmp = t = r = x - 1.0;
215    for(i = 2; i < 30; i++)
216    {
217        r *= -t;
218        tmp += r / i;
219    }
220
221    /* Compute x^-y as e^t where t = y*ln(x):
222     *   e^t = 1 + t/1! + t^2/2! + t^3/3! + t^4/4! + t^5/5! ...
223     * The convergence is a lot faster here, thanks to the factorial. */
224    r = t = - y * tmp;
225    tmp = 1.0 + t;
226    for(i = 2; i < 16; i++)
227    {
228        r = r * t / i;
229        tmp += r;
230    }
231
232    /* Return x^y as 1/(x^-y) */
233    return 1.0 / tmp;
234#endif
235}
236
237/**
238 * \brief Create an internal bitmap object.
239 *
240 * Create a bitmap structure from its coordinates (depth, width, height and
241 * pitch) and pixel mask values. If the depth is 8 bits per pixel, the mask
242 * values are ignored and the colour palette should be set using the
243 * cucul_set_bitmap_palette() function. For depths greater than 8 bits per
244 * pixel, a zero alpha mask causes the alpha values to be ignored.
245 *
246 * \param bpp Bitmap depth in bits per pixel.
247 * \param w Bitmap width in pixels.
248 * \param h Bitmap height in pixels.
249 * \param pitch Bitmap pitch in bytes.
250 * \param rmask Bitmask for red values.
251 * \param gmask Bitmask for green values.
252 * \param bmask Bitmask for blue values.
253 * \param amask Bitmask for alpha values.
254 * \return Bitmap object, or NULL upon error.
255 */
256struct cucul_bitmap *cucul_create_bitmap(cucul_t *qq,
257                                       unsigned int bpp, unsigned int w,
258                                       unsigned int h, unsigned int pitch,
259                                       unsigned int rmask, unsigned int gmask,
260                                       unsigned int bmask, unsigned int amask)
261{
262    struct cucul_bitmap *bitmap;
263    int i;
264
265    /* Minor sanity test */
266    if(!w || !h || !pitch || bpp > 32 || bpp < 8)
267        return NULL;
268
269    bitmap = malloc(sizeof(struct cucul_bitmap));
270    if(!bitmap)
271        return NULL;
272
273    bitmap->bpp = bpp;
274    bitmap->has_palette = 0;
275    bitmap->has_alpha = amask ? 1 : 0;
276
277    bitmap->w = w;
278    bitmap->h = h;
279    bitmap->pitch = pitch;
280
281    bitmap->rmask = rmask;
282    bitmap->gmask = gmask;
283    bitmap->bmask = bmask;
284    bitmap->amask = amask;
285
286    /* Load bitmasks */
287    if(rmask || gmask || bmask || amask)
288    {
289        mask2shift(rmask, &bitmap->rright, &bitmap->rleft);
290        mask2shift(gmask, &bitmap->gright, &bitmap->gleft);
291        mask2shift(bmask, &bitmap->bright, &bitmap->bleft);
292        mask2shift(amask, &bitmap->aright, &bitmap->aleft);
293    }
294
295    /* In 8 bpp mode, default to a grayscale palette */
296    if(bpp == 8)
297    {
298        bitmap->has_palette = 1;
299        bitmap->has_alpha = 0;
300        for(i = 0; i < 256; i++)
301        {
302            bitmap->red[i] = i * 0xfff / 256;
303            bitmap->green[i] = i * 0xfff / 256;
304            bitmap->blue[i] = i * 0xfff / 256;
305        }
306    }
307
308    /* Default gamma value */
309    for(i = 0; i < 4096; i++)
310        bitmap->gammatab[i] = i;
311
312    return bitmap;
313}
314
315/**
316 * \brief Set the palette of an 8bpp bitmap object.
317 *
318 * Set the palette of an 8 bits per pixel bitmap. Values should be between
319 * 0 and 4095 (0xfff).
320 *
321 * \param bitmap Bitmap object.
322 * \param red Array of 256 red values.
323 * \param green Array of 256 green values.
324 * \param blue Array of 256 blue values.
325 * \param alpha Array of 256 alpha values.
326 */
327void cucul_set_bitmap_palette(struct cucul_bitmap *bitmap,
328                              unsigned int red[], unsigned int green[],
329                              unsigned int blue[], unsigned int alpha[])
330{
331    int i, has_alpha = 0;
332
333    if(bitmap->bpp != 8)
334        return;
335
336    for(i = 0; i < 256; i++)
337    {
338        if(red[i] >= 0 && red[i] < 0x1000 &&
339           green[i] >= 0 && green[i] < 0x1000 &&
340           blue[i] >= 0 && blue[i] < 0x1000 &&
341           alpha[i] >= 0 && alpha[i] < 0x1000)
342        {
343            bitmap->red[i] = red[i];
344            bitmap->green[i] = green[i];
345            bitmap->blue[i] = blue[i];
346            if(alpha[i])
347            {
348                bitmap->alpha[i] = alpha[i];
349                has_alpha = 1;
350            }
351        }
352    }
353
354    bitmap->has_alpha = has_alpha;
355}
356
357/**
358 * \brief Set the gamma of a bitmap object.
359 *
360 * Set the gamma of bitmap.
361 *
362 * \param bitmap Bitmap object.
363 * \param gamma Gamma value.
364 */
365void cucul_set_bitmap_gamma(struct cucul_bitmap *bitmap, float gamma)
366{
367    int i;
368
369    if(gamma <= 0.0)
370        return;
371
372    bitmap->gamma = gamma;
373
374    for(i = 0; i < 4096; i++)
375        bitmap->gammatab[i] = 4096.0 * gammapow((float)i / 4096.0, 1.0 / gamma);
376}
377
378/**
379 * \brief Free the memory associated with a bitmap.
380 *
381 * Free the memory allocated by cucul_create_bitmap().
382 *
383 * \param bitmap Bitmap object.
384 */
385void cucul_free_bitmap(struct cucul_bitmap *bitmap)
386{
387    if(!bitmap)
388        return;
389
390    free(bitmap);
391}
392
393static void get_rgba_default(struct cucul_bitmap const *bitmap, uint8_t *pixels,
394                             int x, int y, unsigned int *r, unsigned int *g,
395                             unsigned int *b, unsigned int *a)
396{
397    uint32_t bits;
398
399    pixels += (bitmap->bpp / 8) * x + bitmap->pitch * y;
400
401    switch(bitmap->bpp / 8)
402    {
403        case 4:
404            bits = *(uint32_t *)pixels;
405            break;
406        case 3:
407        {
408#if defined(HAVE_ENDIAN_H)
409            if(__BYTE_ORDER == __BIG_ENDIAN)
410#else
411            /* This is compile-time optimised with at least -O1 or -Os */
412            uint32_t const rmask = 0x12345678;
413            if(*(uint8_t const *)&rmask == 0x12)
414#endif
415                bits = ((uint32_t)pixels[0] << 16) |
416                       ((uint32_t)pixels[1] << 8) |
417                       ((uint32_t)pixels[2]);
418            else
419                bits = ((uint32_t)pixels[2] << 16) |
420                       ((uint32_t)pixels[1] << 8) |
421                       ((uint32_t)pixels[0]);
422            break;
423        }
424        case 2:
425            bits = *(uint16_t *)pixels;
426            break;
427        case 1:
428        default:
429            bits = pixels[0];
430            break;
431    }
432
433    if(bitmap->has_palette)
434    {
435        *r += bitmap->gammatab[bitmap->red[bits]];
436        *g += bitmap->gammatab[bitmap->green[bits]];
437        *b += bitmap->gammatab[bitmap->blue[bits]];
438        *a += bitmap->alpha[bits];
439    }
440    else
441    {
442        *r += bitmap->gammatab[((bits & bitmap->rmask) >> bitmap->rright) << bitmap->rleft];
443        *g += bitmap->gammatab[((bits & bitmap->gmask) >> bitmap->gright) << bitmap->gleft];
444        *b += bitmap->gammatab[((bits & bitmap->bmask) >> bitmap->bright) << bitmap->bleft];
445        *a += ((bits & bitmap->amask) >> bitmap->aright) << bitmap->aleft;
446    }
447}
448
449static inline void rgb2hsv_default(int r, int g, int b,
450                                   int *hue, int *sat, int *val)
451{
452    int min, max, delta;
453
454    min = r; max = r;
455    if(min > g) min = g; if(max < g) max = g;
456    if(min > b) min = b; if(max < b) max = b;
457
458    delta = max - min; /* 0 - 0xfff */
459    *val = max; /* 0 - 0xfff */
460
461    if(delta)
462    {
463        *sat = 0xfff * delta / max; /* 0 - 0xfff */
464
465        /* Generate *hue between 0 and 0x5fff */
466        if( r == max )
467            *hue = 0x1000 + 0x1000 * (g - b) / delta;
468        else if( g == max )
469            *hue = 0x3000 + 0x1000 * (b - r) / delta;
470        else
471            *hue = 0x5000 + 0x1000 * (r - g) / delta;
472    }
473    else
474    {
475        *sat = 0;
476        *hue = 0;
477    }
478}
479
480static inline int sq(int x)
481{
482    return x * x;
483}
484
485/**
486 * \brief Draw a bitmap on the screen.
487 *
488 * Draw a bitmap at the given coordinates. The bitmap can be of any size and
489 * will be stretched to the text area.
490 *
491 * \param x1 X coordinate of the upper-left corner of the drawing area.
492 * \param y1 Y coordinate of the upper-left corner of the drawing area.
493 * \param x2 X coordinate of the lower-right corner of the drawing area.
494 * \param y2 Y coordinate of the lower-right corner of the drawing area.
495 * \param bitmap Bitmap object to be drawn.
496 * \param pixels Bitmap's pixels.
497 */
498void cucul_draw_bitmap(cucul_t *qq, int x1, int y1, int x2, int y2,
499                       struct cucul_bitmap const *bitmap, void *pixels)
500{
501    /* Current dithering method */
502    void (*_init_dither) (int);
503    unsigned int (*_get_dither) (void);
504    void (*_increment_dither) (void);
505
506    int *floyd_steinberg, *fs_r, *fs_g, *fs_b;
507    int fs_length;
508
509    /* FIXME: choose better characters! */
510#if !defined(_DOXYGEN_SKIP_ME)
511#   define DCHMAX ((sizeof(density_chars)/sizeof(*density_chars)))
512#endif
513    static char const * density_chars[] =
514    {
515#if CP437
516        " ", ":", "░", "▒", "?"
517        /* "0", "1", "2", "3", "?" */
518#else
519        " ", ".", ":", ";", "t", "%", "S", "X", "@", "8", "?"
520#endif
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(qq->dithering)
546    {
547    case CUCUL_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 CUCUL_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 CUCUL_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 CUCUL_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 CUCUL_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 CUCUL_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)qq->width <= x2 ? (int)qq->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)qq->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)qq->width;
601            x++)
602    {
603        unsigned int i;
604        int ch = 0, distmin;
605        unsigned int r, g, b, a;
606        int 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 cucul_color outfg = 0, outbg = 0;
611        char const *outch;
612
613        r = g = b = a = 0;
614
615        /* First get RGB */
616        if(qq->antialiasing == CUCUL_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(qq->dithering == CUCUL_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(qq->background == CUCUL_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[ch];
735
736            if(qq->dithering == CUCUL_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            unsigned int lum = r; if(g > lum) lum = g; if(b > lum) lum = b;
746            outfg = outbg;
747            outbg = CUCUL_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[ch];
755
756            if(qq->dithering == CUCUL_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(qq->dithering == CUCUL_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        cucul_set_color(qq, outfg, outbg);
782        cucul_putstr(qq, 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 _cucul_init_bitmap(void)
794{
795    unsigned int v, s, h;
796
797    /* These ones are constant */
798    lookup_colors[0] = CUCUL_COLOR_BLACK;
799    lookup_colors[1] = CUCUL_COLOR_DARKGRAY;
800    lookup_colors[2] = CUCUL_COLOR_LIGHTGRAY;
801    lookup_colors[3] = CUCUL_COLOR_WHITE;
802
803    /* These ones will be overwritten */
804    lookup_colors[4] = CUCUL_COLOR_MAGENTA;
805    lookup_colors[5] = CUCUL_COLOR_LIGHTMAGENTA;
806    lookup_colors[6] = CUCUL_COLOR_RED;
807    lookup_colors[7] = CUCUL_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 _cucul_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 const *ordered2_table;
883static unsigned int ordered2_index;
884
885static void init_ordered2_dither(int line)
886{
887    static unsigned int const 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 const *ordered4_table;
915static unsigned int ordered4_index;
916
917static void init_ordered4_dither(int line)
918{
919    static unsigned int const 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 const *ordered8_table;
945static unsigned int ordered8_index;
946
947static void init_ordered8_dither(int line)
948{
949    static unsigned int const 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 cucul_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.