1148 lines
27 KiB
C
1148 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
|