//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; int r, g, b; r = 255*r_menutint.value; g = 0; b = 0; t = strstr(r_menutint.string, " "); if (t) { g = 255*atof(t+1); t = strstr(t+1, " "); if (t) b = 255*atof(t+1); else return NULL; } else return NULL; return D_GetPaletteRemap(r, g, b, 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>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>16)*4]*(1<>16)*4+1]*(1<>16)*4+2]*(1<>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>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>16)*4]*(1<>16)*4+1]*(1<>16)*4+2]*(1<>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>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>16)*3+2]*(1<>16)*3+1]*(1<>16)*3+0]*(1<>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); }