/* dither.c * Dithering * (c) 2000-2002 Karel 'Clock' Kulhavy * This file is a part of the Links program, released under GPL. */ #include "cfg.h" #ifdef G #include "links.h" #include "bits.h" #ifdef HAVE_MATH_H #include #endif /* The input of dithering function is 3 times 16-bit value. The value is * proportional to light that will go out of the monitor. Only in this space it * is possible to dither accurately because distributing the error means maintaining * the photon count (blurring caused by human eye from big distance preservers photon * count, just spreads the photons a little around) * The 8-bit dithering functions are to be used only for dithering text. */ /* This source does dithering and rounding of images (in photon space) into * struct bitmap. It also computes colors given r,g,b. */ /* No dither function destroys the passed bitmap */ /* All dither functions take format in booklike order without inter-line gaps. * red, green, blue order. Input bytes=3*y*x. Takes x and y from bitmap. */ /* The input of dithering function is 3 times 8-bit value. The value is * proportional to desired input into graphics driver (which is in fact * proportional to monitor's input voltage for graphic drivers that do not * pollute the picture with gamma correction) */ /* Dithering algorithm: Floyd-Steinberg error distribution. The used * coefficients are depicted in the following table. The empty box denotes the * originator pixel that generated the error. * * +----+----+ * | |7/16| * +----+----+----+ * |3/16|5/16|1/16| * +----+----+----+ */ /* We assume here int holds at least 32 bits */ static int *red_table = DUMMY, *green_table = DUMMY, *blue_table = DUMMY; static int table_16 = 1; static unsigned short * #ifdef __GNUC__ #if __GNUC__ == 3 volatile #endif #endif real_colors_table = NULL; /* If we want to represent some 16-bit from-screen-light, it would require certain display input * value (0-255 red, 0-255 green, 0-255 blue), possibly not a whole number. [red|green|blue]_table * translares 16-bit light to the nearest index (that should be fed into the * display). Nearest is meant in realm of numbers that are proportional to * display input. The table also says what will be the real value this rounded * display input yields. index is in * bits 16-31, real light value is in bits 0-15. real light value is 0 (no * photons) to 65535 (maximum photon flux). This is subtracted from wanted * value and error remains which is the distributed into some neighboring * pixels. * * Index memory organization * ------------------------- * 1 byte per pixel: obvious. The output byte is OR of all three LSB's from red_table, * green_table, blue_table * 2 bytes per pixel: cast all three values to unsigned short, OR them together * and dump the short into the memory * 3 and 4 bytes per pixel: LSB's contain the red, green, and blue bytes. */ /* These tables allow the most precise dithering possible: * a) Rouding is performed always to perceptually nearest value, not to * nearest light flux * b) error addition is performed in photon space to maintain fiedlity * c) photon space addition from b) is performed with 16 bits thus not * degrading 24-bit images */ /* We assume here unsigned short holds at least 16 bits */ static unsigned short round_red_table[256]; static unsigned short round_green_table[256]; static unsigned short round_blue_table[256]; /* Transforms sRGB red, green, blue (0-255) to light of nearest voltage to * voltage appropriate to given sRGB coordinate. */ void (*round_fn)(unsigned short *my_restrict in, struct bitmap *out); /* When you finish the stuff with dither_start, dither_restart, just do "if (dregs) mem_free(dregs);" */ static void (*dither_fn_internal)(unsigned short *my_restrict in, struct bitmap *out, int *dregs); int slow_fpu = -1; /* EMPIRE IMAGINE FEAR */ #define LTABLES \ ir = in[0];\ ig = in[1];\ ib = in[2];\ r+=(int)ir;\ g+=(int)ig;\ b+=(int)ib;\ in+=3;\ {\ int rc=r,gc=g,bc=b;\ if ((unsigned)rc>65535) rc=rc<0?0:65535;\ if ((unsigned)gc>65535) gc=gc<0?0:65535;\ if ((unsigned)bc>65535) bc=bc<0?0:65535;\ rt=red_table[rc >> shift];\ gt=green_table[gc >> shift];\ bt=blue_table[bc >> shift];\ }\ SAVE_CODE\ rt=r-(rt&65535);\ gt=g-(gt&65535);\ bt=b-(bt&65535);\ #define BODY \ LTABLES\ r=bptr[3];\ g=bptr[4];\ b=bptr[5];\ r+=rt;\ g+=gt;\ b+=bt;\ rt+=8;\ gt+=8;\ bt+=8;\ rt>>=4;\ gt>>=4;\ bt>>=4;\ r-=9*rt;\ g-=9*gt;\ b-=9*bt;\ bptr[3]=rt;\ bptr[4]=gt;\ bptr[5]=bt; #define BODYR \ LTABLES\ rt+=8;\ gt+=8;\ bt+=8;\ rt>>=4;\ gt>>=4;\ bt>>=4;\ bptr[-3]+=3*rt;\ bptr[-2]+=3*gt;\ bptr[-1]+=3*bt;\ bptr[0]+=5*rt;\ bptr[1]+=5*gt;\ bptr[2]+=5*bt; #define BODYC \ LTABLES\ r=rt;\ g=gt;\ b=bt; #define BODYL \ bptr=dregs;\ r=bptr[0];\ g=bptr[1];\ b=bptr[2];\ BODY\ bptr[0]=5*rt;\ bptr[1]=5*gt;\ bptr[2]=5*bt;\ bptr+=3; #define BODYI \ BODY\ bptr[0]+=5*rt;\ bptr[1]+=5*gt;\ bptr[2]+=5*bt;\ bptr[-3]+=3*rt;\ bptr[-2]+=3*gt;\ bptr[-1]+=3*bt;\ bptr+=3; #define DITHER_TEMPLATE(template_name, sh) \ static void template_name(unsigned short *my_restrict in, struct bitmap *out, int *dregs)\ {\ const int shift = sh;\ unsigned short ir, ig, ib;\ int r,g,b,o,rt,gt,bt,y,x;\ unsigned char *my_restrict outp=out->data;\ int *my_restrict bptr;\ ssize_t skip = out->skip - SKIP_CODE;\ \ o=0;o=o; /*warning go away */\ switch(out->x){\ \ case 0:\ return;\ \ case 1:\ r=g=b=0;\ for (y=out->y;y;y--){\ BODYC\ outp+=skip;\ }\ break;\ \ default:\ for (y=out->y;y;y--){\ BODYL\ for (x=out->x-2;x;x--){\ BODYI\ }\ BODYR\ outp+=skip;\ }\ break;\ }\ } #define ROUND_TEMPLATE(template_name, sh)\ static void template_name(unsigned short *my_restrict in, struct bitmap *out)\ {\ const int shift = sh;\ unsigned short ir, ig, ib;\ int rt,gt,bt,o,x,y;\ unsigned char *my_restrict outp=out->data;\ ssize_t skip = out->skip - SKIP_CODE;\ \ o=0;o=o; /*warning go away */\ for (y=out->y;y;y--){\ for (x=out->x;x;x--){\ ir = in[0];\ ig = in[1];\ ib = in[2];\ rt=red_table[limit_16(ir) >> shift];\ gt=green_table[limit_16(ig) >> shift];\ bt=blue_table[limit_16(ib) >> shift];\ in+=3;\ SAVE_CODE\ }\ outp+=skip;\ }\ } /* Expression determining line length in bytes */ #define SKIP_CODE out->x /* Code with input in rt, gt, bt (values from red_table, green_table, blue_table) * that saves appropriate code on *outp (unsigned char *outp). We can use int o; * as a scratchpad. */ #define SAVE_CODE \ o = (rt >> 16) + (gt >> 16) + (bt >> 16); \ *outp++ = (unsigned char)o; DITHER_TEMPLATE(dither_1byte, 0) ROUND_TEMPLATE(round_1byte, 0) DITHER_TEMPLATE(dither_1byte_8, 8) ROUND_TEMPLATE(round_1byte_8, 8) #undef SKIP_CODE #undef SAVE_CODE #define SKIP_CODE out->x #define SAVE_CODE \ { \ int rr, gr, br, or; \ o = (rt >> 16) + (gt >> 16) + (bt >> 16); \ rr = red_table[limit_16(ir) >> shift]; \ gr = green_table[limit_16(ig) >> shift]; \ br = blue_table[limit_16(ib) >> shift]; \ or = (rr >> 16) + (gr >> 16) + (br >> 16); \ if (!((real_colors_table[or * 3 + 0] - ir) | \ (real_colors_table[or * 3 + 1] - ig) | \ (real_colors_table[or * 3 + 2] - ib))) { \ o = or; \ } \ rt = real_colors_table[o * 3 + 0]; \ gt = real_colors_table[o * 3 + 1]; \ bt = real_colors_table[o * 3 + 2]; \ *outp++ = (unsigned char)o; \ } \ DITHER_TEMPLATE(dither_1byte_real_colors, 0) DITHER_TEMPLATE(dither_1byte_real_colors_8, 8) #undef SKIP_CODE #undef SAVE_CODE #define SKIP_CODE out->x*2 #if defined(t2c) && defined(C_LITTLE_ENDIAN) #define SAVE_CODE \ o=rt|gt|bt;\ *(t2c *)outp=(o>>16);\ outp+=2; #else #define SAVE_CODE \ o=rt|gt|bt;\ o>>=16;\ outp[0]=o;\ outp[1]=o>>8;\ outp+=2; #endif /* #ifdef t2c */ DITHER_TEMPLATE(dither_2byte, 0) ROUND_TEMPLATE(round_2byte, 0) DITHER_TEMPLATE(dither_2byte_8, 8) ROUND_TEMPLATE(round_2byte_8, 8) #undef SAVE_CODE #undef SKIP_CODE /* B G R */ #define SKIP_CODE out->x*3 #define SAVE_CODE \ outp[0]=bt>>16;\ outp[1]=gt>>16;\ outp[2]=rt>>16;\ outp+=3; DITHER_TEMPLATE(dither_195, 0) ROUND_TEMPLATE(round_195, 0) DITHER_TEMPLATE(dither_195_8, 8) ROUND_TEMPLATE(round_195_8, 8) #undef SAVE_CODE #undef SKIP_CODE /* R G B */ #define SKIP_CODE out->x*3 #define SAVE_CODE \ outp[0]=rt>>16;\ outp[1]=gt>>16;\ outp[2]=bt>>16;\ outp+=3; DITHER_TEMPLATE(dither_451, 0) ROUND_TEMPLATE(round_451, 0) DITHER_TEMPLATE(dither_451_8, 8) ROUND_TEMPLATE(round_451_8, 8) #undef SAVE_CODE #undef SKIP_CODE /* B G R 0 */ #define SKIP_CODE out->x*4 #define SAVE_CODE \ outp[0]=bt>>16;\ outp[1]=gt>>16;\ outp[2]=rt>>16;\ outp[3]=0;\ outp+=4; DITHER_TEMPLATE(dither_196, 0) ROUND_TEMPLATE(round_196, 0) DITHER_TEMPLATE(dither_196_8, 8) ROUND_TEMPLATE(round_196_8, 8) #undef SAVE_CODE #undef SKIP_CODE /* 0 B G R */ #define SKIP_CODE out->x*4 #define SAVE_CODE \ outp[0]=0;\ outp[1]=bt>>16;\ outp[2]=gt>>16;\ outp[3]=rt>>16;\ outp+=4; DITHER_TEMPLATE(dither_452, 0) ROUND_TEMPLATE(round_452, 0) DITHER_TEMPLATE(dither_452_8, 8) ROUND_TEMPLATE(round_452_8, 8) #undef SAVE_CODE #undef SKIP_CODE /* 0 R G B */ #define SKIP_CODE out->x*4 #define SAVE_CODE \ outp[0]=0;\ outp[1]=rt>>16;\ outp[2]=gt>>16;\ outp[3]=bt>>16;\ outp+=4; DITHER_TEMPLATE(dither_708, 0) ROUND_TEMPLATE(round_708, 0) DITHER_TEMPLATE(dither_708_8, 8) ROUND_TEMPLATE(round_708_8, 8) #undef SAVE_CODE #undef SKIP_CODE /* For 256-color cube */ static long color_332(int rgb) { int r,g,b; long ret = 0; r=(rgb>>16)&255; g=(rgb>>8)&255; b=rgb&255; r=(r*7+127)/255; g=(g*7+127)/255; b=(b*3+127)/255; *(unsigned char *)&ret=(r<<5)|(g<<2)|b; return ret; } /* For 216-color cube */ static long color_666(int rgb) { int r, g, b; unsigned char i; long ret = 0; r = (rgb >> 16) & 255; g = (rgb >> 8) & 255; b = rgb & 255; r = (r * 5 + 127) / 255; g = (g * 5 + 127) / 255; b = (b * 5 + 127) / 255; i = (unsigned char)r; i *= 6; i += g; i *= 6; i += b; *(unsigned char *)&ret = i; return ret; } static long color_121(int rgb) { int r, g, b; long ret = 0; r = (rgb >> 16) & 255; g = (rgb >> 8) & 255; b = rgb & 255; r = (r + 127) / 255; g = (3 * g + 127) / 255; b = (b + 127) / 255; *(unsigned char *)&ret = (r << 3) | (g << 1) | b; return ret; } static long color_111(int rgb) { int r, g, b; long ret = 0; r = (rgb >> 16) & 255; g = (rgb >> 8) & 255; b = rgb & 255; r = (r + 127) / 255; g = (g + 127) / 255; b = (b + 127) / 255; *(unsigned char *)&ret = (r << 2) | (g << 1) | b; return ret; } static long color_888_rgb(int rgb) { long ret = 0; ((unsigned char *)&ret)[0]=rgb>>16; ((unsigned char *)&ret)[1]=rgb>>8; ((unsigned char *)&ret)[2]=(unsigned char)rgb; return ret; } static long color_888_bgr(int rgb) { long ret = 0; ((unsigned char *)&ret)[0]=(unsigned char)rgb; ((unsigned char *)&ret)[1]=rgb>>8; ((unsigned char *)&ret)[2]=rgb>>16; return ret; } static long color_8888_bgr0(int rgb) { long ret = 0; ((unsigned char *)&ret)[0]=(unsigned char)rgb; ((unsigned char *)&ret)[1]=rgb>>8; ((unsigned char *)&ret)[2]=rgb>>16; ((unsigned char *)&ret)[3]=0; return ret; } /* Long live the sigma-delta modulator! */ static long color_8888_0bgr(int rgb) { long ret = 0; /* Atmospheric lightwave communication rulez */ ((unsigned char *)&ret)[0]=0; ((unsigned char *)&ret)[1]=(unsigned char)rgb; ((unsigned char *)&ret)[2]=rgb>>8; ((unsigned char *)&ret)[3]=rgb>>16; return ret; } /* Long live His Holiness The 14. Dalai Lama Taendzin Gjamccho! */ /* The above line will probably cause a ban of this browser in China under * the capital punishment ;-) */ static long color_8888_0rgb(int rgb) { long ret = 0; /* Chokpori Dharamsala Lhasa Laddakh */ ((unsigned char *)&ret)[0]=0; ((unsigned char *)&ret)[1]=rgb>>16; ((unsigned char *)&ret)[2]=rgb>>8; ((unsigned char *)&ret)[3]=(unsigned char)rgb; return ret; } /* We assume long holds at least 32 bits */ static long color_555be(int rgb) { int r=(rgb>>16)&255; int g=(rgb>>8)&255; int b=(rgb)&255; int i; long ret = 0; r=(r*31+127)/255; g=(g*31+127)/255; b=(b*31+127)/255; i=(r<<10)|(g<<5)|b; ((unsigned char *)&ret)[0]=i>>8; ((unsigned char *)&ret)[1]=(unsigned char)i; return ret; } /* We assume long holds at least 32 bits */ static long color_555(int rgb) { int r=(rgb>>16)&255; int g=(rgb>>8)&255; int b=(rgb)&255; int i; long ret = 0; r=(r*31+127)/255; g=(g*31+127)/255; b=(b*31+127)/255; i=(r<<10)|(g<<5)|b; ((unsigned char *)&ret)[0]=(unsigned char)i; ((unsigned char *)&ret)[1]=i>>8; return ret; } static long color_565be(int rgb) { int r,g,b; long ret = 0; int i; r=(rgb>>16)&255; g=(rgb>>8)&255; /* Long live the PIN photodiode */ b=rgb&255; r=(r*31+127)/255; g=(g*63+127)/255; b=(b*31+127)/255; i = (r<<11)|(g<<5)|b; ((unsigned char *)&ret)[0]=i>>8; ((unsigned char *)&ret)[1]=(unsigned char)i; return ret; } static long color_565(int rgb) { int r,g,b; long ret = 0; int i; r=(rgb>>16)&255; g=(rgb>>8)&255; /* Long live the PIN photodiode */ b=rgb&255; r=(r*31+127)/255; g=(g*63+127)/255; b=(b*31+127)/255; i=(r<<11)|(g<<5)|b; ((unsigned char *)&ret)[0]=(unsigned char)i; ((unsigned char *)&ret)[1]=i>>8; return ret; } static long color_888_bgr_15bit(int rgb) { int r,g,b; long ret = 0; r=(rgb>>16)&255; g=(rgb>>8)&255; /* Long live the PIN photodiode */ b=rgb&255; r=(r*31+127)/255; g=(g*31+127)/255; b=(b*31+127)/255; ((unsigned char *)&ret)[0]=(unsigned char)(r<<3); ((unsigned char *)&ret)[1]=(unsigned char)(g<<3); ((unsigned char *)&ret)[2]=(unsigned char)(b<<3); return ret; } static long color_888_bgr_16bit(int rgb) { int r,g,b; long ret = 0; r=(rgb>>16)&255; g=(rgb>>8)&255; /* Long live the PIN photodiode */ b=rgb&255; r=(r*31+127)/255; g=(g*63+127)/255; b=(b*31+127)/255; ((unsigned char *)&ret)[0]=(unsigned char)(r<<3); ((unsigned char *)&ret)[1]=(unsigned char)(g<<2); ((unsigned char *)&ret)[2]=(unsigned char)(b<<3); return ret; } /* rgb = r*65536+g*256+b */ /* The selected color_fn returns a long. * When we have for example 2 bytes per pixel, we make them in the memory, * then copy them to the beginning of the memory occupied by the long * variable, and return that long variable. */ long (*get_color_fn(int depth))(int rgb) { switch (depth) { case 33: return color_121; break; case 801: return color_111; break; case 65: return color_332; break; case 833: return color_666; case 122: return color_555; break; case 378: return color_555be; break; case 130: return color_565; break; case 386: return color_565be; break; case 451: return color_888_rgb; break; case 195: return color_888_bgr; break; case 452: return color_8888_0bgr; break; case 196: return color_8888_bgr0; break; case 708: return color_8888_0rgb; break; case 15555: return color_888_bgr_15bit; break; case 16579: return color_888_bgr_16bit; break; default: return NULL; break; } } /* Gamma says that light=electricity raised to gamma */ static void make_16_table(int *table, int values, int mult, float_double gamma, int bigendian) { int grades = values - 1; int j, light_val, grade; float_double voltage; float_double rev_gamma = 1 / gamma; const float_double inv_65535 = (float_double)(1 / 65535.); int last_grade, last_content; unsigned val; uttime start_time = get_time(); int sample_state = 0; int x_slow_fpu = slow_fpu; if (gamma_bits != 2) x_slow_fpu = !gamma_bits; repeat_loop: last_grade = -1; last_content = 0; for (j=0;j<65536;j++){ if (x_slow_fpu) { if (x_slow_fpu == 1) { if (j & 255) { table[j] = last_content; continue; } } else { if (!(j & (j - 1))) { uttime now = get_time(); if (!sample_state) { if (now != start_time) start_time = now, sample_state = 1; } else { if (now - start_time > SLOW_FPU_DETECT_THRESHOLD && (now - start_time) * 65536 / j > SLOW_FPU_MAX_STARTUP / 3) { x_slow_fpu = 1; goto repeat_loop; } } } } } voltage=fd_pow(j * inv_65535, rev_gamma); /* Determine which monitor input voltage is equivalent * to said photon flux level */ grade=(int)(voltage * grades + (float_double)0.5); if (grade == last_grade){ table[j] = last_content; continue; } last_grade = grade; voltage = (float_double)grade / grades; /* Find nearest voltage to this voltage. Finding nearest voltage, not * nearest photon flux ensures the dithered pixels will be perceived to be * near. The voltage input into the monitor was intentionally chosen by * generations of television engineers to roughly comply with eye's * response, thus minimizing and unifying noise impact on transmitted * signal. This is only marginal enhancement however it sounds * kool ;-) (and is kool) */ light_val = (int)(fd_pow(voltage, gamma) * 65535 + (float_double)0.5); /* Find out what photon flux this index represents */ if (light_val < 0) light_val = 0; if (light_val > 65535) light_val = 65535; /* Clip photon flux for safety */ val = grade * mult; if (bigendian) val = (val >> 8) | ((val & 0xff) << 8); last_content = light_val | (val << 16); table[j] = last_content; /* Save index and photon flux. */ } if (x_slow_fpu == -1) slow_fpu = 0; /* if loop passed once without detecting slow fpu, always assume fast FPU */ if (gamma_bits == 2 && x_slow_fpu == 1) slow_fpu = 1; } static void make_red_table(int values, int mult, int be) { red_table = mem_realloc(red_table, 65536 * sizeof(*red_table)); make_16_table(red_table, values, mult, (float_double)(display_red_gamma tcc_hack), be); } static void make_green_table(int values, int mult, int be) { green_table = mem_realloc(green_table, 65536 * sizeof(*green_table)); make_16_table(green_table, values, mult, (float_double)(display_green_gamma tcc_hack), be); } static void make_blue_table(int values, int mult, int be) { blue_table = mem_realloc(blue_table, 65536 * sizeof(*blue_table)); make_16_table(blue_table, values, mult, (float_double)(display_blue_gamma tcc_hack), be); } void dither(unsigned short *in, struct bitmap *out) { int *dregs; if ((unsigned)out->x > MAXINT / 3 / sizeof(*dregs)) overalloc(); dregs=mem_calloc(out->x*3*sizeof(*dregs)); (*dither_fn_internal)(in, out, dregs); mem_free(dregs); } /* For functions that do dithering. * Returns allocated dregs. */ int *dither_start(unsigned short *in, struct bitmap *out) { int *dregs; if ((unsigned)out->x > MAXINT / 3 / sizeof(*dregs)) overalloc(); dregs=mem_calloc(out->x*3*sizeof(*dregs)); (*dither_fn_internal)(in, out, dregs); return dregs; } void dither_restart(unsigned short *in, struct bitmap *out, int *dregs) { (*dither_fn_internal)(in, out, dregs); } static void make_round_tables(void) { int a; /* ICC bug */ icc_volatile unsigned short v; for (a = 0; a < 256; a++) { /* a is sRGB coordinate */ v=ags_8_to_16((unsigned char)a, (float)(user_gamma / sRGB_gamma)); round_red_table[a] = red_table[v >> (8 - 8 * table_16)] & 0xffff; round_green_table[a] = green_table[v >> (8 - 8 * table_16)] & 0xffff; round_blue_table[a] = blue_table[v >> (8 - 8 * table_16)] & 0xffff; } } static void compress_tables(void) { int i; int *rt, *gt, *bt; /*for (i = 0; i < 65536; i++) { fprintf(stderr, "16: %03d: %08x %08x %08x\n", i, red_table[i], green_table[i], blue_table[i]); }*/ for (i = 0; i < 65536; i++) { if (red_table[i] != red_table[i & 0xff00] || green_table[i] != green_table[i & 0xff00] || blue_table[i] != blue_table[i & 0xff00]) return; } table_16 = 0; rt = mem_alloc(256 * sizeof(*rt)); gt = mem_alloc(256 * sizeof(*gt)); bt = mem_alloc(256 * sizeof(*bt)); for (i = 0; i < 256; i++) { rt[i] = red_table[i << 8]; gt[i] = green_table[i << 8]; bt[i] = blue_table[i << 8]; /*fprintf(stderr, "8: %03d: %08x %08x %08x\n", i, rt[i], gt[i], bt[i]);*/ } mem_free(red_table); mem_free(green_table); mem_free(blue_table); red_table = rt; green_table = gt; blue_table = bt; } static void make_real_colors_table(void) { unsigned short *real_colors; if (real_colors_table) mem_free(real_colors_table), real_colors_table = NULL; if (round_fn != round_1byte && round_fn != round_1byte_8) return; if (!drv->get_real_colors) return; real_colors = drv->get_real_colors(); if (!real_colors) return; real_colors_table = mem_alloc(256 * 3 * sizeof(unsigned short)); agx_48_to_48(real_colors_table, real_colors, 256, (float)(display_red_gamma tcc_hack), (float)(display_green_gamma tcc_hack), (float)(display_blue_gamma tcc_hack)); mem_free(real_colors); } /* Also makes up the dithering tables. * You may call it twice - it doesn't leak any memory. */ void init_dither(int depth) { table_16 = 1; switch (depth) { case 33: /* 4bpp, 1Bpp */ make_red_table(1 << 1, 1 << 3, 0); make_green_table(1 << 2, 1 << 1, 0); make_blue_table(1 << 1, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_1byte : dither_1byte_8; round_fn = table_16 ? round_1byte : round_1byte_8; break; case 801: /* 8bpp, 1Bpp, 1x1x1 */ make_red_table(1 << 1, 1 << 2, 0); make_green_table(1 << 1, 1 << 1, 0); make_blue_table(1 << 1, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_1byte : dither_1byte_8; round_fn = table_16 ? round_1byte : round_1byte_8; break; case 65: /* 8 bpp, 1Bpp */ make_red_table(1 << 3, 1 << 5, 0); make_green_table(1 << 3, 1 << 2, 0); make_blue_table(1 << 2, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_1byte : dither_1byte_8; round_fn = table_16 ? round_1byte : round_1byte_8; break; case 833: /* 8bpp, 1Bpp, 6x6x6 */ make_red_table(6, 36, 0); make_green_table(6, 6, 0); make_blue_table(6, 1, 0); compress_tables(); dither_fn_internal = table_16 ? dither_1byte : dither_1byte_8; round_fn = table_16 ? round_1byte : round_1byte_8; break; case 122: /* 15bpp, 2Bpp */ make_red_table(1 << 5, 1 << 10, 0); make_green_table(1 << 5, 1 << 5, 0); make_blue_table(1 << 5, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_2byte : dither_2byte_8; round_fn = table_16 ? round_2byte : round_2byte_8; break; case 378: /* 15bpp, 2Bpp, disordered (1 << I have a mental disorder) */ make_red_table(1 << 5, 1 << 10, 1); make_green_table(1 << 5, 1 << 5, 1); make_blue_table(1 << 5, 1 << 0, 1); compress_tables(); dither_fn_internal = table_16 ? dither_2byte : dither_2byte_8; round_fn = table_16 ? round_2byte : round_2byte_8; break; case 130: /* 16bpp, 2Bpp */ make_red_table(1 << 5, 1 << 11, 0); make_green_table(1 << 6, 1 << 5, 0); make_blue_table(1 << 5, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_2byte : dither_2byte_8; round_fn = table_16 ? round_2byte : round_2byte_8; break; case 386: /* 16bpp, 2Bpp, disordered */ make_red_table(1 << 5, 1 << 11, 1); make_green_table(1 << 6, 1 << 5, 1); make_blue_table(1 << 5, 1 << 0, 1); compress_tables(); dither_fn_internal = table_16 ? dither_2byte : dither_2byte_8; round_fn = table_16 ? round_2byte : round_2byte_8; break; case 451: /* 24bpp, 3Bpp, misordered * Even this is dithered! * R G B */ make_red_table(1 << 8, 1 << 0, 0); make_green_table(1 << 8, 1 << 0, 0); make_blue_table(1 << 8, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_451 : dither_451_8; round_fn = table_16 ? round_451 : round_451_8; break; case 195: /* 24bpp, 3Bpp * Even this is dithered! * B G R */ make_red_table(1 << 8, 1 << 0, 0); make_green_table(1 << 8, 1 << 0, 0); make_blue_table(1 << 8, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_195 : dither_195_8; round_fn = table_16 ? round_195 : round_195_8; break; case 452: /* 24bpp, 4Bpp, misordered * Even this is dithered! * 0 B G R */ make_red_table(1 << 8, 1 << 0, 0); make_green_table(1 << 8, 1 << 0, 0); make_blue_table(1 << 8, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_452 : dither_452_8; round_fn = table_16 ? round_452 : round_452_8; break; case 196: /* 24bpp, 4Bpp * Even this is dithered! * B G R 0 */ make_red_table(1 << 8, 1 << 0, 0); make_green_table(1 << 8, 1 << 0, 0); make_blue_table(1 << 8, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_196 : dither_196_8; round_fn = table_16 ? round_196 : round_196_8; break; case 708: /* 24bpp, 4Bpp * Even this is dithered! * 0 R G B */ make_red_table(1 << 8, 1 << 0, 0); make_green_table(1 << 8, 1 << 0, 0); make_blue_table(1 << 8, 1 << 0, 0); compress_tables(); dither_fn_internal = table_16 ? dither_708 : dither_708_8; round_fn = table_16 ? round_708 : round_708_8; break; case 15555: /* 24bpp, 3Bpp, downsampled to 15bit * B G R */ make_red_table(1 << 5, 1 << 3, 0); make_green_table(1 << 5, 1 << 3, 0); make_blue_table(1 << 5, 1 << 3, 0); compress_tables(); dither_fn_internal = table_16 ? dither_195 : dither_195_8; round_fn = table_16 ? round_195 : round_195_8; break; case 16579: /* 24bpp, 3Bpp, downsampled to 16bit * B G R */ make_red_table(1 << 5, 1 << 3, 0); make_green_table(1 << 6, 1 << 2, 0); make_blue_table(1 << 5, 1 << 3, 0); compress_tables(); dither_fn_internal = table_16 ? dither_195 : dither_195_8; round_fn = table_16 ? round_195 : round_195_8; break; default: internal_error("Graphics driver returned unsupported pixel memory organisation %d",depth); } make_round_tables(); make_real_colors_table(); if (real_colors_table) { if (dither_fn_internal == dither_1byte) dither_fn_internal = dither_1byte_real_colors; if (dither_fn_internal == dither_1byte_8) dither_fn_internal = dither_1byte_real_colors_8; } gamma_cache_rgb_1 = -2; gamma_cache_rgb_2 = -2; gamma_stamp++; } /* Input is in sRGB space (unrounded, i. e. directly from HTML) * Output is linear 48-bit value (in photons) that has corresponding * voltage nearest to the voltage that would be procduced ideally * by the input value. */ void round_color_sRGB_to_48(unsigned short *my_restrict red, unsigned short *my_restrict green, unsigned short *my_restrict blue, int rgb) { *red = round_red_table[(rgb >> 16) & 255]; *green = round_green_table[(rgb >> 8) & 255]; *blue = round_blue_table[rgb & 255]; if (real_colors_table) { int shift, rt, gt, bt, o; shift = 8 - table_16 * 8; rt = red_table[*red >> shift]; gt = green_table[*green >> shift]; bt = blue_table[*blue >> shift]; o = (rt >> 16) + (gt >> 16) + (bt >> 16); *red = real_colors_table[o * 3 + 0]; *green = real_colors_table[o * 3 + 1]; *blue = real_colors_table[o * 3 + 2]; } } void free_dither(void) { if (red_table) mem_free(red_table), red_table = DUMMY; if (green_table) mem_free(green_table), green_table = DUMMY; if (blue_table) mem_free(blue_table), blue_table = DUMMY; if (real_colors_table) mem_free(real_colors_table), real_colors_table = NULL; } #endif