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

Last change on this file since 230 was 230, checked in by Sam Hocevar, 17 years ago
  • src/bitmap.c: + Dither chroma outside of rgb2hsv_default(). + Clip fromx and fromy values.
  • NOTES: + Link to the XTerm control sequences.
  • examples/view.c: + Draw status bar. + Move with 'h' 'j' 'k' 'l', à la vi. + '?' toggles a help menu.
  • Property svn:keywords set to Id
File size: 11.1 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 230 2003-11-28 20:39:54Z 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_ordered_dither(int);
57static int get_ordered_dither(void);
58static void increment_ordered_dither(void);
59
60static void init_random_dither(int);
61static int get_random_dither(void);
62static void increment_random_dither(void);
63
64/* Current dithering method */
65static enum caca_dithering _caca_dithering = CACA_DITHER_NONE;
66
67static void (*_init_dither) (int) = init_no_dither;
68static int (*_get_dither) (void) = get_no_dither;
69static void (*_increment_dither) (void) = increment_no_dither;
70
71void caca_set_dithering(enum caca_dithering dither)
72{
73    switch(dither)
74    {
75    case CACA_DITHER_NONE:
76        _init_dither = init_no_dither;
77        _get_dither = get_no_dither;
78        _increment_dither = increment_no_dither;
79        break;
80
81    case CACA_DITHER_ORDERED:
82        _init_dither = init_ordered_dither;
83        _get_dither = get_ordered_dither;
84        _increment_dither = increment_ordered_dither;
85        break;
86
87    case CACA_DITHER_RANDOM:
88        _init_dither = init_random_dither;
89        _get_dither = get_random_dither;
90        _increment_dither = increment_random_dither;
91        break;
92
93    default:
94        return;
95    }
96
97    _caca_dithering = dither;
98}
99
100struct caca_bitmap
101{
102    int bpp, palette;
103    int w, h, pitch;
104    int rmask, gmask, bmask;
105    int rright, gright, bright;
106    int rleft, gleft, bleft;
107    void (*get_hsv)(struct caca_bitmap *, char *, int, int);
108    int red[256], green[256], blue[256];
109};
110
111static void mask2shift(int mask, int *right, int *left)
112{
113    int rshift = 0, lshift = 0;
114    *right = *left = 0;
115
116    if(!mask)
117        return;
118
119    while(!(mask & 1))
120    {
121        mask >>= 1;
122        rshift++;
123    }
124    *right = rshift;
125
126    while(mask & 1)
127    {
128        mask >>= 1;
129        lshift++;
130    }
131    *left = 16 - lshift;
132}
133
134struct caca_bitmap *caca_create_bitmap(int bpp, int w, int h, int pitch,
135                                       int rmask, int gmask, int bmask)
136{
137    struct caca_bitmap *bitmap;
138
139    /* Currently only this format is supported. Will improve later. */
140    if(!w || !h || !pitch || bpp > 32 || bpp < 8)
141        return NULL;
142
143    bitmap = malloc(sizeof(struct caca_bitmap));
144    if(!bitmap)
145        return NULL;
146
147    bitmap->bpp = bpp;
148    bitmap->palette = 0;
149
150    bitmap->w = w;
151    bitmap->h = h;
152    bitmap->pitch = pitch;
153
154    bitmap->rmask = rmask;
155    bitmap->gmask = gmask;
156    bitmap->bmask = bmask;
157
158    /* Load bitmasks */
159    if(rmask || gmask || bmask)
160    {
161        mask2shift(rmask, &bitmap->rright, &bitmap->rleft);
162        mask2shift(gmask, &bitmap->gright, &bitmap->gleft);
163        mask2shift(bmask, &bitmap->bright, &bitmap->bleft);
164    }
165
166    /* In 8 bpp mode, default to a grayscale palette */
167    if(bpp == 8)
168    {
169        int i;
170        bitmap->palette = 1;
171        for(i = 0; i < 256; i++)
172        {
173            bitmap->red[i] = i * 0x100;
174            bitmap->green[i] = i * 0x100;
175            bitmap->blue[i] = i * 0x100;
176        }
177    }
178
179    return bitmap;
180}
181
182void caca_set_bitmap_palette(struct caca_bitmap *bitmap,
183                             int red[], int green[], int blue[])
184{
185    int i;
186
187    if(bitmap->bpp != 8)
188        return;
189
190    for(i = 0; i < 256; i++)
191    {
192        if(red[i] >= 0 && red[i] < 65536 &&
193           green[i] >= 0 && green[i] < 65536 &&
194           blue[i] >= 0 && blue[i] < 65536)
195        {
196            bitmap->red[i] = red[i];
197            bitmap->green[i] = green[i];
198            bitmap->blue[i] = blue[i];
199        }
200    }
201}
202
203void caca_free_bitmap(struct caca_bitmap *bitmap)
204{
205    if(!bitmap)
206        return;
207
208    free(bitmap);
209}
210
211static void get_rgb_default(struct caca_bitmap *bitmap, unsigned char *pixels,
212                            int x, int y, int *r, int *g, int *b)
213{
214    int bits;
215
216    pixels += (bitmap->bpp / 8) * x + bitmap->pitch * y;
217
218    switch(bitmap->bpp / 8)
219    {
220        case 4:
221            bits = *(uint32_t *)(pixels + 0);
222            break;
223        case 3:
224            bits = (pixels[0] << 16) | (pixels[1] << 8) | (pixels[2]);
225            break;
226        case 2:
227            bits = *(uint16_t *)(pixels + 0);
228            break;
229        case 1:
230        default:
231            bits = pixels[0];
232            break;
233    }
234
235    if(bitmap->palette)
236    {
237        *r = bitmap->red[bits];
238        *g = bitmap->green[bits];
239        *b = bitmap->blue[bits];
240    }
241    else
242    {
243        *r = ((bits & bitmap->rmask) >> bitmap->rright) << bitmap->rleft;
244        *g = ((bits & bitmap->gmask) >> bitmap->gright) << bitmap->gleft;
245        *b = ((bits & bitmap->bmask) >> bitmap->bright) << bitmap->bleft;
246    }
247}
248
249static void rgb2hsv_default(int r, int g, int b, int *hue, int *sat, int *val)
250{
251    int min, max, delta;
252
253    min = r; max = r;
254    if(min > g) min = g; if(max < g) max = g;
255    if(min > b) min = b; if(max < b) max = b;
256
257    delta = max - min; /* 0 - 0xffff */
258    *val = max; /* 0 - 0xffff */
259
260    if(delta)
261    {
262        *sat = 0x1000 * delta / max * 0x10; /* 0 - 0xffff */
263
264        /* Generate *hue between 0 and 0x5ffff */
265        if( r == max )
266            *hue = 0x10000 + 0x100 * (g - b) / delta * 0x100;
267        else if( g == max )
268            *hue = 0x30000 + 0x100 * (b - r) / delta * 0x100;
269        else
270            *hue = 0x50000 + 0x100 * (r - g) / delta * 0x100;
271    }
272    else
273    {
274        *sat = 0;
275        *hue = 0;
276    }
277}
278
279void caca_draw_bitmap(int x1, int y1, int x2, int y2,
280                      struct caca_bitmap *bitmap, char *pixels)
281{
282    /* FIXME: this code is shite! */
283    static int white_colors[] =
284    {
285        CACA_COLOR_DARKGRAY,
286        CACA_COLOR_LIGHTGRAY,
287        CACA_COLOR_WHITE
288    };
289
290    static int light_colors[] =
291    {
292        CACA_COLOR_LIGHTMAGENTA,
293        CACA_COLOR_LIGHTRED,
294        CACA_COLOR_YELLOW,
295        CACA_COLOR_LIGHTGREEN,
296        CACA_COLOR_LIGHTCYAN,
297        CACA_COLOR_LIGHTBLUE,
298        CACA_COLOR_LIGHTMAGENTA
299    };
300
301    static int dark_colors[] =
302    {
303        CACA_COLOR_MAGENTA,
304        CACA_COLOR_RED,
305        CACA_COLOR_BROWN,
306        CACA_COLOR_GREEN,
307        CACA_COLOR_CYAN,
308        CACA_COLOR_BLUE,
309        CACA_COLOR_MAGENTA
310    };
311
312    static char foo[] =
313    {
314        ' ', ' ', ' ', ' ',
315        ',', '`', '.', '\'',
316        'i', '-', ':', '^',
317        '|', '/', ';', '\\',
318        '=', '+', 'o', 'x',
319        '<', 'x', '%', '>',
320        '&', 'z', '$', 'w',
321        'W', 'X', 'K', 'M',
322        '#', '8', '#', '#',
323        '8', '@', '8', '#',
324        '@', '8', '@', '8',
325    };
326
327    int x, y, w, h, pitch;
328
329    if(!bitmap || !pixels)
330        return;
331
332    w = bitmap->w;
333    h = bitmap->h;
334    pitch = bitmap->pitch;
335
336    if(x1 > x2)
337    {
338        int tmp = x2; x2 = x1; x1 = tmp;
339    }
340
341    if(y1 > y2)
342    {
343        int tmp = y2; y2 = y1; y1 = tmp;
344    }
345
346    for(y = y1 > 0 ? y1 : 0; y <= y2 && y <= (int)caca_get_height(); y++)
347    {
348        /* Initialize dither tables for the current line */
349        _init_dither(y);
350
351        /* Dither the current line */
352        for(x = x1 > 0 ? x1 : 0; x <= x2 && x <= (int)caca_get_width(); x++)
353        {
354            int ch;
355            int hue, sat, val, r, g, b, R, G, B;
356            int fromx = w * (x - x1) / (x2 - x1 + 1);
357            int fromy = h * (y - y1) / (y2 - y1 + 1);
358
359            /* Clip values (yuck) */
360            if(fromx == 0) fromx = 1;
361            if(fromy == 0) fromy = 1;
362
363            /* First get RGB */
364            R = 0, G = 0, B = 0;
365            get_rgb_default(bitmap, pixels, fromx, fromy, &r, &g, &b);
366            R += r; G += g; B += b;
367            get_rgb_default(bitmap, pixels, fromx - 1, fromy, &r, &g, &b);
368            R += r; G += g; B += b;
369            get_rgb_default(bitmap, pixels, fromx, fromy - 1, &r, &g, &b);
370            R += r; G += g; B += b;
371            get_rgb_default(bitmap, pixels, fromx + 1, fromy, &r, &g, &b);
372            R += r; G += g; B += b;
373            get_rgb_default(bitmap, pixels, fromx, fromy + 1, &r, &g, &b);
374            R += r; G += g; B += b;
375            R /= 5; G /= 5; B /= 5;
376
377            /* Now get HSV from RGB */
378            rgb2hsv_default(R, G, B, &hue, &sat, &val);
379
380            if(sat < 0x6000 + _get_dither() * 0x800)
381                caca_set_color(white_colors[val * 3 / 0x10000], CACA_COLOR_BLACK);
382            else if(val > (_get_dither() + 40) * 0x400)
383                caca_set_color(light_colors[(hue + 0x8000 + _get_dither() * 0x1000) / 0x10000], CACA_COLOR_BLACK);
384            else
385                caca_set_color(dark_colors[(hue + 0x8000 + _get_dither() * 0x1000) / 0x10000], CACA_COLOR_BLACK);
386
387            /* FIXME: choose better characters! */
388            ch = (val + 0x200 * _get_dither()) * 10 / 0x10000;
389            ch = 4 * ch + (_get_dither() + 8) / 4;
390            caca_putchar(x, y, foo[ch]);
391
392            _increment_dither();
393        }
394    }
395}
396
397/*
398 * XXX: The following functions are local.
399 */
400
401/*
402 * No dithering
403 */
404static void init_no_dither(int line)
405{
406    ;
407}
408
409static int get_no_dither(void)
410{
411    return 0;
412}
413
414static void increment_no_dither(void)
415{
416    return;
417}
418
419/*
420 * Ordered dithering
421 */
422static int dither4x4[] = {-8,  0, -6,  2,
423                           4, -4,  6, -2,
424                          -5,  3, -7,  1,
425                           7, -1,  5, -3};
426static int *dither_table;
427static int dither_index;
428
429static void init_ordered_dither(int line)
430{
431    dither_table = dither4x4 + (line % 4) * 4;
432    dither_index = 0;
433}
434
435static int get_ordered_dither(void)
436{
437    return dither_table[dither_index];
438}
439
440static void increment_ordered_dither(void)
441{
442    dither_index = (dither_index + 1) % 4;
443}
444
445/*
446 * Random dithering
447 */
448static void init_random_dither(int line)
449{
450    ;
451}
452
453static int get_random_dither(void)
454{
455    return caca_rand(-8, 7);
456}
457
458static void increment_random_dither(void)
459{
460    return;
461}
462
Note: See TracBrowser for help on using the repository browser.