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

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