fteqw/engine/sw/d_trans.c
2006-03-11 00:35:56 +00:00

773 lines
18 KiB
C

//contains routines for blending images (as well as blitting 32bit to 8bit type stuff)
#include "quakedef.h"
#include "d_local.h"
#include "r_local.h"
void MakeVideoPalette(void);
void MakeSwizzledPalette(void);
void MakePaletteRemaps(void);
int *srctable;
int *dsttable;
qbyte *pal555to8;
int swzpal[TRANS_LEVELS][256];
// menutint
palremap_t *RebuildMenuTint(void);
palremap_t *mtpalremap;
// IB remap
palremap_t *ib_remap;
extern cvar_t r_menutint;
int mtmodified;
#define palette host_basepal
#define _abs(x) ((x)*(x))
void D_ShutdownTrans(void)
{
if (pal555to8)
{
BZ_Free(pal555to8);
pal555to8 = NULL;
}
if (palremaps)
{
BZ_Free(palremaps);
palremapsize = 0;
palremaps = NULL;
}
mtpalremap = NULL;
mtmodified = 0;
ib_remap = NULL;
}
void D_InitTrans(void)
{
// create pal555to8 and swizzled palette
MakeVideoPalette();
MakeSwizzledPalette();
MakePaletteRemaps();
srctable = swzpal[0];
dsttable = swzpal[TRANS_MAX];
mtpalremap = RebuildMenuTint();
ib_remap = D_IdentityRemap();
}
// TODO: INLINE THESE FUNCTIONS
qbyte FASTCALL Trans(qbyte p, qbyte p2)
{
int x;
x = (srctable[p] + dsttable[p2]) | 0x01F07C1F;
return pal555to8[x & (x >> 15)];
}
qbyte FASTCALL AddBlend(qbyte p, qbyte p2)
{
int x, y;
x = (srctable[p] + dsttable[p2]);
y = x & 0x40100400; // overflow bits
x = (x | 0x01F07C1F) & 0x3FFFFFFF;
y = y - (y >> 5);
x = x | y;
return pal555to8[x & (x >> 15)];
}
/*
void Set_TransLevelI(int level)
{
t_curtable = level/(100.0f/(t_numtables-1));
t_curlookupp = t_lookup[t_curtable];
}
*/
void D_SetTransLevel(float level, blendmode_t blend)
{
int ilvl;
// cap and set level
ilvl = (bound(0, level, 1) * (TRANS_MAX + 0.99));
// set blending tables
switch (blend)
{
case BM_ADD:
dsttable = swzpal[ilvl];
srctable = swzpal[TRANS_MAX];
break;
default:
dsttable = swzpal[ilvl];
srctable = swzpal[TRANS_MAX - ilvl];
}
}
#define _abs(x) ((x)*(x))
qbyte FindIndexFromRGB(int red, int green, int blue)
{
int i, best=15;
int bestdif=256*256*256, curdif;
extern qbyte *host_basepal;
qbyte *pa;
pa = host_basepal;
for (i = 0; i < 256; i++, pa+=3)
{
curdif = _abs(red - pa[0]) + _abs(green - pa[1]) + _abs(blue - pa[2]);
if (curdif < bestdif)
{
if (curdif<1)
return i;
bestdif = curdif;
best = i;
}
}
return best;
}
qbyte FindIndexFromRGBNoFB(int red, int green, int blue)
{
int i, best=15;
int bestdif=256*256*256, curdif;
extern qbyte *host_basepal;
qbyte *pa;
pa = host_basepal;
for (i = 0; i < 256 - vid.fullbright; i++, pa+=3)
{
curdif = _abs(red - pa[0]) + _abs(green - pa[1]) + _abs(blue - pa[2]);
if (curdif < bestdif)
{
if (curdif<1)
return i;
bestdif = curdif;
best = i;
}
}
return best;
}
#define FindPalette(r,g,b) pal555to8[((r&0xF8)>>3)|((g&0xF8)<<2)|((b&0xF8)<<7)]
qbyte GetPalette(int red, int green, int blue)
{
if (pal555to8) //fast precalculated method
return FindPalette(red,green,blue);
else //slow, horrible method.
return FindIndexFromRGB(red, green, blue);
}
qbyte GetPaletteNoFB(int red, int green, int blue)
{
if (pal555to8) //fast precalculated (but ugly) method
return fbremapidx(FindPalette(red,green,blue));
else //slow, horrible (but accurate) method.
return FindIndexFromRGBNoFB(red, green, blue);
}
void MakeVideoPalette(void)
{
vfsfile_t *f;
int r, g, b;
// allocate memory
if (!pal555to8)
pal555to8 = BZ_Malloc(PAL555_SIZE);
// pal555to8 = Hunk_AllocName(PAL555_SIZE, "RGB data");
// load in previously created table
if ((f = FS_OpenVFS("pal555.pal", "rb", FS_BASE)))
{
VFS_READ(f, pal555to8, PAL555_SIZE);
VFS_CLOSE(f);
return;
}
// create palette conversion table
for (b = 0; b < 32; b++)
for (g = 0; g < 32; g++)
for (r = 0; r < 32; r++)
pal555to8[r | (g << 5) | (b << 10)] =
FindIndexFromRGB(r<<3|r>>2, g<<3|g>>2, b<<3|b>>2);
// write palette conversion table
if (d_palconvwrite.value)
COM_WriteFile("pal555.pal", pal555to8, PAL555_SIZE);
}
void MakeSwizzledPalette(void)
{
int idx, lvl;
qbyte *pa;
// create swizzled palettes
for (lvl = 0; lvl < TRANS_LEVELS; lvl++)
{
pa = host_basepal;
for (idx = 0; idx < 256; idx++)
{
// create a b10r10g10 table for each alpha level
// may need some hacking due to the tendancy of
// identity merges becoming darker
swzpal[lvl][idx] = ( (pa[0] * lvl) >> 4 ) << 10; // red
swzpal[lvl][idx] |= ( (pa[1] * lvl) >> 4 ); // green
swzpal[lvl][idx] |= ( (pa[2] * lvl) >> 4 ) << 20; // blue
swzpal[lvl][idx] = swzpal[lvl][idx] & 0x3feffbff;
pa += 3;
}
}
}
// colormap functions
// REMAPKEY macro defines the palette remap key
// d = desaturate (0/1), f = fullbrights (0/1), t = top color, b = bottom color
#define REMAPKEY(d, f, t, b) (0x1 ^ (d<<1) ^ (f<<2) ^ (t<<3) ^ (b<<7))
#define DEREFDEFAULT -2147483647 // lowest negative 32-bit number (without MSVC being stupid)
void MakePaletteRemaps(void)
{
int i;
palremapsize = d_palremapsize.value;
if (palremapsize < 4)
{
Con_Printf("Invalid size for d_palremapsize, defaulting to 4.\n");
palremapsize = 4;
}
palremaps = BZ_Malloc(sizeof(palremap_t)*palremapsize);
// build identity remap
palremaps[0].r = palremaps[0].g = palremaps[0].b = 255;
palremaps[0].key = REMAPKEY(0, 1, TOP_DEFAULT, BOTTOM_DEFAULT);
palremaps[0].references = 999;
for (i = 0; i < 256; i++)
palremaps[0].pal[i] = i;
// build fullbright remap
palremaps[1].r = palremaps[1].g = palremaps[1].b = 255;
palremaps[1].key = REMAPKEY(0, 0, TOP_DEFAULT, BOTTOM_DEFAULT);
palremaps[1].references = 999;
for (i = 0; i < 256 - vid.fullbright; i++)
palremaps[1].pal[i] = i;
for (i = 256 - vid.fullbright; i < 256; i++)
palremaps[1].pal[i] = FindIndexFromRGBNoFB(host_basepal[i*3], host_basepal[i*3+1], host_basepal[i*3+2]);
for (i = 2; i < palremapsize; i++)
{
palremaps[i].key = 0;
palremaps[i].references = DEREFDEFAULT;
}
}
void BuildModulatedPalette(qbyte *indexes, int red, int green, int blue, qboolean desaturate, qboolean fullbrights, int topcolor, int bottomcolor)
{
qbyte *rgb = host_basepal;
unsigned int r, g, b, x, invmask = 0;
if (red < 0 || green < 0 || blue < 0)
invmask = 0xff;
// generate palette remap
if (desaturate)
{
int s;
for (x = 0; x < 256; x++)
{
s = rgb[0]*76 + rgb[1]*151 + rgb[2]*29 + 128;
r = abs((127*256 + s*red) >> 16);
g = abs((127*256 + s*green) >> 16);
b = abs((127*256 + s*blue) >> 16);
if (r > 255)
r = 255;
if (g > 255)
g = 255;
if (b > 255)
b = 255;
if (fullbrights) // relying on branch prediction here...
indexes[x] = GetPalette(r^invmask, g^invmask, b^invmask);
else
indexes[x] = GetPaletteNoFB(r^invmask, g^invmask, b^invmask);
rgb += 3;
}
}
else if (red == 255 && green == 255 && blue == 255)
{
// identity merge
if (fullbrights)
memcpy(indexes, identityremap.pal, sizeof(identityremap.pal));
else
memcpy(indexes, fullbrightremap.pal, sizeof(fullbrightremap.pal));
}
else
{
for (x = 0; x < 256; x++)
{
// modulus math
r = abs((127 + rgb[0]*red) >> 8);
g = abs((127 + rgb[1]*green) >> 8);
b = abs((127 + rgb[2]*blue) >> 8);
if (r > 255)
r = 255;
if (g > 255)
g = 255;
if (b > 255)
b = 255;
if (fullbrights) // relying on branch prediction here...
indexes[x] = GetPalette(r^invmask, g^invmask, b^invmask);
else
indexes[x] = GetPaletteNoFB(r^invmask, g^invmask, b^invmask);
rgb += 3;
}
}
// handle top/bottom remap
if (topcolor == TOP_DEFAULT && bottomcolor == BOTTOM_DEFAULT)
return;
{
qbyte topcolors[16];
qbyte bottomcolors[16];
topcolor = topcolor * 16;
bottomcolor = bottomcolor * 16;
for (x = 0; x < 16; x++)
{
if (topcolor < 128)
topcolors[x] = indexes[topcolor + x];
else
topcolors[x] = indexes[topcolor + 15 - x];
if (bottomcolor < 128)
bottomcolors[x] = indexes[bottomcolor + x];
else
bottomcolors[x] = indexes[bottomcolor + 15 - x];
}
for (x = 0; x < 16; x++)
{
indexes[TOP_RANGE + x] = topcolors[x];
indexes[BOTTOM_RANGE + x] = bottomcolors[x];
}
}
}
palremap_t *D_GetPaletteRemap(int red, int green, int blue, qboolean desaturate, qboolean fullbrights, int topcolor, int bottomcolor)
{
int i, key, deref = -1, dereflast = 1;
topcolor = topcolor & 0xf;
bottomcolor = bottomcolor & 0xf;
key = REMAPKEY(desaturate, fullbrights, topcolor, bottomcolor);
for (i = 0; i < palremapsize; i++)
{
if (palremaps[i].r == red &&
palremaps[i].g == green &&
palremaps[i].b == blue &&
palremaps[i].key == key)
{
if (palremaps[i].references < 1)
palremaps[i].references = 1;
else
palremaps[i].references++;
return palremaps + i;
}
else if (palremaps[i].references < dereflast)
{
deref = i;
dereflast = palremaps[i].references;
}
}
if (deref < 2) // no remaps found and all maps are referenced
return palremaps; // identity remap
// return non-referenced map
BuildModulatedPalette(palremaps[deref].pal, red, green, blue, desaturate, fullbrights, topcolor, bottomcolor);
if (palremaps[deref].references < 1)
palremaps[deref].references = 1;
else
palremaps[deref].references++;
palremaps[deref].r = red;
palremaps[deref].g = green;
palremaps[deref].b = blue;
palremaps[deref].key = key;
return palremaps + deref;
}
palremap_t *RebuildMenuTint(void)
{
char *t;
vec3_t rgb;
if (r_menutint.string[0])
SCR_StringToRGB(r_menutint.string, rgb, 1);
else
return NULL;
return D_GetPaletteRemap(rgb[0]*255, rgb[1]*255, rgb[2]*255, true, true, TOP_DEFAULT, BOTTOM_DEFAULT);
}
void D_DereferenceRemap(palremap_t *palremap)
{
static int dereftime;
if (palremap && palremap >= palremaps+2)
{
if (palremap->references < 2)
{
if (dereftime >= 0)
dereftime = DEREFDEFAULT;
palremap->references = dereftime;
dereftime++;
}
else
palremap->references--;
}
}
qbyte *D_GetMenuTintPal(void)
{
if (mtmodified != r_menutint.modified)
{
if (mtpalremap)
D_DereferenceRemap(mtpalremap);
mtpalremap = RebuildMenuTint();
mtmodified = r_menutint.modified;
}
if (mtpalremap && mtpalremap != palremaps)
return mtpalremap->pal;
else
return NULL;
}
struct palremap_s *D_IdentityRemap(void) // TODO: explicitly inline this
{
return palremaps;
}
#undef REMAPKEY
#undef DEREFDEFAULT
void MediaSW_ShowFrame8bit(qbyte *framedata, int inwidth, int inheight, qbyte *palette)
{
int y, x;
D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly
if (r_pixbytes == 1)
{
qbyte *dest, *src;
int lines=vid.conheight;
int v;
int f, fstep;
dest = vid.conbuffer;
for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
{
v = (vid.conheight - lines + y)*inheight/vid.conheight;
src = framedata + v*inwidth;
{
f = 0;
fstep = (inwidth<<16)/vid.conwidth;
for (x=0 ; x<vid.conwidth ; x+=4)
{
dest[x] = FindPalette(palette[src[(f>>16)]*3], palette[src[(f>>16)]*3+1], palette[src[(f>>16)]*3+2]);
f += fstep;
dest[x+1] = FindPalette(palette[src[(f>>16)]*3], palette[src[(f>>16)]*3+1], palette[src[(f>>16)]*3+2]);
f += fstep;
dest[x+2] = FindPalette(palette[src[(f>>16)]*3], palette[src[(f>>16)]*3+1], palette[src[(f>>16)]*3+2]);
f += fstep;
dest[x+3] = FindPalette(palette[src[(f>>16)]*3], palette[src[(f>>16)]*3+1], palette[src[(f>>16)]*3+2]);
f += fstep;
}
}
}
}
else if (r_pixbytes == 2)
{
/* this still expects 32bit input
extern int redbits, redshift;
extern int greenbits, greenshift;
extern int bluebits, blueshift;
unsigned short *dest;
qbyte *src;
int lines=vid.conheight;
int v;
int f, fstep;
dest = (unsigned short *)vid.conbuffer;
for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
{
v = (vid.conheight - lines + y)*inheight/vid.conheight;
src = framedata + v*inwidth*4;
{
f = 0;
fstep = ((inwidth)*0x10000)/vid.conwidth;
for (x=0 ; x<vid.conwidth; x++) //sw 32 bit rendering is bgrx
{
dest[x] = (((src[(f>>16)*4]*(1<<redbits))/256)<<redshift) + (((src[(f>>16)*4+1]*(1<<greenbits))/256)<<greenshift) + (((src[(f>>16)*4+2]*(1<<bluebits))/256)<<blueshift);
f += fstep;
}
}
}
*/
}
else if (r_pixbytes == 4)
{
qbyte *dest, *src;
int lines=vid.conheight;
int v;
int f, fstep;
dest = vid.conbuffer;
for (y=0 ; y<lines ; y++, dest += vid.conrowbytes*4)
{
v = (vid.conheight - lines + y)*inheight/vid.conheight;
src = framedata + v*inwidth;
{
f = 0;
fstep = ((inwidth)*0x10000)/vid.conwidth;
for (x=0 ; x<vid.conwidth*4 ; x+=4) //sw 32 bit rendering is bgrx
{
dest[x] = palette[src[(f>>16)]*3+2];
dest[x+1] = palette[src[(f>>16)]*3+1];
dest[x+2] = palette[src[(f>>16)]*3];
f += fstep;
}
}
}
}
else
Sys_Error("24 bit rendering?");
D_DisableBackBufferAccess (); // for adapters that can't stay mapped in
SCR_SetUpToDrawConsole();
if (scr_con_current)
SCR_DrawConsole (false);
M_Draw(0);
}
void MediaSW_ShowFrameRGBA_32(qbyte *framedata, int inwidth, int inheight) //top down
{
int y, x;
D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly
if (r_pixbytes == 1)
{
qbyte *dest, *src;
int lines=vid.conheight;
int v;
int f, fstep;
dest = vid.conbuffer;
for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
{
v = (vid.conheight - lines + y)*inheight/vid.conheight;
src = framedata + v*inwidth*4;
{
f = 0;
fstep = ((inwidth)*0x10000)/vid.conwidth;
for (x=0 ; x<vid.conwidth ; x+=4)
{
dest[x] = FindPalette(src[(f>>16)*4], src[(f>>16)*4+1], src[(f>>16)*4+2]);
f += fstep;
dest[x+1] = FindPalette(src[(f>>16)*4], src[(f>>16)*4+1], src[(f>>16)*4+2]);
f += fstep;
dest[x+2] = FindPalette(src[(f>>16)*4], src[(f>>16)*4+1], src[(f>>16)*4+2]);
f += fstep;
dest[x+3] = FindPalette(src[(f>>16)*4], src[(f>>16)*4+1], src[(f>>16)*4+2]);
f += fstep;
}
}
}
}
else if (r_pixbytes == 2)
{
extern int redbits, redshift;
extern int greenbits, greenshift;
extern int bluebits, blueshift;
unsigned short *dest;
qbyte *src;
int lines=vid.conheight;
int v;
int f, fstep;
dest = (unsigned short *)vid.conbuffer;
for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
{
v = (vid.conheight - lines + y)*inheight/vid.conheight;
src = framedata + v*inwidth*4;
{
f = 0;
fstep = ((inwidth)*0x10000)/vid.conwidth;
for (x=0 ; x<vid.conwidth; x++) //sw 32 bit rendering is bgrx
{
dest[x] = (((src[(f>>16)*4]*(1<<redbits))/256)<<redshift) + (((src[(f>>16)*4+1]*(1<<greenbits))/256)<<greenshift) + (((src[(f>>16)*4+2]*(1<<bluebits))/256)<<blueshift);
f += fstep;
}
}
}
}
else if (r_pixbytes == 4)
{
qbyte *dest, *src;
int lines=vid.conheight;
int v;
int f, fstep;
dest = vid.conbuffer;
for (y=0 ; y<lines ; y++, dest += vid.conrowbytes*4)
{
v = (vid.conheight - lines + y)*inheight/vid.conheight;
src = framedata + v*inwidth*4;
{
f = 0;
fstep = ((inwidth)*0x10000)/vid.conwidth;
for (x=0 ; x<vid.conwidth*4 ; x+=4) //sw 32 bit rendering is bgrx
{
dest[x] = src[(f>>16)*4+2];
dest[x+1] = src[(f>>16)*4+1];
dest[x+2] = src[(f>>16)*4];
f += fstep;
}
}
}
}
else
Sys_Error("24 bit rendering?");
D_DisableBackBufferAccess (); // for adapters that can't stay mapped in
SCR_SetUpToDrawConsole();
if (scr_con_current)
SCR_DrawConsole (false);
}
void MediaSW_ShowFrameBGR_24_Flip(qbyte *framedata, int inwidth, int inheight) //input is bottom up...
{
int y, x;
D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly
if (r_pixbytes == 1)
{
qbyte *dest, *src;
int lines=vid.conheight;
int v;
int f, fstep;
dest = vid.conbuffer;
for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
{
v = (lines - y)*inheight/vid.conheight;
src = framedata + v*inwidth*3;
{
f = 0;
fstep = ((inwidth)*0x10000)/vid.conwidth;
for (x=0 ; x<vid.conwidth ; x+=4)
{
dest[x] = FindPalette(src[(f>>16)*3+2], src[(f>>16)*3+1], src[(f>>16)*3]);
f += fstep;
dest[x+1] = FindPalette(src[(f>>16)*3+2], src[(f>>16)*3+1], src[(f>>16)*3]);
f += fstep;
dest[x+2] = FindPalette(src[(f>>16)*3+2], src[(f>>16)*3+1], src[(f>>16)*3]);
f += fstep;
dest[x+3] = FindPalette(src[(f>>16)*3+2], src[(f>>16)*3+1], src[(f>>16)*3]);
f += fstep;
}
}
}
}
else if (r_pixbytes == 2)
{
extern int redbits, redshift;
extern int greenbits, greenshift;
extern int bluebits, blueshift;
unsigned short *dest;
qbyte *src;
int lines=vid.conheight;
int v;
int f, fstep;
dest = (unsigned short *)vid.conbuffer;
for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
{
v = (lines - y)*inheight/vid.conheight;
src = framedata + v*inwidth*3;
{
f = 0;
fstep = ((inwidth)*0x10000)/vid.conwidth;
for (x=0 ; x<vid.conwidth; x++) //sw 32 bit rendering is bgrx
{
dest[x] = (((src[(f>>16)*3+2]*(1<<redbits))/256)<<redshift) + (((src[(f>>16)*3+1]*(1<<greenbits))/256)<<greenshift) + (((src[(f>>16)*3+0]*(1<<bluebits))/256)<<blueshift);
f += fstep;
}
}
}
}
else if (r_pixbytes == 4)
{
unsigned int *dest;
qbyte *src;
int lines=vid.conheight;
int v;
int f, fstep;
dest = (unsigned int *)vid.conbuffer;
for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
{
v = (lines - y)*inheight/vid.conheight;
src = framedata + v*inwidth*3;
{
f = 0;
fstep = ((inwidth)*0x10000)/vid.conwidth;
for (x=0 ; x<vid.conwidth ; x++) //sw 32 bit rendering is bgrx
{
*(dest+x) = *(int *)(src + (f>>16)*3);
f += fstep;
}
}
}
}
else
Sys_Error("24 bit rendering?");
D_DisableBackBufferAccess (); // for adapters that can't stay mapped in
SCR_SetUpToDrawConsole();
if (scr_con_current)
SCR_DrawConsole (false);
}