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

Last change on this file since 235 was 235, checked in by Sam Hocevar, 17 years ago
  • src/caca.c src/caca.h: + Added caca_get_dithering_name().
  • src/bitmap.c: + Created a new dithering method with an 8x8 ordered matrix. + Replaced the char list with a string for better readability. + Dithering functions now return a value between 0 and 255.
  • examples/demo.c examples/view.c: + Adapted to use caca_get_dithering_name().
  • Property svn:keywords set to Id
File size: 12.4 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 235 2003-11-29 19:35:07Z 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(int, int *, int *);
46
47static void get_rgb_default(struct caca_bitmap *, unsigned char *,
48                            int, int, int *, int *, int *);
49static void rgb2hsv_default(int, int, int, int *, int *, int *);
50
51/* Dithering methods */
52static void init_no_dither(int);
53static int get_no_dither(void);
54static void increment_no_dither(void);
55
56static void init_ordered4_dither(int);
57static int get_ordered4_dither(void);
58static void increment_ordered4_dither(void);
59
60static void init_ordered8_dither(int);
61static int get_ordered8_dither(void);
62static void increment_ordered8_dither(void);
63
64static void init_random_dither(int);
65static int get_random_dither(void);
66static void increment_random_dither(void);
67
68/* Current dithering method */
69static enum caca_dithering _caca_dithering = CACA_DITHERING_NONE;
70
71static void (*_init_dither) (int) = init_no_dither;
72static int (*_get_dither) (void) = get_no_dither;
73static void (*_increment_dither) (void) = increment_no_dither;
74
75void caca_set_dithering(enum caca_dithering dither)
76{
77    switch(dither)
78    {
79    case CACA_DITHERING_NONE:
80        _init_dither = init_no_dither;
81        _get_dither = get_no_dither;
82        _increment_dither = increment_no_dither;
83        break;
84
85    case CACA_DITHERING_ORDERED4:
86        _init_dither = init_ordered4_dither;
87        _get_dither = get_ordered4_dither;
88        _increment_dither = increment_ordered4_dither;
89        break;
90
91    case CACA_DITHERING_ORDERED8:
92        _init_dither = init_ordered8_dither;
93        _get_dither = get_ordered8_dither;
94        _increment_dither = increment_ordered8_dither;
95        break;
96
97    case CACA_DITHERING_RANDOM:
98        _init_dither = init_random_dither;
99        _get_dither = get_random_dither;
100        _increment_dither = increment_random_dither;
101        break;
102
103    default:
104        return;
105    }
106
107    _caca_dithering = dither;
108}
109
110struct caca_bitmap
111{
112    int bpp, palette;
113    int w, h, pitch;
114    int rmask, gmask, bmask;
115    int rright, gright, bright;
116    int rleft, gleft, bleft;
117    void (*get_hsv)(struct caca_bitmap *, char *, int, int);
118    int red[256], green[256], blue[256];
119};
120
121static void mask2shift(int mask, int *right, int *left)
122{
123    int rshift = 0, lshift = 0;
124    *right = *left = 0;
125
126    if(!mask)
127        return;
128
129    while(!(mask & 1))
130    {
131        mask >>= 1;
132        rshift++;
133    }
134    *right = rshift;
135
136    while(mask & 1)
137    {
138        mask >>= 1;
139        lshift++;
140    }
141    *left = 16 - lshift;
142}
143
144struct caca_bitmap *caca_create_bitmap(int bpp, int w, int h, int pitch,
145                                       int rmask, int gmask, int bmask)
146{
147    struct caca_bitmap *bitmap;
148
149    /* Currently only this format is supported. Will improve later. */
150    if(!w || !h || !pitch || bpp > 32 || bpp < 8)
151        return NULL;
152
153    bitmap = malloc(sizeof(struct caca_bitmap));
154    if(!bitmap)
155        return NULL;
156
157    bitmap->bpp = bpp;
158    bitmap->palette = 0;
159
160    bitmap->w = w;
161    bitmap->h = h;
162    bitmap->pitch = pitch;
163
164    bitmap->rmask = rmask;
165    bitmap->gmask = gmask;
166    bitmap->bmask = bmask;
167
168    /* Load bitmasks */
169    if(rmask || gmask || bmask)
170    {
171        mask2shift(rmask, &bitmap->rright, &bitmap->rleft);
172        mask2shift(gmask, &bitmap->gright, &bitmap->gleft);
173        mask2shift(bmask, &bitmap->bright, &bitmap->bleft);
174    }
175
176    /* In 8 bpp mode, default to a grayscale palette */
177    if(bpp == 8)
178    {
179        int i;
180        bitmap->palette = 1;
181        for(i = 0; i < 256; i++)
182        {
183            bitmap->red[i] = i * 0x100;
184            bitmap->green[i] = i * 0x100;
185            bitmap->blue[i] = i * 0x100;
186        }
187    }
188
189    return bitmap;
190}
191
192void caca_set_bitmap_palette(struct caca_bitmap *bitmap,
193                             int red[], int green[], int blue[])
194{
195    int i;
196
197    if(bitmap->bpp != 8)
198        return;
199
200    for(i = 0; i < 256; i++)
201    {
202        if(red[i] >= 0 && red[i] < 65536 &&
203           green[i] >= 0 && green[i] < 65536 &&
204           blue[i] >= 0 && blue[i] < 65536)
205        {
206            bitmap->red[i] = red[i];
207            bitmap->green[i] = green[i];
208            bitmap->blue[i] = blue[i];
209        }
210    }
211}
212
213void caca_free_bitmap(struct caca_bitmap *bitmap)
214{
215    if(!bitmap)
216        return;
217
218    free(bitmap);
219}
220
221static void get_rgb_default(struct caca_bitmap *bitmap, unsigned char *pixels,
222                            int x, int y, int *r, int *g, int *b)
223{
224    int bits;
225
226    pixels += (bitmap->bpp / 8) * x + bitmap->pitch * y;
227
228    switch(bitmap->bpp / 8)
229    {
230        case 4:
231            bits = *(uint32_t *)(pixels + 0);
232            break;
233        case 3:
234            bits = (pixels[0] << 16) | (pixels[1] << 8) | (pixels[2]);
235            break;
236        case 2:
237            bits = *(uint16_t *)(pixels + 0);
238            break;
239        case 1:
240        default:
241            bits = pixels[0];
242            break;
243    }
244
245    if(bitmap->palette)
246    {
247        *r = bitmap->red[bits];
248        *g = bitmap->green[bits];
249        *b = bitmap->blue[bits];
250    }
251    else
252    {
253        *r = ((bits & bitmap->rmask) >> bitmap->rright) << bitmap->rleft;
254        *g = ((bits & bitmap->gmask) >> bitmap->gright) << bitmap->gleft;
255        *b = ((bits & bitmap->bmask) >> bitmap->bright) << bitmap->bleft;
256    }
257}
258
259static void rgb2hsv_default(int r, int g, int b, int *hue, int *sat, int *val)
260{
261    int min, max, delta;
262
263    min = r; max = r;
264    if(min > g) min = g; if(max < g) max = g;
265    if(min > b) min = b; if(max < b) max = b;
266
267    delta = max - min; /* 0 - 0xffff */
268    *val = max; /* 0 - 0xffff */
269
270    if(delta)
271    {
272        *sat = 0x1000 * delta / max * 0x10; /* 0 - 0xffff */
273
274        /* Generate *hue between 0 and 0x5ffff */
275        if( r == max )
276            *hue = 0x10000 + 0x100 * (g - b) / delta * 0x100;
277        else if( g == max )
278            *hue = 0x30000 + 0x100 * (b - r) / delta * 0x100;
279        else
280            *hue = 0x50000 + 0x100 * (r - g) / delta * 0x100;
281    }
282    else
283    {
284        *sat = 0;
285        *hue = 0;
286    }
287}
288
289void caca_draw_bitmap(int x1, int y1, int x2, int y2,
290                      struct caca_bitmap *bitmap, char *pixels)
291{
292    /* FIXME: this code is shite! */
293    static int white_colors[] =
294    {
295        CACA_COLOR_DARKGRAY,
296        CACA_COLOR_LIGHTGRAY,
297        CACA_COLOR_WHITE
298    };
299
300    static int light_colors[] =
301    {
302        CACA_COLOR_LIGHTMAGENTA,
303        CACA_COLOR_LIGHTRED,
304        CACA_COLOR_YELLOW,
305        CACA_COLOR_LIGHTGREEN,
306        CACA_COLOR_LIGHTCYAN,
307        CACA_COLOR_LIGHTBLUE,
308        CACA_COLOR_LIGHTMAGENTA
309    };
310
311    static int dark_colors[] =
312    {
313        CACA_COLOR_MAGENTA,
314        CACA_COLOR_RED,
315        CACA_COLOR_BROWN,
316        CACA_COLOR_GREEN,
317        CACA_COLOR_CYAN,
318        CACA_COLOR_BLUE,
319        CACA_COLOR_MAGENTA
320    };
321
322    static char foo[] =
323        "    "
324        " ,' "
325        ",`.'"
326        "i-:^"
327        "|/;\\"
328        "=+ox"
329        "<x%>"
330        "&z$w"
331        "WXKM"
332        "#8##"
333        "8@8#"
334        "@8@8";
335
336    int x, y, w, h, pitch;
337
338    if(!bitmap || !pixels)
339        return;
340
341    w = bitmap->w;
342    h = bitmap->h;
343    pitch = bitmap->pitch;
344
345    if(x1 > x2)
346    {
347        int tmp = x2; x2 = x1; x1 = tmp;
348    }
349
350    if(y1 > y2)
351    {
352        int tmp = y2; y2 = y1; y1 = tmp;
353    }
354
355    for(y = y1 > 0 ? y1 : 0; y <= y2 && y <= (int)caca_get_height(); y++)
356    {
357        /* Initialize dither tables for the current line */
358        _init_dither(y);
359
360        /* Dither the current line */
361        for(x = x1 > 0 ? x1 : 0; x <= x2 && x <= (int)caca_get_width(); x++)
362        {
363            int ch;
364            int hue, sat, val, r, g, b, R, G, B;
365            int fromx = w * (x - x1) / (x2 - x1 + 1);
366            int fromy = h * (y - y1) / (y2 - y1 + 1);
367
368            /* Clip values (yuck) */
369            if(fromx == 0) fromx = 1;
370            if(fromy == 0) fromy = 1;
371
372            /* First get RGB */
373            R = 0, G = 0, B = 0;
374            get_rgb_default(bitmap, pixels, fromx, fromy, &r, &g, &b);
375            R += r; G += g; B += b;
376            get_rgb_default(bitmap, pixels, fromx - 1, fromy, &r, &g, &b);
377            R += r; G += g; B += b;
378            get_rgb_default(bitmap, pixels, fromx, fromy - 1, &r, &g, &b);
379            R += r; G += g; B += b;
380            get_rgb_default(bitmap, pixels, fromx + 1, fromy, &r, &g, &b);
381            R += r; G += g; B += b;
382            get_rgb_default(bitmap, pixels, fromx, fromy + 1, &r, &g, &b);
383            R += r; G += g; B += b;
384            R /= 5; G /= 5; B /= 5;
385
386            /* Now get HSV from RGB */
387            rgb2hsv_default(R, G, B, &hue, &sat, &val);
388
389            if(sat < 0x2000 + _get_dither() * 0x80)
390                caca_set_color(white_colors[val * 3 / 0x10000], CACA_COLOR_BLACK);
391            else if(val > 0x8000 + _get_dither() * 0x40)
392                caca_set_color(light_colors[(hue + _get_dither() * 0x100) / 0x10000], CACA_COLOR_BLACK);
393            else
394                caca_set_color(dark_colors[(hue + _get_dither() * 0x100) / 0x10000], CACA_COLOR_BLACK);
395
396            /* FIXME: choose better characters! */
397            ch = (val + 0x20 * _get_dither() - 0x1000 /*???*/) * 10 / 0x10000;
398            ch = 4 * ch + (_get_dither() + 8) / 0x40;
399            caca_putchar(x, y, foo[ch]);
400
401            _increment_dither();
402        }
403    }
404}
405
406/*
407 * XXX: The following functions are local.
408 */
409
410/*
411 * No dithering
412 */
413static void init_no_dither(int line)
414{
415    ;
416}
417
418static int get_no_dither(void)
419{
420    return 0x80;
421}
422
423static void increment_no_dither(void)
424{
425    return;
426}
427
428/*
429 * Ordered 4 dithering
430 */
431/*static int dither4x4[] = { 5,  0,  1,  6,
432                          -1, -6, -5,  2,
433                          -2, -7, -8,  3,
434                           4, -3, -4, -7};*/
435static int *ordered4_table;
436static int ordered4_index;
437
438static void init_ordered4_dither(int line)
439{
440    static int dither4x4[] =
441    {
442        0x00, 0x80, 0x20, 0xa0,
443        0xc0, 0x40, 0xe0, 0x60,
444        0x30, 0xb0, 0x10, 0x90,
445        0xf0, 0x70, 0xd0, 0x50
446    };
447
448    ordered4_table = dither4x4 + (line % 4) * 4;
449    ordered4_index = 0;
450}
451
452static int get_ordered4_dither(void)
453{
454    return ordered4_table[ordered4_index];
455}
456
457static void increment_ordered4_dither(void)
458{
459    ordered4_index = (ordered4_index + 1) % 4;
460}
461
462/*
463 * Ordered 8 dithering
464 */
465static int *ordered8_table;
466static int ordered8_index;
467
468static void init_ordered8_dither(int line)
469{
470    static int dither8x8[] =
471    {
472        0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8,
473        0xc0, 0x40, 0xe0, 0x60, 0xc8, 0x48, 0xe8, 0x68,
474        0x30, 0xb0, 0x10, 0x90, 0x38, 0xb8, 0x18, 0x98,
475        0xf0, 0x70, 0xd0, 0x50, 0xf8, 0x78, 0xd8, 0x58,
476        0x0c, 0x8c, 0x2c, 0xac, 0x04, 0x84, 0x24, 0xa4,
477        0xcc, 0x4c, 0xec, 0x6c, 0xc4, 0x44, 0xe4, 0x64,
478        0x3c, 0xbc, 0x1c, 0x9c, 0x34, 0xb4, 0x14, 0x94,
479        0xfc, 0x7c, 0xdc, 0x5c, 0xf4, 0x74, 0xd4, 0x54,
480    };
481
482    ordered8_table = dither8x8 + (line % 8) * 8;
483    ordered8_index = 0;
484}
485
486static int get_ordered8_dither(void)
487{
488    return ordered8_table[ordered8_index];
489}
490
491static void increment_ordered8_dither(void)
492{
493    ordered8_index = (ordered8_index + 1) % 8;
494}
495
496/*
497 * Random dithering
498 */
499static void init_random_dither(int line)
500{
501    ;
502}
503
504static int get_random_dither(void)
505{
506    return caca_rand(0x00, 0xff);
507}
508
509static void increment_random_dither(void)
510{
511    return;
512}
513
Note: See TracBrowser for help on using the repository browser.