- rewrote the default translation handling to be compatible with luminosity translations.

This commit is contained in:
Christoph Oelckers 2021-05-24 20:29:22 +02:00
parent 8b1757eee2
commit 4ff4fa643b
9 changed files with 121 additions and 185 deletions

View file

@ -32,7 +32,7 @@ struct FRemapTable
PalEntry Palette[256]; // The ideal palette this maps to
int crc32;
int Index;
int NumEntries; // # of elements in this table (usually 256), if this is -1 it is a luminosity translation.
int NumEntries; // # of elements in this table (usually 256)
bool Inactive = false; // This table is inactive and should be treated as if it was passed as NULL
bool TwodOnly = false; // Only used for 2D rendering
bool ForFont = false; // Mark font translations because they may require different handling than the ones for sprites-

View file

@ -83,7 +83,6 @@ FFont::FFont (const char *name, const char *nametemplate, const char *filetempla
Next = FirstFont;
FirstFont = this;
Cursor = '_';
ActiveColors = 0;
SpaceWidth = 0;
FontHeight = 0;
uint8_t pp = 0;
@ -590,61 +589,6 @@ void FFont::RecordAllTextureColors(uint32_t *usedcolors)
}
}
//==========================================================================
//
// SetDefaultTranslation
//
// Builds a translation to map the stock font to a mod provided replacement.
//
//==========================================================================
void FFont::SetDefaultTranslation(uint32_t *othercolors)
{
uint32_t mycolors[256] = {};
RecordAllTextureColors(mycolors);
uint8_t mytranslation[256], othertranslation[256], myreverse[256], otherreverse[256];
TArray<double> myluminosity, otherluminosity;
SimpleTranslation(mycolors, mytranslation, myreverse, myluminosity);
SimpleTranslation(othercolors, othertranslation, otherreverse, otherluminosity);
FRemapTable remap(ActiveColors);
remap.Remap[0] = 0;
remap.Palette[0] = 0;
remap.ForFont = true;
for (unsigned l = 1; l < myluminosity.Size(); l++)
{
for (unsigned o = 1; o < otherluminosity.Size()-1; o++) // luminosity[0] is for the transparent color
{
if (myluminosity[l] >= otherluminosity[o] && myluminosity[l] <= otherluminosity[o+1])
{
PalEntry color1 = GPalette.BaseColors[otherreverse[o]];
PalEntry color2 = GPalette.BaseColors[otherreverse[o+1]];
double weight = 0;
if (otherluminosity[o] != otherluminosity[o + 1])
{
weight = (myluminosity[l] - otherluminosity[o]) / (otherluminosity[o + 1] - otherluminosity[o]);
}
int r = int(color1.r + weight * (color2.r - color1.r));
int g = int(color1.g + weight * (color2.g - color1.g));
int b = int(color1.b + weight * (color2.b - color1.b));
r = clamp(r, 0, 255);
g = clamp(g, 0, 255);
b = clamp(b, 0, 255);
remap.Remap[l] = ColorMatcher.Pick(r, g, b);
remap.Palette[l] = PalEntry(255, r, g, b);
break;
}
}
}
Translations[CR_UNTRANSLATED] = GPalette.StoreTranslation(TRANSLATION_Internal, &remap);
forceremap = true;
}
//==========================================================================
//
// compare
@ -668,66 +612,40 @@ static int compare (const void *arg1, const void *arg2)
//==========================================================================
//
// FFont :: SimpleTranslation
//
// Colorsused, translation, and reverse must all be 256 entry buffers.
// Colorsused must already be filled out.
// Translation be set to remap the source colors to a new range of
// consecutive colors based at 1 (0 is transparent).
// Reverse will be just the opposite of translation: It maps the new color
// range to the original colors.
// *Luminosity will be an array just large enough to hold the brightness
// levels of all the used colors, in consecutive order. It is sorted from
// darkest to lightest and scaled such that the darkest color is 0.0 and
// the brightest color is 1.0.
// The return value is the number of used colors and thus the number of
// entries in *luminosity.
// FFont :: GetLuminosity
//
//==========================================================================
int FFont::SimpleTranslation (uint32_t *colorsused, uint8_t *translation, uint8_t *reverse, TArray<double> &Luminosity, int* minlum, int* maxlum)
int FFont::GetLuminosity (uint32_t *colorsused, TArray<double> &Luminosity, int* minlum, int* maxlum)
{
double min, max, diver;
int i, j;
memset (translation, 0, 256);
reverse[0] = 0;
for (i = 1, j = 1; i < 256; i++)
{
if (colorsused[i])
{
reverse[j++] = i;
}
}
qsort (reverse+1, j-1, 1, compare);
Luminosity.Resize(j);
Luminosity.Resize(256);
Luminosity[0] = 0.0; // [BL] Prevent uninitalized memory
max = 0.0;
min = 100000000.0;
for (i = 1; i < j; i++)
for (int i = 1; i < 256; i++)
{
translation[reverse[i]] = i;
Luminosity[i] = RPART(GPalette.BaseColors[reverse[i]]) * 0.299 +
GPART(GPalette.BaseColors[reverse[i]]) * 0.587 +
BPART(GPalette.BaseColors[reverse[i]]) * 0.114;
if (Luminosity[i] > max)
max = Luminosity[i];
if (Luminosity[i] < min)
min = Luminosity[i];
if (colorsused[i])
{
Luminosity[i] = GPalette.BaseColors[i].r * 0.299 + GPalette.BaseColors[i].g * 0.587 + GPalette.BaseColors[i].b * 0.114;
if (Luminosity[i] > max) max = Luminosity[i];
if (Luminosity[i] < min) min = Luminosity[i];
}
else Luminosity[i] = -1; // this color is not of interest.
}
diver = 1.0 / (max - min);
for (i = 1; i < j; i++)
for (int i = 1; i < 256; i++)
{
Luminosity[i] = (Luminosity[i] - min) * diver;
if (colorsused[i])
{
Luminosity[i] = (Luminosity[i] - min) * diver;
}
}
if (minlum) *minlum = int(min);
if (maxlum) *maxlum = int(max);
return j;
return 256;
}
//==========================================================================
@ -748,7 +666,7 @@ int FFont::GetColorTranslation (EColorRange range, PalEntry *color) const
}
if (color != nullptr) *color = retcolor;
}
if (ActiveColors == 0 || range == CR_UNDEFINED)
if (range == CR_UNDEFINED)
return -1;
else if (range >= NumTextColors)
range = CR_UNTRANSLATED;
@ -1046,7 +964,6 @@ void FFont::LoadTranslations()
{
unsigned int count = LastChar - FirstChar + 1;
uint32_t usedcolors[256] = {};
uint8_t identity[256];
TArray<double> Luminosity;
for (unsigned int i = 0; i < count; i++)
@ -1059,7 +976,7 @@ void FFont::LoadTranslations()
}
int minlum = 0, maxlum = 0;
ActiveColors = SimpleTranslation (usedcolors, PatchRemap, identity, Luminosity, &minlum, &maxlum);
GetLuminosity (usedcolors, Luminosity, &minlum, &maxlum);
// Here we can set everything to a luminosity translation.
@ -1068,7 +985,7 @@ void FFont::LoadTranslations()
for (int i = 0; i < NumTextColors; i++)
{
if (i == CR_UNTRANSLATED) Translations[i] = 0;
else Translations[i] = LuminosityTranslation(i*2, minlum, maxlum);
else Translations[i] = LuminosityTranslation(i*2 + TranslationType, minlum, maxlum);
}
}

View file

@ -395,58 +395,6 @@ public:
else Translations[i] = LuminosityTranslation(i * 2, minlum, maxlum);
}
}
void SetDefaultTranslation(uint32_t *colors) override
{
double myluminosity[18];
myluminosity[0] = 0;
for (int i = 1; i < 18; i++)
{
myluminosity[i] = (i - 1) / 16.;
}
uint8_t othertranslation[256], otherreverse[256];
TArray<double> otherluminosity;
SimpleTranslation(colors, othertranslation, otherreverse, otherluminosity);
FRemapTable remap(ActiveColors);
remap.Remap[0] = 0;
remap.Palette[0] = 0;
remap.ForFont = true;
for (unsigned l = 1; l < 18; l++)
{
for (unsigned o = 1; o < otherluminosity.Size() - 1; o++) // luminosity[0] is for the transparent color
{
if (myluminosity[l] >= otherluminosity[o] && myluminosity[l] <= otherluminosity[o + 1])
{
PalEntry color1 = GPalette.BaseColors[otherreverse[o]];
PalEntry color2 = GPalette.BaseColors[otherreverse[o + 1]];
double weight = 0;
if (otherluminosity[o] != otherluminosity[o + 1])
{
weight = (myluminosity[l] - otherluminosity[o]) / (otherluminosity[o + 1] - otherluminosity[o]);
}
int r = int(color1.r + weight * (color2.r - color1.r));
int g = int(color1.g + weight * (color2.g - color1.g));
int b = int(color1.b + weight * (color2.b - color1.b));
r = clamp(r, 0, 255);
g = clamp(g, 0, 255);
b = clamp(b, 0, 255);
remap.Remap[l] = ColorMatcher.Pick(r, g, b);
remap.Palette[l] = PalEntry(255, r, g, b);
break;
}
}
}
Translations[CR_UNTRANSLATED] = GPalette.StoreTranslation(TRANSLATION_Internal, &remap);
forceremap = true;
}
};

View file

@ -105,6 +105,7 @@ protected:
} FontType;
PalEntry Palette[256];
bool RescalePalette;
int ActiveColors = -1;
};
@ -468,7 +469,7 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data)
// FSingleLumpFont :: CheckFON1Chars
//
// Scans a FON1 resource for all the color values it uses and sets up
// some tables like SimpleTranslation. Data points to the RLE data for
// some tables. Data points to the RLE data for
// the characters. Also sets up the character textures.
//
//==========================================================================

View file

@ -79,7 +79,6 @@ FSinglePicFont::FSinglePicFont(const char *picname) :
SpaceWidth = (int)pic->GetDisplayWidth();
GlobalKerning = 0;
FirstChar = LastChar = 'A';
ActiveColors = 0;
PicNum = picnum;
Next = FirstFont;

View file

@ -129,11 +129,6 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FGameTexture
}
FixXMoves();
if (noTranslate)
{
ActiveColors = 0;
}
}
//==========================================================================

View file

@ -687,6 +687,75 @@ void V_ApplyLuminosityTranslation(int translation, uint8_t* pixel, int size)
}
}
//==========================================================================
//
// SetDefaultTranslation
//
// Builds a translation to map the stock font to a mod provided replacement.
// This probably won't work that well if the original font is extremely colorful.
//
//==========================================================================
static void CalcDefaultTranslation(FFont* base, int index)
{
uint32_t othercolors[256] = {};
base->RecordAllTextureColors(othercolors);
TArray<double> otherluminosity;
base->GetLuminosity(othercolors, otherluminosity);
PalEntry *remap = &paletteptr[index * 256];
memset(remap, 0, 1024);
for (unsigned i = 0; i < 256; i++)
{
auto lum = otherluminosity[i];
if (lum >= 0 && lum <= 1)
{
int index = int(lum * 255);
remap[index] = GPalette.BaseColors[i];
}
}
// todo: fill the gaps.
remap[0] = 0;
int lowindex = 0;
int highindex = 1;
while (lowindex < 255)
{
while (highindex <= 255 && remap[highindex].a == 0) highindex++;
if (lowindex == 0)
{
for (int i = 0; i < highindex; i++) remap[i] = remap[highindex];
lowindex = highindex++;
}
else if (highindex > 256)
{
for (int i = lowindex + 1; i < highindex; i++) remap[i] = remap[lowindex];
break;
}
else
{
for (int i = lowindex + 1; i < highindex; i++)
{
PalEntry color1 = remap[lowindex];
PalEntry color2 = remap[highindex];
double weight = (i - lowindex) / double(highindex - lowindex);
int r = int(color1.r + weight * (color2.r - color1.r));
int g = int(color1.g + weight * (color2.g - color1.g));
int b = int(color1.b + weight * (color2.b - color1.b));
r = clamp(r, 0, 255);
g = clamp(g, 0, 255);
b = clamp(b, 0, 255);
remap[i] = PalEntry(255, r, g, b);
}
lowindex = highindex++;
}
}
}
//==========================================================================
//
// V_LogColorFromColorRange
@ -929,24 +998,44 @@ void V_LoadTranslations()
for (auto font = FFont::FirstFont; font; font = font->Next)
{
if (!font->noTranslate) font->LoadTranslations();
else font->ActiveColors = 0;
}
if (BigFont)
{
uint32_t colors[256] = {};
BigFont->RecordAllTextureColors(colors);
if (OriginalBigFont != nullptr) OriginalBigFont->SetDefaultTranslation(colors);
CalcDefaultTranslation(BigFont, CR_UNTRANSLATED * 2 + 1);
if (OriginalBigFont != nullptr && OriginalBigFont != BigFont)
{
int sometrans = OriginalBigFont->Translations[0];
sometrans &= ~(0x3fff << 16);
sometrans |= (CR_UNTRANSLATED * 2 + 1) << 16;
OriginalBigFont->Translations[CR_UNTRANSLATED] = sometrans;
OriginalBigFont->forceremap = true;
}
}
if (SmallFont)
{
uint32_t colors[256] = {};
SmallFont->RecordAllTextureColors(colors);
if (OriginalSmallFont != nullptr) OriginalSmallFont->SetDefaultTranslation(colors);
NewSmallFont->SetDefaultTranslation(colors);
CalcDefaultTranslation(SmallFont, CR_UNTRANSLATED * 2);
if (OriginalSmallFont != nullptr && OriginalSmallFont != SmallFont)
{
int sometrans = OriginalSmallFont->Translations[0];
sometrans &= ~(0x3fff << 16);
sometrans |= (CR_UNTRANSLATED * 2) << 16;
OriginalSmallFont->Translations[CR_UNTRANSLATED] = sometrans;
OriginalSmallFont->forceremap = true;
}
if (NewSmallFont != nullptr)
{
int sometrans = NewSmallFont->Translations[0];
sometrans &= ~(0x3fff << 16);
sometrans |= (CR_UNTRANSLATED * 2) << 16;
NewSmallFont->Translations[CR_UNTRANSLATED] = sometrans;
NewSmallFont->forceremap = true;
}
}
translationsLoaded = true;
}
void V_ClearFonts()
{
while (FFont::FirstFont != nullptr)

View file

@ -128,20 +128,17 @@ public:
void SetKerning(int c) { GlobalKerning = c; }
bool NoTranslate() const { return noTranslate; }
virtual void RecordAllTextureColors(uint32_t *usedcolors);
virtual void SetDefaultTranslation(uint32_t *colors);
void CheckCase();
int GetDisplacement() const { return Displacement; }
static int GetLuminosity(uint32_t* colorsused, TArray<double>& Luminosity, int* minlum = nullptr, int* maxlum = nullptr);
protected:
FFont (int lump);
void FixXMoves();
static int SimpleTranslation (uint32_t *colorsused, uint8_t *translation,
uint8_t *identity, TArray<double> &Luminosity, int* minlum = nullptr, int* maxlum = nullptr);
void ReadSheetFont(TArray<FolderEntry> &folderdata, int width, int height, const DVector2 &Scale);
EFontType Type = EFontType::Unknown;
@ -163,7 +160,6 @@ protected:
int XMove = INT_MIN;
};
TArray<CharData> Chars;
int ActiveColors = -1;
TArray<int> Translations;
uint8_t PatchRemap[256];

View file

@ -186,15 +186,6 @@ void UpdateStatusBar(SummaryInfo* info)
VMValue params[] = { StatusBar, info };
VMCall(func, params, 2, nullptr, 0);
}
#if 1 // for testing.
for (int i = 0; i < NumTextColors; i++)
{
FStringf buffer("This is font color %d", i);
DrawText(twod, BigFont, i, 340, i * 15, buffer, DTA_FullscreenScale, FSMode_Fit640x400, TAG_DONE);
}
#endif
}
void TickStatusBar()