| @@ -40,6 +40,12 @@ typedef unsigned char uint8_t; | |||
| #include "caca.h" | |||
| #include "caca_internals.h" | |||
| static void mask2shift(int, int *, int *); | |||
| static void get_rgb_default(struct caca_bitmap *, unsigned char *, | |||
| int, int, int *, int *, int *); | |||
| static void rgb2hsv_default(int, int, int, int *, int *, int *); | |||
| /* Dithering methods */ | |||
| static void init_no_dither(int); | |||
| static int get_no_dither(void); | |||
| @@ -91,19 +97,45 @@ void caca_set_dithering(enum caca_dithering dither) | |||
| struct caca_bitmap | |||
| { | |||
| int bpp; | |||
| int bpp, palette; | |||
| int w, h, pitch; | |||
| int rmask, gmask, bmask; | |||
| int rright, gright, bright; | |||
| int rleft, gleft, bleft; | |||
| void (*get_hsv)(struct caca_bitmap *, char *, int, int); | |||
| int red[256], green[256], blue[256]; | |||
| }; | |||
| static void mask2shift(int mask, int *right, int *left) | |||
| { | |||
| int rshift = 0, lshift = 0; | |||
| *right = *left = 0; | |||
| if(!mask) | |||
| return; | |||
| while(!(mask & 1)) | |||
| { | |||
| mask >>= 1; | |||
| rshift++; | |||
| } | |||
| *right = rshift; | |||
| while(mask & 1) | |||
| { | |||
| mask >>= 1; | |||
| lshift++; | |||
| } | |||
| *left = 16 - lshift; | |||
| } | |||
| struct caca_bitmap *caca_create_bitmap(int bpp, int w, int h, int pitch, | |||
| int rmask, int gmask, int bmask) | |||
| { | |||
| struct caca_bitmap *bitmap; | |||
| /* Currently only this format is supported. Will improve later. */ | |||
| if(!w || !h || !pitch || bpp != 32 || | |||
| rmask != 0x00ff0000 || gmask != 0x0000ff00 || bmask != 0x000000ff) | |||
| if(!w || !h || !pitch || bpp > 32 || bpp < 8) | |||
| return NULL; | |||
| bitmap = malloc(sizeof(struct caca_bitmap)); | |||
| @@ -111,6 +143,7 @@ struct caca_bitmap *caca_create_bitmap(int bpp, int w, int h, int pitch, | |||
| return NULL; | |||
| bitmap->bpp = bpp; | |||
| bitmap->palette = 0; | |||
| bitmap->w = w; | |||
| bitmap->h = h; | |||
| @@ -120,9 +153,51 @@ struct caca_bitmap *caca_create_bitmap(int bpp, int w, int h, int pitch, | |||
| bitmap->gmask = gmask; | |||
| bitmap->bmask = bmask; | |||
| /* Load bitmasks */ | |||
| if(rmask || gmask || bmask) | |||
| { | |||
| mask2shift(rmask, &bitmap->rright, &bitmap->rleft); | |||
| mask2shift(gmask, &bitmap->gright, &bitmap->gleft); | |||
| mask2shift(bmask, &bitmap->bright, &bitmap->bleft); | |||
| } | |||
| /* In 8 bpp mode, default to a grayscale palette */ | |||
| if(bpp == 8) | |||
| { | |||
| int i; | |||
| bitmap->palette = 1; | |||
| for(i = 0; i < 256; i++) | |||
| { | |||
| bitmap->red[i] = i * 0x100; | |||
| bitmap->green[i] = i * 0x100; | |||
| bitmap->blue[i] = i * 0x100; | |||
| } | |||
| } | |||
| return bitmap; | |||
| } | |||
| void caca_set_bitmap_palette(struct caca_bitmap *bitmap, | |||
| int red[], int green[], int blue[]) | |||
| { | |||
| int i; | |||
| if(bitmap->bpp != 8) | |||
| return; | |||
| for(i = 0; i < 256; i++) | |||
| { | |||
| if(red[i] >= 0 && red[i] < 65536 && | |||
| green[i] >= 0 && green[i] < 65536 && | |||
| blue[i] >= 0 && blue[i] < 65536) | |||
| { | |||
| bitmap->red[i] = red[i]; | |||
| bitmap->green[i] = green[i]; | |||
| bitmap->blue[i] = blue[i]; | |||
| } | |||
| } | |||
| } | |||
| void caca_free_bitmap(struct caca_bitmap *bitmap) | |||
| { | |||
| if(!bitmap) | |||
| @@ -131,14 +206,126 @@ void caca_free_bitmap(struct caca_bitmap *bitmap) | |||
| free(bitmap); | |||
| } | |||
| static void get_rgb_default(struct caca_bitmap *bitmap, unsigned char *pixels, | |||
| int x, int y, int *r, int *g, int *b) | |||
| { | |||
| int bits; | |||
| pixels += (bitmap->bpp / 8) * x + bitmap->pitch * y; | |||
| switch(bitmap->bpp / 8) | |||
| { | |||
| case 4: | |||
| bits = *(uint32_t *)(pixels + 0); | |||
| break; | |||
| case 3: | |||
| bits = (pixels[0] << 16) | |||
| | (pixels[1] << 8) | |||
| | (pixels[2]); | |||
| break; | |||
| case 2: | |||
| bits = *(uint16_t *)(pixels + 0); | |||
| break; | |||
| case 1: | |||
| default: | |||
| bits = pixels[0]; | |||
| break; | |||
| } | |||
| if(bitmap->palette) | |||
| { | |||
| *r = bitmap->red[bits]; | |||
| *g = bitmap->green[bits]; | |||
| *b = bitmap->blue[bits]; | |||
| } | |||
| else | |||
| { | |||
| *r = ((bits & bitmap->rmask) >> bitmap->rright) << bitmap->rleft; | |||
| *g = ((bits & bitmap->gmask) >> bitmap->gright) << bitmap->gleft; | |||
| *b = ((bits & bitmap->bmask) >> bitmap->bright) << bitmap->bleft; | |||
| } | |||
| } | |||
| static void rgb2hsv_default(int r, int g, int b, int *hue, int *sat, int *val) | |||
| { | |||
| int min, max, delta; | |||
| min = r; max = r; | |||
| if(min > g) min = g; if(max < g) max = g; | |||
| if(min > b) min = b; if(max < b) max = b; | |||
| delta = max - min; /* 0 - 65535 */ | |||
| *val = max; /* 0 - 65535 */ | |||
| *sat = max ? 0x100 * delta / max * 0x100: 0; /* 0 - 65536 */ | |||
| if(*sat > (_get_dither() + 24) * 0x400) | |||
| { | |||
| /* XXX: Values should be between 1 and 6, but since we | |||
| * are dithering, there may be overflows, hence our bigger | |||
| * *_colors[] tables. */ | |||
| if( r == max ) | |||
| *hue = 0x10000 + 0x100 * (g - b) / delta * 0x100; | |||
| else if( g == max ) | |||
| *hue = 0x30000 + 0x100 * (b - r) / delta * 0x100; | |||
| else | |||
| *hue = 0x50000 + 0x100 * (r - g) / delta * 0x100; | |||
| *hue = (*hue + 0x8000 + 0x1000 * _get_dither()) / 0x10000; | |||
| } | |||
| else | |||
| { | |||
| *sat = 0; | |||
| } | |||
| } | |||
| void caca_draw_bitmap(int x1, int y1, int x2, int y2, | |||
| struct caca_bitmap *bitmap, char *pixels) | |||
| { | |||
| /* FIXME: this code is shite! */ | |||
| static int white_colors[] = {CACA_COLOR_DARKGRAY, CACA_COLOR_LIGHTGRAY, CACA_COLOR_WHITE}; | |||
| static int light_colors[] = {CACA_COLOR_LIGHTMAGENTA, CACA_COLOR_LIGHTRED, CACA_COLOR_YELLOW, CACA_COLOR_LIGHTGREEN, CACA_COLOR_LIGHTCYAN, CACA_COLOR_LIGHTBLUE, CACA_COLOR_LIGHTMAGENTA}; | |||
| static int dark_colors[] = {CACA_COLOR_MAGENTA, CACA_COLOR_RED, CACA_COLOR_BROWN, CACA_COLOR_GREEN, CACA_COLOR_CYAN, CACA_COLOR_BLUE, CACA_COLOR_MAGENTA}; | |||
| static char foo[] = { ' ', '.', ':', ';', '=', '%', '$', 'W', '#', '8', '@' }; | |||
| static int white_colors[] = | |||
| { | |||
| CACA_COLOR_DARKGRAY, | |||
| CACA_COLOR_LIGHTGRAY, | |||
| CACA_COLOR_WHITE | |||
| }; | |||
| static int light_colors[] = | |||
| { | |||
| CACA_COLOR_LIGHTMAGENTA, | |||
| CACA_COLOR_LIGHTRED, | |||
| CACA_COLOR_YELLOW, | |||
| CACA_COLOR_LIGHTGREEN, | |||
| CACA_COLOR_LIGHTCYAN, | |||
| CACA_COLOR_LIGHTBLUE, | |||
| CACA_COLOR_LIGHTMAGENTA | |||
| }; | |||
| static int dark_colors[] = | |||
| { | |||
| CACA_COLOR_MAGENTA, | |||
| CACA_COLOR_RED, | |||
| CACA_COLOR_BROWN, | |||
| CACA_COLOR_GREEN, | |||
| CACA_COLOR_CYAN, | |||
| CACA_COLOR_BLUE, | |||
| CACA_COLOR_MAGENTA | |||
| }; | |||
| static char foo[] = | |||
| { | |||
| ' ', ' ', ' ', ' ', | |||
| ',', '`', '.', '\'', | |||
| 'i', '-', ':', '^', | |||
| '|', '/', ';', '\\', | |||
| '=', '+', 'o', 'x', | |||
| '<', 'x', '%', '>', | |||
| '&', 'z', '$', '§', | |||
| 'W', 'X', 'K', 'M', | |||
| '#', '8', '#', '#', | |||
| '8', '@', '8', '#', | |||
| '@', '8', '@', '8', | |||
| }; | |||
| int x, y, w, h, pitch; | |||
| if(!bitmap || !pixels) | |||
| @@ -166,47 +353,39 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, | |||
| /* Dither the current line */ | |||
| for(x = x1 > 0 ? x1 : 0; x <= x2 && x <= (int)caca_get_width(); x++) | |||
| { | |||
| int ch; | |||
| int hue, sat, val, r, g, b, R, G, B; | |||
| int fromx = w * (x - x1) / (x2 - x1 + 1); | |||
| int fromy = h * (y - y1) / (y2 - y1 + 1); | |||
| /* FIXME: bwahaaa, we don't even respect masks */ | |||
| int b = ((unsigned char *)pixels)[4 * fromx + pitch * fromy]; | |||
| int g = ((unsigned char *)pixels)[4 * fromx + 1 + pitch * fromy]; | |||
| int r = ((unsigned char *)pixels)[4 * fromx + 2 + pitch * fromy]; | |||
| int hue, sat, val; | |||
| int min = r, max = r, delta; | |||
| if(min > g) min = g; if(max < g) max = g; | |||
| if(min > b) min = b; if(max < b) max = b; | |||
| delta = max - min; | |||
| val = max; /* 0 - 255 */ | |||
| sat = max ? 256 * delta / max : 0; /* 0 - 255 */ | |||
| if(sat > (_get_dither() + 24) * 4) | |||
| { | |||
| /* XXX: Values should be between 1 and 6, but since we | |||
| * are dithering, there may be overflows, hence our bigger | |||
| * *_colors[] tables. */ | |||
| if( r == max ) | |||
| hue = 256 + 256 * (g - b) / delta; | |||
| else if( g == max ) | |||
| hue = 768 + 256 * (b - r) / delta; | |||
| else | |||
| hue = 1280 + 256 * (r - g) / delta; | |||
| hue = (hue + 128 + 16 * _get_dither()) / 256; | |||
| if(val > (_get_dither() + 40) * 4) | |||
| caca_set_color(light_colors[hue]); | |||
| else | |||
| caca_set_color(dark_colors[hue]); | |||
| } | |||
| /* First get RGB */ | |||
| R = 0, G = 0, B = 0; | |||
| get_rgb_default(bitmap, pixels, fromx, fromy, &r, &g, &b); | |||
| R += r; G += g; B += b; | |||
| get_rgb_default(bitmap, pixels, fromx - 1, fromy, &r, &g, &b); | |||
| R += r; G += g; B += b; | |||
| get_rgb_default(bitmap, pixels, fromx, fromy - 1, &r, &g, &b); | |||
| R += r; G += g; B += b; | |||
| get_rgb_default(bitmap, pixels, fromx + 1, fromy, &r, &g, &b); | |||
| R += r; G += g; B += b; | |||
| get_rgb_default(bitmap, pixels, fromx, fromy + 1, &r, &g, &b); | |||
| R += r; G += g; B += b; | |||
| R /= 5; G /= 5; B /= 5; | |||
| /* Now get HSV from RGB */ | |||
| rgb2hsv_default(R, G, B, &hue, &sat, &val); | |||
| if(!sat) | |||
| caca_set_color(white_colors[val * 3 / 0x10000], CACA_COLOR_BLACK); | |||
| else if(val > (_get_dither() + 40) * 0x400) | |||
| caca_set_color(light_colors[hue], CACA_COLOR_BLACK); | |||
| else | |||
| { | |||
| caca_set_color(white_colors[max * 3 / 256]); | |||
| } | |||
| caca_set_color(dark_colors[hue], CACA_COLOR_BLACK); | |||
| caca_putchar(x, y, foo[(r + g + b + 2 * _get_dither()) / 3 / 25]); | |||
| /* FIXME: choose better characters! */ | |||
| ch = (val + 0x200 * _get_dither()) * 10 / 0x10000; | |||
| ch = 4 * ch + (_get_dither() + 8) / 4; | |||
| caca_putchar(x, y, foo[ch]); | |||
| _increment_dither(); | |||
| } | |||