links/dither.c

1149 lines
27 KiB
C

/* 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 <math.h>
#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