raze-gles/source/build/src/palette.cpp
Christoph Oelckers 156a225ff4 - renamed global palookup variable.
Since the same word gets used in text messages and local variables in the game code it is easier this way to search for it and facilitate its transition to the translation table management in PaletteContainer.
2020-04-13 23:58:46 +02:00

665 lines
No EOL
17 KiB
C++

// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.
//
// This file has been modified from Ken Silverman's original release
// by Jonathon Fowler (jf@jonof.id.au)
// by the EDuke32 team (development@voidpoint.com)
#include "compat.h"
#include "build.h"
#include "engine_priv.h"
#include "baselayer.h"
#include "imagehelpers.h"
#include "palette.h"
#include "superfasthash.h"
#include "common.h"
#include "../../glbackend/glbackend.h"
uint8_t *basepaltable[MAXBASEPALS] = { palette };
uint8_t basepalreset=1;
uint8_t curbasepal;
int32_t globalblend;
uint32_t g_lastpalettesum = 0;
palette_t palfadergb = { 0, 0, 0, 0 };
unsigned char palfadedelta = 0;
ESetPalFlags curpaletteflags;
int32_t realmaxshade;
float frealmaxshade;
#if defined(USE_OPENGL)
palette_t palookupfog[MAXPALOOKUPS];
#endif
// For every pal number, whether tsprite pal should not be taken over from
// floor pal.
// NOTE: g_noFloorPal[0] is irrelevant as it's never checked.
int8_t g_noFloorPal[MAXPALOOKUPS];
int32_t curbrightness = 0;
void setBlendFactor(int index, int alpha);
int DetermineTranslucency(const uint8_t *table)
{
uint8_t index;
PalEntry newcolor;
PalEntry newcolor2;
index = table[blackcol * 256 + whitecol];
auto pp = &basepaltable[0][index];
newcolor = PalEntry(pp[0], pp[1], pp[2]);
index = table[whitecol * 256 + blackcol];
pp = &basepaltable[0][index];
newcolor2 = PalEntry(pp[0], pp[1], pp[2]);
if (newcolor2.r == 255) // if black on white results in white it's either
// fully transparent or additive
{
return -newcolor.r;
}
return newcolor.r;
}
void fullscreen_tint_gl(PalEntry pe);
static void alloc_palookup(int32_t pal)
{
// The asm functions vlineasm1, mvlineasm1 (maybe others?) access the next
// lookuptables[...] shade entry for tilesizy==512 tiles.
// See DEBUG_TILESIZY_512 and the comment in a.nasm: vlineasm1.
lookuptables[pal] = (char *) Xaligned_alloc(16, (numshades + 1) * 256);
memset(lookuptables[pal], 0, (numshades + 1) * 256);
}
static void maybe_alloc_palookup(int32_t palnum);
void (*paletteLoadFromDisk_replace)(void) = NULL;
inline bool read_and_test(FileReader& handle, void* buffer, int32_t leng)
{
return handle.Read(buffer, leng) != leng;
};
//
// loadpalette (internal)
//
void paletteLoadFromDisk(void)
{
#ifdef USE_OPENGL
for (auto & x : glblend)
x = defaultglblend;
#endif
if (paletteLoadFromDisk_replace)
{
paletteLoadFromDisk_replace();
return;
}
auto fil = fileSystem.OpenFileReader("palette.dat");
if (!fil.isOpen())
return;
// PALETTE_MAIN
if (768 != fil.Read(palette, 768))
return;
for (unsigned char & k : palette)
k <<= 2;
paletteloaded |= PALETTE_MAIN;
// PALETTE_SHADES
if (2 != fil.Read(&numshades, 2))
return;
numshades = B_LITTLE16(numshades);
if (numshades <= 1)
{
Printf("Warning: Invalid number of shades in \"palette.dat\"!\n");
numshades = 0;
return;
}
// Auto-detect LameDuke. Its PALETTE.DAT doesn't have a 'numshades' 16-bit
// int after the base palette, but starts directly with the shade tables.
// Thus, the first two bytes will be 00 01, which is 256 if read as
// little-endian int16_t.
int32_t lamedukep = 0;
if (numshades >= 256)
{
uint16_t temp;
if (read_and_test(fil, &temp, 2))
return;
temp = B_LITTLE16(temp);
if (temp == 770 || numshades > 256) // 02 03
{
if (fil.Seek(-4, FileReader::SeekCur) < 0)
{
Printf("Warning: seek failed in loadpalette()!\n");
return;
}
numshades = 32;
lamedukep = 1;
}
else
{
if (fil.Seek(-2, FileReader::SeekCur) < 0)
{
Printf("Warning: seek failed in loadpalette()!\n");
return;
}
}
}
// Read base shade table (lookuptables 0).
maybe_alloc_palookup(0);
if (read_and_test(fil, lookuptables[0], numshades<<8))
return;
paletteloaded |= PALETTE_SHADE;
paletteloaded |= PALETTE_TRANSLUC;
// additional blending tables
uint8_t magic[12];
if (!read_and_test(fil, magic, sizeof(magic)) && !Bmemcmp(magic, "MoreBlendTab", sizeof(magic)))
{
uint8_t addblendtabs;
if (read_and_test(fil, &addblendtabs, 1))
{
Printf("Warning: failed reading additional blending table count\n");
return;
}
uint8_t blendnum;
char *tab = (char *) Xmalloc(256*256);
for (bssize_t i=0; i<addblendtabs; i++)
{
if (read_and_test(fil, &blendnum, 1))
{
Printf("Warning: failed reading additional blending table index\n");
Xfree(tab);
return;
}
if (read_and_test(fil, tab, 256*256))
{
Printf("Warning: failed reading additional blending table\n");
Xfree(tab);
return;
}
setBlendFactor(blendnum, DetermineTranslucency((const uint8_t*)tab));
}
Xfree(tab);
// Read log2 of count of alpha blending tables.
uint8_t lognumalphatabs;
if (!read_and_test(fil, &lognumalphatabs, 1))
{
if (!(lognumalphatabs >= 1 && lognumalphatabs <= 7))
Printf("invalid lognumalphatabs value, must be in [1 .. 7]\n");
else
numalphatabs = 1<<lognumalphatabs;
}
}
}
uint8_t PaletteIndexFullbrights[32];
void palettePostLoadTables(void)
{
globalpal = 0;
char const * const palookup0 = lookuptables[0];
#ifdef DEBUG_TILESIZY_512
// Bump shade 1 by 16.
for (bssize_t i=256; i<512; i++)
palookup0[i] = palookup0[i+(16<<8)];
#endif
PalEntry pe[256];
for (int i = 0; i < 256; i++) pe[i] = PalEntry(palette[i * 3], palette[i * 3 + 1], palette[i * 3 + 2]);
ImageHelpers::SetPalette(pe);
blackcol = ImageHelpers::BestColor(0, 0, 0);
whitecol = ImageHelpers::BestColor(255, 255, 255);
redcol = ImageHelpers::BestColor(255, 0, 0);
// Bmemset(PaletteIndexFullbrights, 0, sizeof(PaletteIndexFullbrights));
for (bssize_t c = 0; c < 255; ++c) // skipping transparent color
{
uint8_t const index = palookup0[c];
rgb24_t const & color = *(rgb24_t *)&palette[index*3];
// don't consider #000000 fullbright
if (EDUKE32_PREDICT_FALSE(color.r == 0 && color.g == 0 && color.b == 0))
continue;
for (size_t s = c + 256, s_end = 256*numshades; s < s_end; s += 256)
if (EDUKE32_PREDICT_FALSE(palookup0[s] != index))
goto PostLoad_NotFullbright;
SetPaletteIndexFullbright(c);
PostLoad_NotFullbright: ;
}
if (realmaxshade == 0)
{
uint8_t const * const blackcolor = &palette[blackcol*3];
size_t s;
for (s = numshades < 2 ? 0 : numshades-2; s > 0; --s)
{
for (size_t c = s*256, c_end = c+255; c < c_end; ++c) // skipping transparent color
{
uint8_t const index = palookup0[c];
uint8_t const * const color = &palette[index*3];
if (!IsPaletteIndexFullbright(index) && memcmp(blackcolor, color, sizeof(rgb24_t)))
goto PostLoad_FoundShade;
}
}
PostLoad_FoundShade: ;
frealmaxshade = (float)(realmaxshade = s+1);
}
}
void paletteFixTranslucencyMask(void)
{
for (auto thispalookup : lookuptables)
{
if (thispalookup == NULL)
continue;
for (bssize_t j=0; j<numshades; j++)
thispalookup[(j<<8) + 255] = 255;
}
}
// Load LOOKUP.DAT, which contains lookup tables and additional base palettes.
//
// <fp>: open file handle
//
// Returns:
// - on success, 0
// - on error, -1 (didn't read enough data)
// - -2: error, we already wrote an error message ourselves
int32_t paletteLoadLookupTable(FileReader &fp)
{
uint8_t numlookups;
char remapbuf[256];
if (1 != fp.Read(&numlookups, 1))
return -1;
for (bssize_t j=0; j<numlookups; j++)
{
uint8_t palnum;
if (1 != fp.Read(&palnum, 1))
return -1;
if (palnum >= 256-RESERVEDPALS)
{
Printf("ERROR: attempt to load lookup at reserved pal %d\n", palnum);
return -2;
}
if (256 != fp.Read(remapbuf, 256))
return -1;
paletteMakeLookupTable(palnum, remapbuf, 0, 0, 0, 0);
}
return 0;
}
void paletteSetupDefaultFog(void)
{
// Find a gap of four consecutive unused pal numbers to generate fog shade
// tables.
for (bssize_t j=1; j<=255-3; j++)
if (!lookuptables[j] && !lookuptables[j+1] && !lookuptables[j+2] && !lookuptables[j+3])
{
paletteMakeLookupTable(j, NULL, 60, 60, 60, 1);
paletteMakeLookupTable(j+1, NULL, 60, 0, 0, 1);
paletteMakeLookupTable(j+2, NULL, 0, 60, 0, 1);
paletteMakeLookupTable(j+3, NULL, 0, 0, 60, 1);
break;
}
}
void palettePostLoadLookups(void)
{
// Alias remaining unused pal numbers to the base shade table.
for (bssize_t j=1; j<MAXPALOOKUPS; j++)
{
// If an existing lookup is identical to #0, free it.
if (lookuptables[j] && lookuptables[j] != lookuptables[0] && !Bmemcmp(lookuptables[0], lookuptables[j], 256*numshades))
paletteFreeLookupTable(j);
if (!lookuptables[j])
paletteMakeLookupTable(j, NULL, 0, 0, 0, 1);
}
}
static int32_t palookup_isdefault(int32_t palnum) // KEEPINSYNC engine.lua
{
return (lookuptables[palnum] == NULL || (palnum!=0 && lookuptables[palnum] == lookuptables[0]));
}
static void maybe_alloc_palookup(int32_t palnum)
{
if (palookup_isdefault(palnum))
{
alloc_palookup(palnum);
if (lookuptables[palnum] == NULL)
Bexit(1);
}
}
#ifdef USE_OPENGL
glblend_t const nullglblend =
{
{
{ 1.f, BLENDFACTOR_ONE, BLENDFACTOR_ZERO, 0 },
{ 1.f, BLENDFACTOR_ONE, BLENDFACTOR_ZERO, 0 },
},
};
glblend_t const defaultglblend =
{
{
{ 2.f/3.f, BLENDFACTOR_SRC_ALPHA, BLENDFACTOR_ONE_MINUS_SRC_ALPHA, 0 },
{ 1.f/3.f, BLENDFACTOR_SRC_ALPHA, BLENDFACTOR_ONE_MINUS_SRC_ALPHA, 0 },
},
};
glblend_t glblend[MAXBLENDTABS];
void setBlendFactor(int index, int alpha)
{
if (index >= 0 && index < MAXBLENDTABS)
{
auto& myblend = glblend[index];
if (index >= 0)
{
myblend.def[0].alpha = index / 255.f;
myblend.def[1].alpha = 1.f - (index / 255.f);
myblend.def[0].src = myblend.def[1].src = BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
myblend.def[0].dst = myblend.def[1].dst = BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
}
else
{
myblend.def[0].alpha = 1;
myblend.def[1].alpha = 1;
myblend.def[0].src = myblend.def[1].src = BLENDFACTOR_ONE;
myblend.def[0].dst = myblend.def[1].dst = BLENDFACTOR_ONE;
}
}
}
FRenderStyle GetBlend(int blend, int def)
{
static uint8_t const blendFuncTokens[NUMBLENDFACTORS] =
{
STYLEALPHA_Zero,
STYLEALPHA_One,
STYLEALPHA_SrcCol,
STYLEALPHA_InvSrcCol,
STYLEALPHA_Src,
STYLEALPHA_InvSrc,
STYLEALPHA_Dst,
STYLEALPHA_InvDst,
STYLEALPHA_DstCol,
STYLEALPHA_InvDstCol,
};
FRenderStyle rs;
rs.BlendOp = STYLEOP_Add;
glblenddef_t const* const glbdef = glblend[blend].def + def;
rs.SrcAlpha = blendFuncTokens[glbdef->src];
rs.DestAlpha = blendFuncTokens[glbdef->dst];
rs.Flags = 0;
return rs;
}
void handle_blend(uint8_t enable, uint8_t blend, uint8_t def)
{
if (!enable)
{
GLInterface.SetRenderStyle(LegacyRenderStyles[STYLE_Translucent]);
return;
}
auto rs = GetBlend(blend, def);
GLInterface.SetRenderStyle(rs);
}
float float_trans(uint32_t maskprops, uint8_t blend)
{
switch (maskprops)
{
case DAMETH_TRANS1:
case DAMETH_TRANS2:
return glblend[blend].def[maskprops - 2].alpha;
default:
return 1.0f;
}
}
#endif
int32_t paletteSetLookupTable(int32_t palnum, const uint8_t *shtab)
{
if (shtab != NULL)
{
maybe_alloc_palookup(palnum);
Bmemcpy(lookuptables[palnum], shtab, 256*numshades);
}
return 0;
}
void paletteFreeLookupTable(int32_t const palnum)
{
if (palnum == 0 && lookuptables[palnum] != NULL)
{
for (bssize_t i = 1; i < MAXPALOOKUPS; i++)
if (lookuptables[i] == lookuptables[palnum])
lookuptables[i] = NULL;
ALIGNED_FREE_AND_NULL(lookuptables[palnum]);
}
else if (lookuptables[palnum] == lookuptables[0])
lookuptables[palnum] = NULL;
else
ALIGNED_FREE_AND_NULL(lookuptables[palnum]);
}
//
// makepalookup
//
void paletteMakeLookupTable(int32_t palnum, const char *remapbuf, uint8_t r, uint8_t g, uint8_t b, char noFloorPal)
{
int32_t i, j;
static char idmap[256] = { 1 };
if (paletteloaded == 0)
return;
// NOTE: palnum==0 is allowed
if ((unsigned) palnum >= MAXPALOOKUPS)
return;
g_noFloorPal[palnum] = noFloorPal;
if (remapbuf==NULL)
{
if ((r|g|b) == 0)
{
lookuptables[palnum] = lookuptables[0]; // Alias to base shade table!
return;
}
if (idmap[0]==1) // init identity map
for (i=0; i<256; i++)
idmap[i] = i;
remapbuf = idmap;
}
maybe_alloc_palookup(palnum);
if ((r|g|b) == 0)
{
// "black fog"/visibility case -- only remap color indices
for (j=0; j<numshades; j++)
for (i=0; i<256; i++)
{
const char *src = lookuptables[0];
lookuptables[palnum][256*j + i] = src[256*j + remapbuf[i]];
}
}
else
{
// colored fog case
char *ptr2 = lookuptables[palnum];
for (i=0; i<numshades; i++)
{
int32_t palscale = divscale16(i, numshades-1);
for (j=0; j<256; j++)
{
const char *ptr = (const char *) &palette[remapbuf[j]*3];
*ptr2++ = ImageHelpers::BestColor(ptr[0] + mulscale16(r-ptr[0], palscale),
ptr[1] + mulscale16(g-ptr[1], palscale),
ptr[2] + mulscale16(b-ptr[2], palscale));
}
}
}
#if defined(USE_OPENGL)
palookupfog[palnum].r = r;
palookupfog[palnum].g = g;
palookupfog[palnum].b = b;
palookupfog[palnum].f = 1;
#endif
}
//
// setbasepal
//
void paletteSetColorTable(int32_t id, uint8_t const * const table, bool transient)
{
if (basepaltable[id] == NULL)
basepaltable[id] = (uint8_t *) Xmalloc(768);
Bmemcpy(basepaltable[id], table, 768);
#ifdef USE_OPENGL
if (videoGetRenderMode() >= REND_POLYMOST)
{
uploadbasepalette(id);
}
#endif
}
void paletteFreeColorTable(int32_t const id)
{
if (id == 0)
Bmemset(basepaltable[id], 0, 768);
else
DO_FREE_AND_NULL(basepaltable[id]);
}
void paletteFreeColorTables()
{
for (int i = 0; i < countof(basepaltable); i++)
{
paletteFreeColorTable(i);
}
}
//
// setbrightness
//
// flags:
// 1: don't setpalette(), not checked anymore.
// 2: don't gltexinvalidateall()
// 4: don't calc curbrightness from dabrightness, DON'T USE THIS FLAG!
// 8: don't gltexinvalidate8()
// 16: don't reset palfade*
// 32: apply brightness to scene in OpenGL
void videoSetPalette(int dabrightness, int dapalid, ESetPalFlags flags)
{
if (/*(unsigned)dapalid >= MAXBASEPALS ||*/ basepaltable[dapalid] == NULL)
dapalid = 0;
curbasepal = dapalid;
basepalreset = 0;
auto dapal = basepaltable[curbasepal];
// In-scene brightness mode for RR's thunderstorm. This shouldn't affect the global gamma ramp.
if ((videoGetRenderMode() >= REND_POLYMOST) && (flags & Pal_SceneBrightness))
{
r_scenebrightness = clamp(dabrightness, 0, 15);
}
else
{
r_scenebrightness = 0;
}
if ((flags & Pal_DontResetFade) == 0)
{
palfadergb.r = palfadergb.g = palfadergb.b = 0;
palfadedelta = 0;
}
curpaletteflags = flags;
}
//
// setpalettefade
//
void videoFadePalette(uint8_t r, uint8_t g, uint8_t b, uint8_t offset)
{
palfadergb.r = r;
palfadergb.g = g;
palfadergb.b = b;
palfadedelta = offset;
}
void paletteFreeAll()
{
paletteloaded = 0;
for (bssize_t i = 0; i < MAXPALOOKUPS; i++)
if (i == 0 || lookuptables[i] != lookuptables[0])
{
// Take care of handling aliased ^^^ cases!
Xaligned_free(lookuptables[i]);
}
Bmemset(lookuptables, 0, sizeof(lookuptables));
for (bssize_t i = 1; i < MAXBASEPALS; i++)
Xfree(basepaltable[i]);
Bmemset(basepaltable, 0, sizeof(basepaltable));
basepaltable[0] = palette;
}