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

Last change on this file since 238 was 238, checked in by Sam Hocevar, 18 years ago
  • src/bitmap.c: + Fixed an endianness issue in the byte reader. + Decreased precision in RGB and HSV values to avoid overflows. + New dithering method: 2x2 ordered. + New renderer, with background colour awareness.
  • configure.ac: + Check for <endian.h>.
  • examples/demo.c examples/view.c: + Fixed an endianness issue in cacaview. + Adapted code to the additional dithering method.
  • Property svn:keywords set to Id
File size: 17.0 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 238 2003-11-30 15:01:55Z sam $
24 *   \author Sam Hocevar <sam@zoy.org>
25 *   \brief Bitmap functions
26 *
27 *   This file contains bitmap blitting functions.
28 */
29
30#include "config.h"
31
32#ifdef HAVE_INTTYPES_H
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#include <stdlib.h>
41
42#include "caca.h"
43#include "caca_internals.h"
44
45static void mask2shift(unsigned int, int *, int *);
46
47static void get_rgb_default(struct caca_bitmap *, uint8_t *, int, int,
48                            unsigned int *, unsigned int *, unsigned int *);
49static void rgb2hsv_default(int, int, int, int *, int *, int *);
50
51/* Dithering methods */
52static void init_no_dither(int);
53static unsigned int get_no_dither(void);
54static void increment_no_dither(void);
55
56static void init_ordered2_dither(int);
57static unsigned int get_ordered2_dither(void);
58static void increment_ordered2_dither(void);
59
60static void init_ordered4_dither(int);
61static unsigned int get_ordered4_dither(void);
62static void increment_ordered4_dither(void);
63
64static void init_ordered8_dither(int);
65static unsigned int get_ordered8_dither(void);
66static void increment_ordered8_dither(void);
67
68static void init_random_dither(int);
69static unsigned int get_random_dither(void);
70static void increment_random_dither(void);
71
72/* Current dithering method */
73static enum caca_dithering _caca_dithering = CACA_DITHERING_NONE;
74
75static void (*_init_dither) (int) = init_no_dither;
76static unsigned int (*_get_dither) (void) = get_no_dither;
77static void (*_increment_dither) (void) = increment_no_dither;
78
79void caca_set_dithering(enum caca_dithering dither)
80{
81    switch(dither)
82    {
83    case CACA_DITHERING_NONE:
84        _init_dither = init_no_dither;
85        _get_dither = get_no_dither;
86        _increment_dither = increment_no_dither;
87        break;
88
89    case CACA_DITHERING_ORDERED2:
90        _init_dither = init_ordered2_dither;
91        _get_dither = get_ordered2_dither;
92        _increment_dither = increment_ordered2_dither;
93        break;
94
95    case CACA_DITHERING_ORDERED4:
96        _init_dither = init_ordered4_dither;
97        _get_dither = get_ordered4_dither;
98        _increment_dither = increment_ordered4_dither;
99        break;
100
101    case CACA_DITHERING_ORDERED8:
102        _init_dither = init_ordered8_dither;
103        _get_dither = get_ordered8_dither;
104        _increment_dither = increment_ordered8_dither;
105        break;
106
107    case CACA_DITHERING_RANDOM:
108        _init_dither = init_random_dither;
109        _get_dither = get_random_dither;
110        _increment_dither = increment_random_dither;
111        break;
112
113    default:
114        return;
115    }
116
117    _caca_dithering = dither;
118}
119
120struct caca_bitmap
121{
122    int bpp, palette;
123    int w, h, pitch;
124    int rmask, gmask, bmask;
125    int rright, gright, bright;
126    int rleft, gleft, bleft;
127    void (*get_hsv)(struct caca_bitmap *, char *, int, int);
128    int red[256], green[256], blue[256];
129};
130
131static void mask2shift(unsigned int mask, int *right, int *left)
132{
133    int rshift = 0, lshift = 0;
134
135    if(!mask)
136    {
137        *right = *left = 0;
138        return;
139    }
140
141    while(!(mask & 1))
142    {
143        mask >>= 1;
144        rshift++;
145    }
146    *right = rshift;
147
148    while(mask & 1)
149    {
150        mask >>= 1;
151        lshift++;
152    }
153    *left = 12 - lshift;
154}
155
156struct caca_bitmap *caca_create_bitmap(int bpp, int w, int h, int pitch,
157                                       int rmask, int gmask, int bmask)
158{
159    struct caca_bitmap *bitmap;
160
161    /* Currently only this format is supported. Will improve later. */
162    if(!w || !h || !pitch || bpp > 32 || bpp < 8)
163        return NULL;
164
165    bitmap = malloc(sizeof(struct caca_bitmap));
166    if(!bitmap)
167        return NULL;
168
169    bitmap->bpp = bpp;
170    bitmap->palette = 0;
171
172    bitmap->w = w;
173    bitmap->h = h;
174    bitmap->pitch = pitch;
175
176    bitmap->rmask = rmask;
177    bitmap->gmask = gmask;
178    bitmap->bmask = bmask;
179
180    /* Load bitmasks */
181    if(rmask || gmask || bmask)
182    {
183        mask2shift(rmask, &bitmap->rright, &bitmap->rleft);
184        mask2shift(gmask, &bitmap->gright, &bitmap->gleft);
185        mask2shift(bmask, &bitmap->bright, &bitmap->bleft);
186    }
187
188    /* In 8 bpp mode, default to a grayscale palette */
189    if(bpp == 8)
190    {
191        int i;
192        bitmap->palette = 1;
193        for(i = 0; i < 256; i++)
194        {
195            bitmap->red[i] = i * 0x10;
196            bitmap->green[i] = i * 0x10;
197            bitmap->blue[i] = i * 0x10;
198        }
199    }
200
201    return bitmap;
202}
203
204void caca_set_bitmap_palette(struct caca_bitmap *bitmap,
205                             int red[], int green[], int blue[])
206{
207    int i;
208
209    if(bitmap->bpp != 8)
210        return;
211
212    for(i = 0; i < 256; i++)
213    {
214        if(red[i] >= 0 && red[i] < 0x1000 &&
215           green[i] >= 0 && green[i] < 0x1000 &&
216           blue[i] >= 0 && blue[i] < 0x1000)
217        {
218            bitmap->red[i] = red[i];
219            bitmap->green[i] = green[i];
220            bitmap->blue[i] = blue[i];
221        }
222    }
223}
224
225void caca_free_bitmap(struct caca_bitmap *bitmap)
226{
227    if(!bitmap)
228        return;
229
230    free(bitmap);
231}
232
233static void get_rgb_default(struct caca_bitmap *bitmap, uint8_t *pixels,
234                            int x, int y, unsigned int *r,
235                            unsigned int *g, unsigned int *b)
236{
237    unsigned int bits;
238
239    pixels += (bitmap->bpp / 8) * x + bitmap->pitch * y;
240
241    switch(bitmap->bpp / 8)
242    {
243        case 4:
244            bits = ((uint32_t)pixels[0] << 24) |
245                   ((uint32_t)pixels[1] << 16) |
246                   ((uint32_t)pixels[2] << 8) |
247                   ((uint32_t)pixels[3]);
248            break;
249        case 3:
250            bits = ((uint32_t)pixels[0] << 16) |
251                   ((uint32_t)pixels[1] << 8) |
252                   ((uint32_t)pixels[2]);
253            break;
254        case 2:
255            bits = ((uint16_t)pixels[0] << 8) |
256                   ((uint16_t)pixels[1]);
257            break;
258        case 1:
259        default:
260            bits = pixels[0];
261            break;
262    }
263
264    if(bitmap->palette)
265    {
266        *r = bitmap->red[bits];
267        *g = bitmap->green[bits];
268        *b = bitmap->blue[bits];
269    }
270    else
271    {
272        *r = ((bits & bitmap->rmask) >> bitmap->rright) << bitmap->rleft;
273        *g = ((bits & bitmap->gmask) >> bitmap->gright) << bitmap->gleft;
274        *b = ((bits & bitmap->bmask) >> bitmap->bright) << bitmap->bleft;
275    }
276}
277
278static void rgb2hsv_default(int r, int g, int b, int *hue, int *sat, int *val)
279{
280    int min, max, delta;
281
282    min = r; max = r;
283    if(min > g) min = g; if(max < g) max = g;
284    if(min > b) min = b; if(max < b) max = b;
285
286    delta = max - min; /* 0 - 0xffff */
287    *val = max; /* 0 - 0xffff */
288
289    if(delta)
290    {
291        *sat = 0x1000 * delta / max; /* 0 - 0xfff */
292
293        /* Generate *hue between 0 and 0x5fff */
294        if( r == max )
295            *hue = 0x1000 + 0x1000 * (g - b) / delta;
296        else if( g == max )
297            *hue = 0x3000 + 0x1000 * (b - r) / delta;
298        else
299            *hue = 0x5000 + 0x1000 * (r - g) / delta;
300    }
301    else
302    {
303        *sat = 0;
304        *hue = 0;
305    }
306}
307
308void caca_draw_bitmap(int x1, int y1, int x2, int y2,
309                      struct caca_bitmap *bitmap, char *pixels)
310{
311    /* FIXME: this code is shite! */
312    static int white_colors[] =
313    {
314        CACA_COLOR_BLACK,
315        CACA_COLOR_DARKGRAY,
316        CACA_COLOR_LIGHTGRAY,
317        CACA_COLOR_WHITE
318    };
319
320    static int light_colors[] =
321    {
322        CACA_COLOR_LIGHTMAGENTA,
323        CACA_COLOR_LIGHTRED,
324        CACA_COLOR_YELLOW,
325        CACA_COLOR_LIGHTGREEN,
326        CACA_COLOR_LIGHTCYAN,
327        CACA_COLOR_LIGHTBLUE,
328        CACA_COLOR_LIGHTMAGENTA
329    };
330
331    static int dark_colors[] =
332    {
333        CACA_COLOR_MAGENTA,
334        CACA_COLOR_RED,
335        CACA_COLOR_BROWN,
336        CACA_COLOR_GREEN,
337        CACA_COLOR_CYAN,
338        CACA_COLOR_BLUE,
339        CACA_COLOR_MAGENTA
340    };
341
342    /* FIXME: choose better characters! */
343#   define DENSITY_CHARS 13
344    static char density_chars[] =
345        "    "
346        "    "
347        ".`  "
348        ",`.'"
349        "i:-^"
350        "|=+;"
351        "ox/\\"
352        "<>x%"
353        "&$zw"
354        "WXKM"
355        "#8##"
356        "8@8#"
357        "@8@8"
358        "????";
359
360    int x, y, w, h, pitch;
361
362    if(!bitmap || !pixels)
363        return;
364
365    w = bitmap->w;
366    h = bitmap->h;
367    pitch = bitmap->pitch;
368
369    if(x1 > x2)
370    {
371        int tmp = x2; x2 = x1; x1 = tmp;
372    }
373
374    if(y1 > y2)
375    {
376        int tmp = y2; y2 = y1; y1 = tmp;
377    }
378
379    for(y = y1 > 0 ? y1 : 0; y <= y2 && y <= (int)caca_get_height(); y++)
380        for(x = x1 > 0 ? x1 : 0, _init_dither(y);
381            x <= x2 && x <= (int)caca_get_width();
382            x++)
383    {
384        int ch;
385        unsigned int r, g, b, R, G, B;
386        int hue, sat, val;
387        int fromx = w * (x - x1) / (x2 - x1 + 1);
388        int fromy = h * (y - y1) / (y2 - y1 + 1);
389        enum caca_color outfg, outbg;
390        char outch;
391#define NEW_DITHERER 1
392#ifdef NEW_DITHERER
393        int distbg, distfg, dist;
394#endif
395
396        /* Clip values (yuck) */
397        if(fromx == 0) fromx = 1;
398        if(fromy == 0) fromy = 1;
399
400        /* First get RGB */
401        R = 0, G = 0, B = 0;
402        get_rgb_default(bitmap, pixels, fromx, fromy, &r, &g, &b);
403#if 0
404        R += r; G += g; B += b;
405        get_rgb_default(bitmap, pixels, fromx - 1, fromy, &r, &g, &b);
406        R += r; G += g; B += b;
407        get_rgb_default(bitmap, pixels, fromx, fromy - 1, &r, &g, &b);
408        R += r; G += g; B += b;
409        get_rgb_default(bitmap, pixels, fromx + 1, fromy, &r, &g, &b);
410        R += r; G += g; B += b;
411        get_rgb_default(bitmap, pixels, fromx, fromy + 1, &r, &g, &b);
412        R += r; G += g; B += b;
413        R /= 5; G /= 5; B /= 5;
414#else
415        R += r; G += g; B += b;
416#endif
417
418        /* Now get HSV from RGB */
419        rgb2hsv_default(R, G, B, &hue, &sat, &val);
420
421        /* The hard work: calculate foreground and background colours,
422         * as well as the most appropriate character to output. */
423#if NEW_DITHERER
424#define XRATIO 5*5
425#define YRATIO 3*3
426        /* distance to black */
427        distbg = XRATIO * val * val;
428        distbg += XRATIO * 0x1000 * _get_dither() - XRATIO * 0x8000;
429        outbg = CACA_COLOR_BLACK;
430
431        /* distance to 30% */
432        dist = XRATIO * (val - 0x600) * (val - 0x600)
433             + YRATIO * sat * sat;
434        dist += XRATIO * 0x1000 * _get_dither() - XRATIO * 0x8000;
435        if(dist <= distbg)
436        {
437            outfg = outbg;
438            distfg = distbg;
439            outbg = CACA_COLOR_DARKGRAY;
440            distbg = dist;
441        }
442        else
443        {
444            outfg = CACA_COLOR_DARKGRAY;
445            distfg = dist;
446        }
447
448        /* check dist to 70% */
449        dist = XRATIO * (val - 0xa00) * (val - 0xa00)
450             + YRATIO * sat * sat;
451        dist += XRATIO * 0x1000 * _get_dither() - XRATIO * 0x8000;
452        if(dist <= distbg)
453        {
454            outfg = outbg;
455            distfg = distbg;
456            outbg = CACA_COLOR_LIGHTGRAY;
457            distbg = dist;
458        }
459        else if(dist <= distfg)
460        {
461            outfg = CACA_COLOR_LIGHTGRAY;
462            distfg = dist;
463        }
464
465        /* check dist to white */
466        dist = XRATIO * (val - 0x1000) * (val - 0x1000)
467             + YRATIO * sat * sat;
468        dist += XRATIO * 0x1000 * _get_dither() - XRATIO * 0x8000;
469        if(dist <= distbg)
470        {
471            outfg = outbg;
472            distfg = distbg;
473            outbg = CACA_COLOR_WHITE;
474            distbg = dist;
475        }
476        else if(dist <= distfg)
477        {
478            outfg = CACA_COLOR_WHITE;
479            distfg = dist;
480        }
481
482        /* check dist to dark */
483        dist = XRATIO * (val - 0x600) * (val - 0x600)
484             + YRATIO * (sat - 0x1000) * (sat - 0x1000);
485        dist += XRATIO * 0x1000 * _get_dither() - XRATIO * 0x8000;
486        dist = dist * 3 / 4;
487        if(dist <= distbg)
488        {
489            outfg = outbg;
490            distfg = distbg;
491            outbg = dark_colors[(hue + _get_dither() * 0x10) / 0x1000];
492            distbg = dist;
493        }
494        else if(dist <= distfg)
495        {
496            outfg = dark_colors[(hue + _get_dither() * 0x10) / 0x1000];
497            distfg = dist;
498        }
499
500        /* check dist to light */
501        dist = XRATIO * (val - 0x1000) * (val - 0x1000)
502             + YRATIO * (sat - 0x1000) * (sat - 0x1000);
503        dist += XRATIO * 0x1000 * _get_dither() - XRATIO * 0x8000;
504        dist = dist / 2;
505        if(dist <= distbg)
506        {
507            outfg = outbg;
508            distfg = distbg;
509            outbg = light_colors[(hue + _get_dither() * 0x10) / 0x1000];
510            distbg = dist;
511        }
512        else if(dist <= distfg)
513        {
514            outfg = light_colors[(hue + _get_dither() * 0x10) / 0x1000];
515            distfg = dist;
516        }
517
518        if(distbg <= 0) distbg = 1;
519        if(distfg <= 0) distfg = 1;
520
521        /* distbg can be > distfg because of dithering fuzziness */
522        ch = distbg * 2 * (DENSITY_CHARS - 1) / (distbg + distfg);
523        ch = 4 * ch /*+ _get_dither() / 0x40*/;
524        outch = density_chars[ch];
525
526#else
527        outbg = CACA_COLOR_BLACK;
528        if(sat < 0x200 + _get_dither() * 0x8)
529            outfg = white_colors[1 + (val * 2 + _get_dither() * 0x10) / 0x1000];
530        else if(val > 0x800 + _get_dither() * 0x4)
531            outfg = light_colors[(hue + _get_dither() * 0x10) / 0x1000];
532        else
533            outfg = dark_colors[(hue + _get_dither() * 0x10) / 0x1000];
534
535        ch = (val + 0x2 * _get_dither()) * 10 / 0x1000;
536        ch = 4 * ch + _get_dither() / 0x40;
537        outch = density_chars[ch];
538
539#endif
540
541        /* Now output the character */
542        caca_set_color(outfg, outbg);
543        caca_putchar(x, y, outch);
544
545        _increment_dither();
546    }
547}
548
549/*
550 * XXX: The following functions are local.
551 */
552
553/*
554 * No dithering
555 */
556static void init_no_dither(int line)
557{
558    ;
559}
560
561static unsigned int get_no_dither(void)
562{
563    return 0x80;
564}
565
566static void increment_no_dither(void)
567{
568    return;
569}
570
571/*
572 * Ordered 2 dithering
573 */
574static unsigned int *ordered2_table;
575static unsigned int ordered2_index;
576
577static void init_ordered2_dither(int line)
578{
579    static unsigned int dither2x2[] =
580    {
581        0x00, 0x80,
582        0xc0, 0x40,
583    };
584
585    ordered2_table = dither2x2 + (line % 2) * 2;
586    ordered2_index = 0;
587}
588
589static unsigned int get_ordered2_dither(void)
590{
591    return ordered2_table[ordered2_index];
592}
593
594static void increment_ordered2_dither(void)
595{
596    ordered2_index = (ordered2_index + 1) % 2;
597}
598
599/*
600 * Ordered 4 dithering
601 */
602/*static int dither4x4[] = { 5,  0,  1,  6,
603                          -1, -6, -5,  2,
604                          -2, -7, -8,  3,
605                           4, -3, -4, -7};*/
606static unsigned int *ordered4_table;
607static unsigned int ordered4_index;
608
609static void init_ordered4_dither(int line)
610{
611    static unsigned int dither4x4[] =
612    {
613        0x00, 0x80, 0x20, 0xa0,
614        0xc0, 0x40, 0xe0, 0x60,
615        0x30, 0xb0, 0x10, 0x90,
616        0xf0, 0x70, 0xd0, 0x50
617    };
618
619    ordered4_table = dither4x4 + (line % 4) * 4;
620    ordered4_index = 0;
621}
622
623static unsigned int get_ordered4_dither(void)
624{
625    return ordered4_table[ordered4_index];
626}
627
628static void increment_ordered4_dither(void)
629{
630    ordered4_index = (ordered4_index + 1) % 4;
631}
632
633/*
634 * Ordered 8 dithering
635 */
636static unsigned int *ordered8_table;
637static unsigned int ordered8_index;
638
639static void init_ordered8_dither(int line)
640{
641    static unsigned int dither8x8[] =
642    {
643        0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8,
644        0xc0, 0x40, 0xe0, 0x60, 0xc8, 0x48, 0xe8, 0x68,
645        0x30, 0xb0, 0x10, 0x90, 0x38, 0xb8, 0x18, 0x98,
646        0xf0, 0x70, 0xd0, 0x50, 0xf8, 0x78, 0xd8, 0x58,
647        0x0c, 0x8c, 0x2c, 0xac, 0x04, 0x84, 0x24, 0xa4,
648        0xcc, 0x4c, 0xec, 0x6c, 0xc4, 0x44, 0xe4, 0x64,
649        0x3c, 0xbc, 0x1c, 0x9c, 0x34, 0xb4, 0x14, 0x94,
650        0xfc, 0x7c, 0xdc, 0x5c, 0xf4, 0x74, 0xd4, 0x54,
651    };
652
653    ordered8_table = dither8x8 + (line % 8) * 8;
654    ordered8_index = 0;
655}
656
657static unsigned int get_ordered8_dither(void)
658{
659    return ordered8_table[ordered8_index];
660}
661
662static void increment_ordered8_dither(void)
663{
664    ordered8_index = (ordered8_index + 1) % 8;
665}
666
667/*
668 * Random dithering
669 */
670static void init_random_dither(int line)
671{
672    ;
673}
674
675static unsigned int get_random_dither(void)
676{
677    return caca_rand(0x00, 0xff);
678}
679
680static void increment_random_dither(void)
681{
682    return;
683}
684
Note: See TracBrowser for help on using the repository browser.