mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-06-02 09:51:24 +00:00
- merged FPalette and PaletteContainer.
This commit is contained in:
parent
f94e4a908c
commit
ac610d87e5
28 changed files with 162 additions and 181 deletions
|
@ -43,6 +43,8 @@
|
||||||
#include "v_palette.h"
|
#include "v_palette.h"
|
||||||
|
|
||||||
|
|
||||||
|
PaletteContainer GPalette;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -60,6 +62,22 @@ void PaletteContainer::Init(int numslots) // This cannot be a constructor!!!
|
||||||
TranslationTables.Resize(numslots);
|
TranslationTables.Resize(numslots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PaletteContainer::SetPalette(const uint8_t* colors)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 256; i++, colors += 3)
|
||||||
|
{
|
||||||
|
BaseColors[i] = PalEntry(colors[0], colors[1], colors[2]);
|
||||||
|
Remap[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find white and black from the original palette so that they can be
|
||||||
|
// used to make an educated guess of the translucency % for a BOOM
|
||||||
|
// translucency map.
|
||||||
|
WhiteIndex = BestColor((uint32_t*)BaseColors, 255, 255, 255, 0, 255);
|
||||||
|
BlackIndex = BestColor((uint32_t*)BaseColors, 0, 0, 0, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -168,7 +186,7 @@ int PaletteContainer::StoreTranslation(int slot, FRemapTable *remap)
|
||||||
auto size = NumTranslations(slot);
|
auto size = NumTranslations(slot);
|
||||||
for (i = 0; i < size; i++)
|
for (i = 0; i < size; i++)
|
||||||
{
|
{
|
||||||
if (*remap == *palMgr.TranslationToTable(TRANSLATION(slot, i)))
|
if (*remap == *TranslationToTable(TRANSLATION(slot, i)))
|
||||||
{
|
{
|
||||||
// A duplicate of this translation already exists
|
// A duplicate of this translation already exists
|
||||||
return TRANSLATION(slot, i);
|
return TRANSLATION(slot, i);
|
||||||
|
|
|
@ -67,11 +67,19 @@ inline int GetTranslationIndex(uint32_t trans)
|
||||||
|
|
||||||
class PaletteContainer
|
class PaletteContainer
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
PalEntry BaseColors[256]; // non-gamma corrected palette
|
||||||
|
uint8_t Remap[256]; // remap original palette indices to in-game indices
|
||||||
|
|
||||||
|
uint8_t WhiteIndex; // white in original palette index
|
||||||
|
uint8_t BlackIndex; // black in original palette index
|
||||||
|
private:
|
||||||
FMemArena remapArena;
|
FMemArena remapArena;
|
||||||
TArray<FRemapTable*> uniqueRemaps;
|
TArray<FRemapTable*> uniqueRemaps;
|
||||||
TArray<TAutoGrowArray<FRemapTablePtr, FRemapTable*>> TranslationTables;
|
TArray<TAutoGrowArray<FRemapTablePtr, FRemapTable*>> TranslationTables;
|
||||||
public:
|
public:
|
||||||
void Init(int numslots); // This cannot be a constructor!!!
|
void Init(int numslots); // This cannot be a constructor!!!
|
||||||
|
void SetPalette(const uint8_t* colors);
|
||||||
void Clear();
|
void Clear();
|
||||||
FRemapTable* AddRemap(FRemapTable* remap);
|
FRemapTable* AddRemap(FRemapTable* remap);
|
||||||
void UpdateTranslation(int trans, FRemapTable* remap);
|
void UpdateTranslation(int trans, FRemapTable* remap);
|
||||||
|
@ -102,5 +110,5 @@ public:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PaletteContainer palMgr;
|
extern PaletteContainer GPalette;
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
#include "printf.h"
|
#include "printf.h"
|
||||||
#include "templates.h"
|
#include "templates.h"
|
||||||
|
#include "m_png.h"
|
||||||
|
|
||||||
/****************************/
|
/****************************/
|
||||||
/* Palette management stuff */
|
/* Palette management stuff */
|
||||||
|
@ -703,6 +704,7 @@ int V_GetColor(const uint32_t* palette, FScanner& sc)
|
||||||
|
|
||||||
|
|
||||||
TArray<FSpecialColormap> SpecialColormaps;
|
TArray<FSpecialColormap> SpecialColormaps;
|
||||||
|
uint8_t DesaturateColormap[31][256];
|
||||||
|
|
||||||
// These default tables are needed for texture composition.
|
// These default tables are needed for texture composition.
|
||||||
static FSpecialColormapParameters SpecialColormapParms[] =
|
static FSpecialColormapParameters SpecialColormapParms[] =
|
||||||
|
@ -820,5 +822,91 @@ void InitSpecialColormaps(PalEntry *pe)
|
||||||
SpecialColormapParms[i].Start[2], SpecialColormapParms[i].End[0],
|
SpecialColormapParms[i].Start[2], SpecialColormapParms[i].End[0],
|
||||||
SpecialColormapParms[i].End[1], SpecialColormapParms[i].End[2]);
|
SpecialColormapParms[i].End[1], SpecialColormapParms[i].End[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// desaturated colormaps. These are used for texture composition
|
||||||
|
for (int m = 0; m < 31; m++)
|
||||||
|
{
|
||||||
|
uint8_t* shade = DesaturateColormap[m];
|
||||||
|
for (int c = 0; c < 256; c++)
|
||||||
|
{
|
||||||
|
int intensity = pe[c].Luminance();
|
||||||
|
|
||||||
|
int r = (pe[c].r * (31 - m) + intensity * m) / 31;
|
||||||
|
int g = (pe[c].g * (31 - m) + intensity * m) / 31;
|
||||||
|
int b = (pe[c].b * (31 - m) + intensity * m) / 31;
|
||||||
|
shade[c] = BestColor((uint32_t*)pe, r, g, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int ReadPalette(int lumpnum, uint8_t* buffer)
|
||||||
|
{
|
||||||
|
if (lumpnum < 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
FileData lump = fileSystem.ReadFile(lumpnum);
|
||||||
|
uint8_t* lumpmem = (uint8_t*)lump.GetMem();
|
||||||
|
memset(buffer, 0, 768);
|
||||||
|
|
||||||
|
FileReader fr;
|
||||||
|
fr.OpenMemory(lumpmem, lump.GetSize());
|
||||||
|
auto png = M_VerifyPNG(fr);
|
||||||
|
if (png)
|
||||||
|
{
|
||||||
|
uint32_t id, len;
|
||||||
|
fr.Seek(33, FileReader::SeekSet);
|
||||||
|
fr.Read(&len, 4);
|
||||||
|
fr.Read(&id, 4);
|
||||||
|
bool succeeded = false;
|
||||||
|
while (id != MAKE_ID('I', 'D', 'A', 'T') && id != MAKE_ID('I', 'E', 'N', 'D'))
|
||||||
|
{
|
||||||
|
len = BigLong((unsigned int)len);
|
||||||
|
if (id != MAKE_ID('P', 'L', 'T', 'E'))
|
||||||
|
fr.Seek(len, FileReader::SeekCur);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int PaletteSize = MIN<int>(len, 768);
|
||||||
|
fr.Read(buffer, PaletteSize);
|
||||||
|
return PaletteSize / 3;
|
||||||
|
}
|
||||||
|
fr.Seek(4, FileReader::SeekCur); // Skip CRC
|
||||||
|
fr.Read(&len, 4);
|
||||||
|
id = MAKE_ID('I', 'E', 'N', 'D');
|
||||||
|
fr.Read(&id, 4);
|
||||||
|
}
|
||||||
|
I_Error("%s contains no palette", fileSystem.GetFileFullName(lumpnum));
|
||||||
|
}
|
||||||
|
if (memcmp(lumpmem, "JASC-PAL", 8) == 0)
|
||||||
|
{
|
||||||
|
FScanner sc;
|
||||||
|
|
||||||
|
sc.OpenMem(fileSystem.GetFileFullName(lumpnum), (char*)lumpmem, int(lump.GetSize()));
|
||||||
|
sc.MustGetString();
|
||||||
|
sc.MustGetNumber(); // version - ignore
|
||||||
|
sc.MustGetNumber();
|
||||||
|
int colors = MIN(256, sc.Number) * 3;
|
||||||
|
for (int i = 0; i < colors; i++)
|
||||||
|
{
|
||||||
|
sc.MustGetNumber();
|
||||||
|
if (sc.Number < 0 || sc.Number > 255)
|
||||||
|
{
|
||||||
|
sc.ScriptError("Color %d value out of range.", sc.Number);
|
||||||
|
}
|
||||||
|
buffer[i] = sc.Number;
|
||||||
|
}
|
||||||
|
return colors / 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(buffer, lumpmem, MIN<size_t>(768, lump.GetSize()));
|
||||||
|
return 256;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,9 @@ struct FSpecialColormap
|
||||||
};
|
};
|
||||||
|
|
||||||
extern TArray<FSpecialColormap> SpecialColormaps;
|
extern TArray<FSpecialColormap> SpecialColormaps;
|
||||||
|
extern uint8_t DesaturateColormap[31][256];
|
||||||
|
|
||||||
int AddSpecialColormap(PalEntry *pe, float r1, float g1, float b1, float r2, float g2, float b2);
|
int AddSpecialColormap(PalEntry *pe, float r1, float g1, float b1, float r2, float g2, float b2);
|
||||||
void InitSpecialColormaps(PalEntry* pe);
|
void InitSpecialColormaps(PalEntry* pe);
|
||||||
void UpdateSpecialColormap(PalEntry* BaseColors, unsigned int index, float r1, float g1, float b1, float r2, float g2, float b2);
|
void UpdateSpecialColormap(PalEntry* BaseColors, unsigned int index, float r1, float g1, float b1, float r2, float g2, float b2);
|
||||||
|
int ReadPalette(int lumpnum, uint8_t* buffer);
|
||||||
|
|
|
@ -2872,7 +2872,7 @@ static int D_DoomMain_Internal (void)
|
||||||
C_InitConback();
|
C_InitConback();
|
||||||
|
|
||||||
StartScreen->Progress();
|
StartScreen->Progress();
|
||||||
palMgr.Init(NUM_TRANSLATION_TABLES);
|
GPalette.Init(NUM_TRANSLATION_TABLES);
|
||||||
V_InitFonts();
|
V_InitFonts();
|
||||||
|
|
||||||
// [CW] Parse any TEAMINFO lumps.
|
// [CW] Parse any TEAMINFO lumps.
|
||||||
|
|
|
@ -1634,7 +1634,7 @@ void FLevelLocals::QueueBody (AActor *body)
|
||||||
GetTranslationType(body->Translation) == TRANSLATION_PlayersExtra)
|
GetTranslationType(body->Translation) == TRANSLATION_PlayersExtra)
|
||||||
{
|
{
|
||||||
// This needs to be able to handle multiple levels, in case a level with dead players is used as a secondary one later.
|
// This needs to be able to handle multiple levels, in case a level with dead players is used as a secondary one later.
|
||||||
palMgr.CopyTranslation(TRANSLATION(TRANSLATION_PlayerCorpses, modslot), body->Translation);
|
GPalette.CopyTranslation(TRANSLATION(TRANSLATION_PlayerCorpses, modslot), body->Translation);
|
||||||
body->Translation = TRANSLATION(TRANSLATION_PlayerCorpses, modslot);
|
body->Translation = TRANSLATION(TRANSLATION_PlayerCorpses, modslot);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -666,7 +666,7 @@ void FFont::SetDefaultTranslation(uint32_t *othercolors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Translations[CR_UNTRANSLATED] = palMgr.StoreTranslation(TRANSLATION_Font, &remap);
|
Translations[CR_UNTRANSLATED] = GPalette.StoreTranslation(TRANSLATION_Font, &remap);
|
||||||
forceremap = true;
|
forceremap = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -797,7 +797,7 @@ void FFont::BuildTranslations (const double *luminosity, const uint8_t *identity
|
||||||
remap.Palette[j] = GPalette.BaseColors[identity[j]] | MAKEARGB(255, 0, 0, 0);
|
remap.Palette[j] = GPalette.BaseColors[identity[j]] | MAKEARGB(255, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Translations.Push(palMgr.StoreTranslation(TRANSLATION_Font, &remap));
|
Translations.Push(GPalette.StoreTranslation(TRANSLATION_Font, &remap));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -837,7 +837,7 @@ void FFont::BuildTranslations (const double *luminosity, const uint8_t *identity
|
||||||
remap.Palette[j] = PalEntry(255,r,g,b);
|
remap.Palette[j] = PalEntry(255,r,g,b);
|
||||||
}
|
}
|
||||||
if (post) post(&remap);
|
if (post) post(&remap);
|
||||||
Translations.Push(palMgr.StoreTranslation(TRANSLATION_Font, &remap));
|
Translations.Push(GPalette.StoreTranslation(TRANSLATION_Font, &remap));
|
||||||
|
|
||||||
// Advance to the next color range.
|
// Advance to the next color range.
|
||||||
while (parmstart[1].RangeStart > parmstart[0].RangeEnd)
|
while (parmstart[1].RangeStart > parmstart[0].RangeEnd)
|
||||||
|
|
|
@ -417,7 +417,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Translations[CR_UNTRANSLATED] = palMgr.StoreTranslation(TRANSLATION_Font, &remap);
|
Translations[CR_UNTRANSLATED] = GPalette.StoreTranslation(TRANSLATION_Font, &remap);
|
||||||
forceremap = true;
|
forceremap = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ FBuildTexture::FBuildTexture(const FString &pathprefix, int tilenum, const uint8
|
||||||
TArray<uint8_t> FBuildTexture::CreatePalettedPixels(int conversion)
|
TArray<uint8_t> FBuildTexture::CreatePalettedPixels(int conversion)
|
||||||
{
|
{
|
||||||
TArray<uint8_t> Pixels(Width * Height, true);
|
TArray<uint8_t> Pixels(Width * Height, true);
|
||||||
FRemapTable *Remap = palMgr.GetTranslation(TRANSLATION_Standard, Translation);
|
FRemapTable *Remap = GPalette.GetTranslation(TRANSLATION_Standard, Translation);
|
||||||
for (int i = 0; i < Width*Height; i++)
|
for (int i = 0; i < Width*Height; i++)
|
||||||
{
|
{
|
||||||
auto c = RawPixels[i];
|
auto c = RawPixels[i];
|
||||||
|
@ -95,7 +95,7 @@ TArray<uint8_t> FBuildTexture::CreatePalettedPixels(int conversion)
|
||||||
|
|
||||||
int FBuildTexture::CopyPixels(FBitmap *bmp, int conversion)
|
int FBuildTexture::CopyPixels(FBitmap *bmp, int conversion)
|
||||||
{
|
{
|
||||||
PalEntry *Remap = palMgr.GetTranslation(TRANSLATION_Standard, Translation)->Palette;
|
PalEntry *Remap = GPalette.GetTranslation(TRANSLATION_Standard, Translation)->Palette;
|
||||||
bmp->CopyPixelData(0, 0, RawPixels, Width, Height, Height, 1, 0, Remap);
|
bmp->CopyPixelData(0, 0, RawPixels, Width, Height, Height, 1, 0, Remap);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ static int BuildPaletteTranslation(int lump)
|
||||||
opal.Remap[255] = 0;
|
opal.Remap[255] = 0;
|
||||||
// Store the remap table in the translation manager so that we do not need to keep track of it ourselves.
|
// Store the remap table in the translation manager so that we do not need to keep track of it ourselves.
|
||||||
// Slot 0 for internal translations is a convenient location because normally it only contains a small number of translations.
|
// Slot 0 for internal translations is a convenient location because normally it only contains a small number of translations.
|
||||||
return GetTranslationIndex(palMgr.StoreTranslation(TRANSLATION_Standard, &opal));
|
return GetTranslationIndex(GPalette.StoreTranslation(TRANSLATION_Standard, &opal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,6 @@ TArray<uint8_t> FIMGZTexture::CreatePalettedPixels(int conversion)
|
||||||
int FIMGZTexture::CopyPixels(FBitmap *bmp, int conversion)
|
int FIMGZTexture::CopyPixels(FBitmap *bmp, int conversion)
|
||||||
{
|
{
|
||||||
if (!isalpha) return FImageSource::CopyPixels(bmp, conversion);
|
if (!isalpha) return FImageSource::CopyPixels(bmp, conversion);
|
||||||
else return CopyTranslatedPixels(bmp, palMgr.GetTranslation(TRANSLATION_Standard, STD_Grayscale)->Palette);
|
else return CopyTranslatedPixels(bmp, GPalette.GetTranslation(TRANSLATION_Standard, STD_Grayscale)->Palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ static uint8_t *GetBlendMap(PalEntry blend, uint8_t *blendwork)
|
||||||
switch (blend.a==0 ? int(blend) : -1)
|
switch (blend.a==0 ? int(blend) : -1)
|
||||||
{
|
{
|
||||||
case BLEND_ICEMAP:
|
case BLEND_ICEMAP:
|
||||||
return palMgr.TranslationToTable(TRANSLATION(TRANSLATION_Standard, 7))->Remap;
|
return GPalette.TranslationToTable(TRANSLATION(TRANSLATION_Standard, 7))->Remap;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (blend >= BLEND_SPECIALCOLORMAP1 && blend < BLEND_SPECIALCOLORMAP1 + SpecialColormaps.Size())
|
if (blend >= BLEND_SPECIALCOLORMAP1 && blend < BLEND_SPECIALCOLORMAP1 + SpecialColormaps.Size())
|
||||||
|
|
|
@ -263,7 +263,7 @@ TArray<uint8_t> FPatchTexture::CreatePalettedPixels(int conversion)
|
||||||
int FPatchTexture::CopyPixels(FBitmap *bmp, int conversion)
|
int FPatchTexture::CopyPixels(FBitmap *bmp, int conversion)
|
||||||
{
|
{
|
||||||
if (!isalpha) return FImageSource::CopyPixels(bmp, conversion);
|
if (!isalpha) return FImageSource::CopyPixels(bmp, conversion);
|
||||||
else return CopyTranslatedPixels(bmp, palMgr.GetTranslation(TRANSLATION_Standard, STD_Grayscale)->Palette);
|
else return CopyTranslatedPixels(bmp, GPalette.GetTranslation(TRANSLATION_Standard, STD_Grayscale)->Palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
|
@ -122,7 +122,7 @@ public:
|
||||||
|
|
||||||
int CopyPixels(FBitmap *bmp, int conversion) override
|
int CopyPixels(FBitmap *bmp, int conversion) override
|
||||||
{
|
{
|
||||||
bmp->CopyPixelData(0, 0, Pixels, Width, Height, Height, 1, 0, palMgr.GetTranslation(TRANSLATION_Standard, STD_Gray)->Palette);
|
bmp->CopyPixelData(0, 0, Pixels, Width, Height, Height, 1, 0, GPalette.GetTranslation(TRANSLATION_Standard, STD_Gray)->Palette);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ namespace ImageHelpers
|
||||||
{
|
{
|
||||||
if (wantluminance)
|
if (wantluminance)
|
||||||
{
|
{
|
||||||
return palMgr.GetTranslation(TRANSLATION_Standard, srcisgrayscale ? STD_Gray : STD_Grayscale)->Remap;
|
return GPalette.GetTranslation(TRANSLATION_Standard, srcisgrayscale ? STD_Gray : STD_Grayscale)->Remap;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -701,7 +701,7 @@ FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
|
||||||
buffer = new unsigned char[W*(H + 1) * 4];
|
buffer = new unsigned char[W*(H + 1) * 4];
|
||||||
memset(buffer, 0, W * (H + 1) * 4);
|
memset(buffer, 0, W * (H + 1) * 4);
|
||||||
|
|
||||||
auto remap = translation <= 0 ? nullptr : palMgr.TranslationToTable(translation);
|
auto remap = translation <= 0 ? nullptr : GPalette.TranslationToTable(translation);
|
||||||
FBitmap bmp(buffer, W * 4, W, H);
|
FBitmap bmp(buffer, W * 4, W, H);
|
||||||
|
|
||||||
int trans;
|
int trans;
|
||||||
|
|
|
@ -406,7 +406,7 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame)
|
||||||
{
|
{
|
||||||
Level->Players[i]->mo = nullptr;
|
Level->Players[i]->mo = nullptr;
|
||||||
}
|
}
|
||||||
palMgr.ClearTranslationSlot(TRANSLATION_LevelScripted);
|
GPalette.ClearTranslationSlot(TRANSLATION_LevelScripted);
|
||||||
|
|
||||||
|
|
||||||
// Initial height of PointOfView will be set by player think.
|
// Initial height of PointOfView will be set by player think.
|
||||||
|
|
|
@ -9590,7 +9590,7 @@ scriptwait:
|
||||||
case PCD_ENDTRANSLATION:
|
case PCD_ENDTRANSLATION:
|
||||||
if (translation != NULL)
|
if (translation != NULL)
|
||||||
{
|
{
|
||||||
palMgr.UpdateTranslation(TRANSLATION(TRANSLATION_LevelScripted, transi), translation);
|
GPalette.UpdateTranslation(TRANSLATION(TRANSLATION_LevelScripted, transi), translation);
|
||||||
delete translation;
|
delete translation;
|
||||||
translation = NULL;
|
translation = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,9 +62,6 @@ CUSTOM_CVAR(Color, cl_custominvulmapcolor2, 0xa6a67a, CVAR_ARCHIVE|CVAR_NOINITCA
|
||||||
|
|
||||||
TArray<FakeCmap> fakecmaps;
|
TArray<FakeCmap> fakecmaps;
|
||||||
|
|
||||||
uint8_t DesaturateColormap[31][256];
|
|
||||||
|
|
||||||
|
|
||||||
static void FreeSpecialLights();
|
static void FreeSpecialLights();
|
||||||
|
|
||||||
|
|
||||||
|
@ -168,23 +165,6 @@ void R_InitColormaps (bool allowCustomColormap)
|
||||||
// build default special maps (e.g. invulnerability)
|
// build default special maps (e.g. invulnerability)
|
||||||
InitSpecialColormaps(GPalette.BaseColors);
|
InitSpecialColormaps(GPalette.BaseColors);
|
||||||
R_UpdateInvulnerabilityColormap();
|
R_UpdateInvulnerabilityColormap();
|
||||||
|
|
||||||
// desaturated colormaps. These are used for texture composition
|
|
||||||
for(int m = 0; m < 31; m++)
|
|
||||||
{
|
|
||||||
uint8_t *shade = DesaturateColormap[m];
|
|
||||||
for (int c = 0; c < 256; c++)
|
|
||||||
{
|
|
||||||
int intensity = (GPalette.BaseColors[c].r * 77 +
|
|
||||||
GPalette.BaseColors[c].g * 143 +
|
|
||||||
GPalette.BaseColors[c].b * 37) / 256;
|
|
||||||
|
|
||||||
int r = (GPalette.BaseColors[c].r * (31-m) + intensity *m) / 31;
|
|
||||||
int g = (GPalette.BaseColors[c].g * (31-m) + intensity *m) / 31;
|
|
||||||
int b = (GPalette.BaseColors[c].b * (31-m) + intensity *m) / 31;
|
|
||||||
shade[c] = ColorMatcher.Pick(r, g, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
|
@ -106,9 +106,6 @@ inline uint32_t MakeSpecialColormap(int index)
|
||||||
return index | SPECIALCOLORMAP_MASK;
|
return index | SPECIALCOLORMAP_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern uint8_t DesaturateColormap[31][256];
|
|
||||||
|
|
||||||
|
|
||||||
enum EColorManipulation
|
enum EColorManipulation
|
||||||
{
|
{
|
||||||
CM_PLAIN2D = -2, // regular 2D drawing.
|
CM_PLAIN2D = -2, // regular 2D drawing.
|
||||||
|
|
|
@ -54,10 +54,6 @@
|
||||||
|
|
||||||
#include "gi.h"
|
#include "gi.h"
|
||||||
|
|
||||||
PaletteContainer palMgr;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const uint8_t IcePalette[16][3] =
|
const uint8_t IcePalette[16][3] =
|
||||||
{
|
{
|
||||||
{ 10, 8, 18 },
|
{ 10, 8, 18 },
|
||||||
|
@ -115,10 +111,10 @@ void StaticSerializeTranslations(FSerializer &arc)
|
||||||
int w;
|
int w;
|
||||||
if (arc.isWriting())
|
if (arc.isWriting())
|
||||||
{
|
{
|
||||||
auto size = palMgr.NumTranslations(TRANSLATION_LevelScripted);
|
auto size = GPalette.NumTranslations(TRANSLATION_LevelScripted);
|
||||||
for (unsigned int i = 0; i < size; ++i)
|
for (unsigned int i = 0; i < size; ++i)
|
||||||
{
|
{
|
||||||
trans = palMgr.TranslationToTable(TRANSLATION(TRANSLATION_LevelScripted, i));
|
trans = GPalette.TranslationToTable(TRANSLATION(TRANSLATION_LevelScripted, i));
|
||||||
if (trans != NULL && !trans->IsIdentity())
|
if (trans != NULL && !trans->IsIdentity())
|
||||||
{
|
{
|
||||||
if (arc.BeginObject(nullptr))
|
if (arc.BeginObject(nullptr))
|
||||||
|
@ -137,7 +133,7 @@ void StaticSerializeTranslations(FSerializer &arc)
|
||||||
arc("index", w);
|
arc("index", w);
|
||||||
FRemapTable remap;
|
FRemapTable remap;
|
||||||
SerializeRemap(arc, remap);
|
SerializeRemap(arc, remap);
|
||||||
palMgr.UpdateTranslation(TRANSLATION(TRANSLATION_LevelScripted, w), &remap);
|
GPalette.UpdateTranslation(TRANSLATION(TRANSLATION_LevelScripted, w), &remap);
|
||||||
arc.EndObject();
|
arc.EndObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +156,7 @@ int CreateBloodTranslation(PalEntry color)
|
||||||
if (BloodTranslationColors.Size() == 0)
|
if (BloodTranslationColors.Size() == 0)
|
||||||
{
|
{
|
||||||
// Don't use the first slot.
|
// Don't use the first slot.
|
||||||
palMgr.PushIdentityTable(TRANSLATION_Blood);
|
GPalette.PushIdentityTable(TRANSLATION_Blood);
|
||||||
BloodTranslationColors.Push(0);
|
BloodTranslationColors.Push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +186,7 @@ int CreateBloodTranslation(PalEntry color)
|
||||||
trans.Palette[i] = pe;
|
trans.Palette[i] = pe;
|
||||||
trans.Remap[i] = entry;
|
trans.Remap[i] = entry;
|
||||||
}
|
}
|
||||||
palMgr.AddTranslation(TRANSLATION_Blood, &trans);
|
GPalette.AddTranslation(TRANSLATION_Blood, &trans);
|
||||||
return BloodTranslationColors.Push(color);
|
return BloodTranslationColors.Push(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,12 +208,12 @@ void R_InitTranslationTables ()
|
||||||
// maps until then so they won't be invalid.
|
// maps until then so they won't be invalid.
|
||||||
for (i = 0; i < MAXPLAYERS; ++i)
|
for (i = 0; i < MAXPLAYERS; ++i)
|
||||||
{
|
{
|
||||||
palMgr.PushIdentityTable(TRANSLATION_Players);
|
GPalette.PushIdentityTable(TRANSLATION_Players);
|
||||||
palMgr.PushIdentityTable(TRANSLATION_PlayersExtra);
|
GPalette.PushIdentityTable(TRANSLATION_PlayersExtra);
|
||||||
palMgr.PushIdentityTable(TRANSLATION_RainPillar);
|
GPalette.PushIdentityTable(TRANSLATION_RainPillar);
|
||||||
}
|
}
|
||||||
// The menu player also gets a separate translation table
|
// The menu player also gets a separate translation table
|
||||||
palMgr.PushIdentityTable(TRANSLATION_Players);
|
GPalette.PushIdentityTable(TRANSLATION_Players);
|
||||||
|
|
||||||
// The three standard translations from Doom or Heretic (seven for Strife),
|
// The three standard translations from Doom or Heretic (seven for Strife),
|
||||||
// plus the generic ice translation.
|
// plus the generic ice translation.
|
||||||
|
@ -231,7 +227,7 @@ void R_InitTranslationTables ()
|
||||||
// color if the player who created them changes theirs.
|
// color if the player who created them changes theirs.
|
||||||
for (i = 0; i < FLevelLocals::BODYQUESIZE; ++i)
|
for (i = 0; i < FLevelLocals::BODYQUESIZE; ++i)
|
||||||
{
|
{
|
||||||
palMgr.PushIdentityTable(TRANSLATION_PlayerCorpses);
|
GPalette.PushIdentityTable(TRANSLATION_PlayerCorpses);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the standard translation tables
|
// Create the standard translation tables
|
||||||
|
@ -367,7 +363,7 @@ void R_InitTranslationTables ()
|
||||||
remap->Remap[i] = v;
|
remap->Remap[i] = v;
|
||||||
remap->Palette[i] = PalEntry(255, v, v, v);
|
remap->Palette[i] = PalEntry(255, v, v, v);
|
||||||
}
|
}
|
||||||
palMgr.AddTranslation(TRANSLATION_Standard, stdremaps, 10);
|
GPalette.AddTranslation(TRANSLATION_Standard, stdremaps, 10);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,9 +641,9 @@ void R_BuildPlayerTranslation (int player)
|
||||||
FRemapTable remaps[3];
|
FRemapTable remaps[3];
|
||||||
R_CreatePlayerTranslation (h, s, v, colorset, &Skins[players[player].userinfo.GetSkin()], &remaps[0], &remaps[1], &remaps[2]);
|
R_CreatePlayerTranslation (h, s, v, colorset, &Skins[players[player].userinfo.GetSkin()], &remaps[0], &remaps[1], &remaps[2]);
|
||||||
|
|
||||||
palMgr.UpdateTranslation(TRANSLATION(TRANSLATION_Players, player), &remaps[0]);
|
GPalette.UpdateTranslation(TRANSLATION(TRANSLATION_Players, player), &remaps[0]);
|
||||||
palMgr.UpdateTranslation(TRANSLATION(TRANSLATION_PlayersExtra, player), &remaps[1]);
|
GPalette.UpdateTranslation(TRANSLATION(TRANSLATION_PlayersExtra, player), &remaps[1]);
|
||||||
palMgr.UpdateTranslation(TRANSLATION(TRANSLATION_RainPillar, player), &remaps[2]);
|
GPalette.UpdateTranslation(TRANSLATION(TRANSLATION_RainPillar, player), &remaps[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -694,7 +690,7 @@ DEFINE_ACTION_FUNCTION(_Translation, SetPlayerTranslation)
|
||||||
FRemapTable remap;
|
FRemapTable remap;
|
||||||
R_GetPlayerTranslation(PlayerColor, GetColorSet(cls->Type, PlayerColorset),
|
R_GetPlayerTranslation(PlayerColor, GetColorSet(cls->Type, PlayerColorset),
|
||||||
&Skins[PlayerSkin], &remap);
|
&Skins[PlayerSkin], &remap);
|
||||||
palMgr.UpdateTranslation(TRANSLATION(tgroup, tnum), &remap);
|
GPalette.UpdateTranslation(TRANSLATION(tgroup, tnum), &remap);
|
||||||
}
|
}
|
||||||
ACTION_RETURN_BOOL(true);
|
ACTION_RETURN_BOOL(true);
|
||||||
}
|
}
|
||||||
|
@ -758,7 +754,7 @@ DEFINE_ACTION_FUNCTION(_Translation, GetID)
|
||||||
void R_ParseTrnslate()
|
void R_ParseTrnslate()
|
||||||
{
|
{
|
||||||
customTranslationMap.Clear();
|
customTranslationMap.Clear();
|
||||||
palMgr.ClearTranslationSlot(TRANSLATION_Custom);
|
GPalette.ClearTranslationSlot(TRANSLATION_Custom);
|
||||||
|
|
||||||
int lump;
|
int lump;
|
||||||
int lastlump = 0;
|
int lastlump = 0;
|
||||||
|
@ -781,7 +777,7 @@ void R_ParseTrnslate()
|
||||||
{
|
{
|
||||||
sc.ScriptError("Translation must be in the range [0,%d]", max);
|
sc.ScriptError("Translation must be in the range [0,%d]", max);
|
||||||
}
|
}
|
||||||
NewTranslation = *palMgr.TranslationToTable(TRANSLATION(TRANSLATION_Standard, sc.Number));
|
NewTranslation = *GPalette.TranslationToTable(TRANSLATION(TRANSLATION_Standard, sc.Number));
|
||||||
}
|
}
|
||||||
else if (sc.TokenType == TK_Identifier)
|
else if (sc.TokenType == TK_Identifier)
|
||||||
{
|
{
|
||||||
|
@ -790,7 +786,7 @@ void R_ParseTrnslate()
|
||||||
{
|
{
|
||||||
sc.ScriptError("Base translation '%s' not found in '%s'", sc.String, newtrans.GetChars());
|
sc.ScriptError("Base translation '%s' not found in '%s'", sc.String, newtrans.GetChars());
|
||||||
}
|
}
|
||||||
NewTranslation = *palMgr.TranslationToTable(tnum);
|
NewTranslation = *GPalette.TranslationToTable(tnum);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -829,7 +825,7 @@ void R_ParseTrnslate()
|
||||||
}
|
}
|
||||||
} while (sc.CheckToken(','));
|
} while (sc.CheckToken(','));
|
||||||
|
|
||||||
int trans = palMgr.StoreTranslation(TRANSLATION_Custom, &NewTranslation);
|
int trans = GPalette.StoreTranslation(TRANSLATION_Custom, &NewTranslation);
|
||||||
customTranslationMap[newtrans] = trans;
|
customTranslationMap[newtrans] = trans;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -856,7 +852,7 @@ DEFINE_ACTION_FUNCTION(_Translation, AddTranslation)
|
||||||
{
|
{
|
||||||
NewTranslation.Remap[i] = ColorMatcher.Pick(self->colors[i]);
|
NewTranslation.Remap[i] = ColorMatcher.Pick(self->colors[i]);
|
||||||
}
|
}
|
||||||
int trans = palMgr.StoreTranslation(TRANSLATION_Custom, &NewTranslation);
|
int trans = GPalette.StoreTranslation(TRANSLATION_Custom, &NewTranslation);
|
||||||
ACTION_RETURN_INT(trans);
|
ACTION_RETURN_INT(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,6 @@ uint32_t Col2RGB8_2[63][256]; // this array's second dimension is called up by p
|
||||||
ColorTable32k RGB32k;
|
ColorTable32k RGB32k;
|
||||||
ColorTable256k RGB256k;
|
ColorTable256k RGB256k;
|
||||||
|
|
||||||
FPalette GPalette;
|
|
||||||
FColorMatcher ColorMatcher;
|
FColorMatcher ColorMatcher;
|
||||||
|
|
||||||
/* Current color blending values */
|
/* Current color blending values */
|
||||||
|
@ -97,99 +96,6 @@ CCMD (bumpgamma)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FPalette::FPalette ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FPalette::FPalette (const uint8_t *colors)
|
|
||||||
{
|
|
||||||
SetPalette (colors);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FPalette::SetPalette (const uint8_t *colors)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 256; i++, colors += 3)
|
|
||||||
{
|
|
||||||
BaseColors[i] = PalEntry (colors[0], colors[1], colors[2]);
|
|
||||||
Remap[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find white and black from the original palette so that they can be
|
|
||||||
// used to make an educated guess of the translucency % for a BOOM
|
|
||||||
// translucency map.
|
|
||||||
WhiteIndex = BestColor ((uint32_t *)BaseColors, 255, 255, 255, 0, 255);
|
|
||||||
BlackIndex = BestColor ((uint32_t *)BaseColors, 0, 0, 0, 0, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ReadPalette(int lumpnum, uint8_t *buffer)
|
|
||||||
{
|
|
||||||
if (lumpnum < 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
FileData lump = fileSystem.ReadFile(lumpnum);
|
|
||||||
uint8_t *lumpmem = (uint8_t*)lump.GetMem();
|
|
||||||
memset(buffer, 0, 768);
|
|
||||||
|
|
||||||
FileReader fr;
|
|
||||||
fr.OpenMemory(lumpmem, lump.GetSize());
|
|
||||||
auto png = M_VerifyPNG(fr);
|
|
||||||
if (png)
|
|
||||||
{
|
|
||||||
uint32_t id, len;
|
|
||||||
fr.Seek(33, FileReader::SeekSet);
|
|
||||||
fr.Read(&len, 4);
|
|
||||||
fr.Read(&id, 4);
|
|
||||||
bool succeeded = false;
|
|
||||||
while (id != MAKE_ID('I', 'D', 'A', 'T') && id != MAKE_ID('I', 'E', 'N', 'D'))
|
|
||||||
{
|
|
||||||
len = BigLong((unsigned int)len);
|
|
||||||
if (id != MAKE_ID('P', 'L', 'T', 'E'))
|
|
||||||
fr.Seek(len, FileReader::SeekCur);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int PaletteSize = MIN<int>(len, 768);
|
|
||||||
fr.Read(buffer, PaletteSize);
|
|
||||||
return PaletteSize / 3;
|
|
||||||
}
|
|
||||||
fr.Seek(4, FileReader::SeekCur); // Skip CRC
|
|
||||||
fr.Read(&len, 4);
|
|
||||||
id = MAKE_ID('I', 'E', 'N', 'D');
|
|
||||||
fr.Read(&id, 4);
|
|
||||||
}
|
|
||||||
I_Error("%s contains no palette", fileSystem.GetFileFullName(lumpnum));
|
|
||||||
}
|
|
||||||
if (memcmp(lumpmem, "JASC-PAL", 8) == 0)
|
|
||||||
{
|
|
||||||
FScanner sc;
|
|
||||||
|
|
||||||
sc.OpenMem(fileSystem.GetFileFullName(lumpnum), (char*)lumpmem, int(lump.GetSize()));
|
|
||||||
sc.MustGetString();
|
|
||||||
sc.MustGetNumber(); // version - ignore
|
|
||||||
sc.MustGetNumber();
|
|
||||||
int colors = MIN(256, sc.Number) * 3;
|
|
||||||
for (int i = 0; i < colors; i++)
|
|
||||||
{
|
|
||||||
sc.MustGetNumber();
|
|
||||||
if (sc.Number < 0 || sc.Number > 255)
|
|
||||||
{
|
|
||||||
sc.ScriptError("Color %d value out of range.", sc.Number);
|
|
||||||
}
|
|
||||||
buffer[i] = sc.Number;
|
|
||||||
}
|
|
||||||
return colors / 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(buffer, lumpmem, MIN<size_t>(768, lump.GetSize()));
|
|
||||||
return 256;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// BuildTransTable
|
// BuildTransTable
|
||||||
|
|
|
@ -38,23 +38,7 @@
|
||||||
#include "c_cvars.h"
|
#include "c_cvars.h"
|
||||||
#include "palutil.h"
|
#include "palutil.h"
|
||||||
|
|
||||||
struct FPalette
|
|
||||||
{
|
|
||||||
FPalette ();
|
|
||||||
FPalette (const uint8_t *colors);
|
|
||||||
|
|
||||||
void SetPalette (const uint8_t *colors);
|
|
||||||
|
|
||||||
void MakeGoodRemap ();
|
|
||||||
|
|
||||||
PalEntry BaseColors[256]; // non-gamma corrected palette
|
|
||||||
uint8_t Remap[256]; // remap original palette indices to in-game indices
|
|
||||||
|
|
||||||
uint8_t WhiteIndex; // white in original palette index
|
|
||||||
uint8_t BlackIndex; // black in original palette index
|
|
||||||
};
|
|
||||||
|
|
||||||
extern FPalette GPalette;
|
|
||||||
|
|
||||||
// The color overlay to use for depleted items
|
// The color overlay to use for depleted items
|
||||||
#define DIM_OVERLAY MAKEARGB(170,0,0,0)
|
#define DIM_OVERLAY MAKEARGB(170,0,0,0)
|
||||||
|
|
|
@ -110,7 +110,7 @@ void hw_PrecacheTexture(uint8_t *texhitlist, TMap<PClassActor*, bool> &actorhitl
|
||||||
while (it.NextPair(pair))
|
while (it.NextPair(pair))
|
||||||
{
|
{
|
||||||
PClassActor *cls = pair->Key;
|
PClassActor *cls = pair->Key;
|
||||||
auto remap = palMgr.TranslationToTable(GetDefaultByType(cls)->Translation);
|
auto remap = GPalette.TranslationToTable(GetDefaultByType(cls)->Translation);
|
||||||
int gltrans = remap == nullptr ? 0 : remap->Index;
|
int gltrans = remap == nullptr ? 0 : remap->Index;
|
||||||
|
|
||||||
for (unsigned i = 0; i < cls->GetStateCount(); i++)
|
for (unsigned i = 0; i < cls->GetStateCount(); i++)
|
||||||
|
|
|
@ -44,7 +44,7 @@ private:
|
||||||
|
|
||||||
TranslatedTexture * GetTexID(int translation, bool expanded)
|
TranslatedTexture * GetTexID(int translation, bool expanded)
|
||||||
{
|
{
|
||||||
auto remap = palMgr.TranslationToTable(translation);
|
auto remap = GPalette.TranslationToTable(translation);
|
||||||
translation = remap == nullptr ? 0 : remap->Index;
|
translation = remap == nullptr ? 0 : remap->Index;
|
||||||
|
|
||||||
if (translation == 0)
|
if (translation == 0)
|
||||||
|
|
|
@ -474,7 +474,7 @@ namespace swrenderer
|
||||||
SetTranslationMap(nullptr);
|
SetTranslationMap(nullptr);
|
||||||
if (translation != 0)
|
if (translation != 0)
|
||||||
{
|
{
|
||||||
FRemapTable *table = palMgr.TranslationToTable(translation);
|
FRemapTable *table = GPalette.TranslationToTable(translation);
|
||||||
if (table != NULL && !table->Inactive)
|
if (table != NULL && !table->Inactive)
|
||||||
{
|
{
|
||||||
if (viewport->RenderTarget->IsBgra())
|
if (viewport->RenderTarget->IsBgra())
|
||||||
|
|
|
@ -132,7 +132,7 @@ void V_DrawPaletteTester(int paletteno)
|
||||||
PalEntry pe;
|
PalEntry pe;
|
||||||
if (t > 1)
|
if (t > 1)
|
||||||
{
|
{
|
||||||
auto palette = palMgr.GetTranslation(TRANSLATION_Standard, t - 2)->Palette;
|
auto palette = GPalette.GetTranslation(TRANSLATION_Standard, t - 2)->Palette;
|
||||||
pe = palette[k];
|
pe = palette[k];
|
||||||
}
|
}
|
||||||
else GPalette.BaseColors[k];
|
else GPalette.BaseColors[k];
|
||||||
|
|
|
@ -756,7 +756,7 @@ DEFINE_PROPERTY(translation, L, Actor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defaults->Translation = palMgr.StoreTranslation (TRANSLATION_Decorate, &CurrentTranslation);
|
defaults->Translation = GPalette.StoreTranslation (TRANSLATION_Decorate, &CurrentTranslation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,8 @@
|
||||||
#include "cmdlib.h"
|
#include "cmdlib.h"
|
||||||
#include "v_text.h"
|
#include "v_text.h"
|
||||||
#include "m_argv.h"
|
#include "m_argv.h"
|
||||||
|
#include "keydef.h"
|
||||||
|
#include "printf.h"
|
||||||
|
|
||||||
#define SAFE_RELEASE(x) { if (x != NULL) { x->Release(); x = NULL; } }
|
#define SAFE_RELEASE(x) { if (x != NULL) { x->Release(); x = NULL; } }
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue