mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-01-31 13:00:59 +00:00
- font system overhaul.
This eliminates nearly all palette dependencies, most importantly font translation will now be done on True Color data, making translations on True Color font less destructive.
This commit is contained in:
parent
0236271ef6
commit
5f02b92cd0
15 changed files with 479 additions and 699 deletions
|
@ -173,9 +173,8 @@ void DrawChar(F2DDrawer *drawer, FFont* font, int normalcolor, double x, double
|
|||
|
||||
FGameTexture* pic;
|
||||
int dummy;
|
||||
bool redirected;
|
||||
|
||||
if (NULL != (pic = font->GetChar(character, normalcolor, &dummy, &redirected)))
|
||||
if (NULL != (pic = font->GetChar(character, normalcolor, &dummy)))
|
||||
{
|
||||
DrawParms parms;
|
||||
Va_List tags;
|
||||
|
@ -188,7 +187,7 @@ void DrawChar(F2DDrawer *drawer, FFont* font, int normalcolor, double x, double
|
|||
}
|
||||
bool palettetrans = (normalcolor == CR_UNDEFINED && parms.TranslationId != 0);
|
||||
PalEntry color = 0xffffffff;
|
||||
if (!palettetrans) parms.TranslationId = redirected ? -1 : font->GetColorTranslation((EColorRange)normalcolor, &color);
|
||||
if (!palettetrans) parms.TranslationId = font->GetColorTranslation((EColorRange)normalcolor, &color);
|
||||
parms.color = PalEntry((color.a * parms.color.a) / 255, (color.r * parms.color.r) / 255, (color.g * parms.color.g) / 255, (color.b * parms.color.b) / 255);
|
||||
drawer->AddTexture(pic, parms);
|
||||
}
|
||||
|
@ -204,9 +203,8 @@ void DrawChar(F2DDrawer *drawer, FFont *font, int normalcolor, double x, double
|
|||
|
||||
FGameTexture *pic;
|
||||
int dummy;
|
||||
bool redirected;
|
||||
|
||||
if (NULL != (pic = font->GetChar(character, normalcolor, &dummy, &redirected)))
|
||||
if (NULL != (pic = font->GetChar(character, normalcolor, &dummy)))
|
||||
{
|
||||
DrawParms parms;
|
||||
uint32_t tag = ListGetInt(args);
|
||||
|
@ -214,7 +212,7 @@ void DrawChar(F2DDrawer *drawer, FFont *font, int normalcolor, double x, double
|
|||
if (!res) return;
|
||||
bool palettetrans = (normalcolor == CR_UNDEFINED && parms.TranslationId != 0);
|
||||
PalEntry color = 0xffffffff;
|
||||
if (!palettetrans) parms.TranslationId = redirected ? -1 : font->GetColorTranslation((EColorRange)normalcolor, &color);
|
||||
if (!palettetrans) parms.TranslationId = font->GetColorTranslation((EColorRange)normalcolor, &color);
|
||||
parms.color = PalEntry((color.a * parms.color.a) / 255, (color.r * parms.color.r) / 255, (color.g * parms.color.g) / 255, (color.b * parms.color.b) / 255);
|
||||
drawer->AddTexture(pic, parms);
|
||||
}
|
||||
|
@ -316,11 +314,10 @@ void DrawTextCommon(F2DDrawer *drawer, FFont *font, int normalcolor, double x, d
|
|||
continue;
|
||||
}
|
||||
|
||||
bool redirected = false;
|
||||
if (NULL != (pic = font->GetChar(c, currentcolor, &w, &redirected)))
|
||||
if (NULL != (pic = font->GetChar(c, currentcolor, &w)))
|
||||
{
|
||||
// if palette translation is used, font colors will be ignored.
|
||||
if (!palettetrans) parms.TranslationId = redirected? -1 : trans;
|
||||
if (!palettetrans) parms.TranslationId = trans;
|
||||
SetTextureParms(drawer, &parms, pic, cx, cy);
|
||||
if (parms.cellx)
|
||||
{
|
||||
|
|
|
@ -70,12 +70,26 @@ inline constexpr uint32_t TRANSLATION(uint8_t a, uint32_t b)
|
|||
{
|
||||
return (a << TRANSLATION_SHIFT) | b;
|
||||
}
|
||||
inline constexpr uint32_t LuminosityTranslation(int range, uint8_t min, uint8_t max)
|
||||
{
|
||||
// ensure that the value remains positive.
|
||||
return ( (1 << 30) | ((range&0x3fff) << 16) | (min << 8) | max );
|
||||
}
|
||||
|
||||
inline constexpr bool IsLuminosityTranslation(int trans)
|
||||
{
|
||||
return trans > 0 && (trans & (1 << 30));
|
||||
}
|
||||
|
||||
inline constexpr int GetTranslationType(uint32_t trans)
|
||||
{
|
||||
assert(!IsLuminosityTranslation(trans));
|
||||
return (trans & TRANSLATIONTYPE_MASK) >> TRANSLATION_SHIFT;
|
||||
}
|
||||
|
||||
inline constexpr int GetTranslationIndex(uint32_t trans)
|
||||
{
|
||||
assert(!IsLuminosityTranslation(trans));
|
||||
return (trans & TRANSLATION_MASK);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -333,23 +332,13 @@ FFont::FFont (const char *name, const char *nametemplate, const char *filetempla
|
|||
TexMan.AddGameTexture(tex);
|
||||
Chars[i].OriginalPic = tex;
|
||||
|
||||
if (!noTranslate)
|
||||
{
|
||||
Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar1(orig->GetImage())), nullptr, ETextureType::FontChar);
|
||||
Chars[i].TranslatedPic->CopySize(pic, true);
|
||||
TexMan.AddGameTexture(Chars[i].TranslatedPic);
|
||||
}
|
||||
else
|
||||
{
|
||||
Chars[i].TranslatedPic = tex;
|
||||
}
|
||||
if (sysCallbacks.FontCharCreated) sysCallbacks.FontCharCreated(pic, Chars[i].OriginalPic, Chars[i].TranslatedPic);
|
||||
if (sysCallbacks.FontCharCreated) sysCallbacks.FontCharCreated(pic, Chars[i].OriginalPic, Chars[i].OriginalPic);
|
||||
|
||||
Chars[i].XMove = (int)Chars[i].TranslatedPic->GetDisplayWidth();
|
||||
Chars[i].XMove = (int)Chars[i].OriginalPic->GetDisplayWidth();
|
||||
}
|
||||
else
|
||||
{
|
||||
Chars[i].TranslatedPic = nullptr;
|
||||
Chars[i].OriginalPic = nullptr;
|
||||
Chars[i].XMove = INT_MIN;
|
||||
}
|
||||
}
|
||||
|
@ -360,7 +349,7 @@ FFont::FFont (const char *name, const char *nametemplate, const char *filetempla
|
|||
{
|
||||
SpaceWidth = spacewidth;
|
||||
}
|
||||
else if ('N' - FirstChar >= 0 && 'N' - FirstChar < count && Chars['N' - FirstChar].TranslatedPic != nullptr)
|
||||
else if ('N' - FirstChar >= 0 && 'N' - FirstChar < count && Chars['N' - FirstChar].OriginalPic != nullptr)
|
||||
{
|
||||
SpaceWidth = (Chars['N' - FirstChar].XMove + 1) / 2;
|
||||
}
|
||||
|
@ -444,11 +433,7 @@ void FFont::ReadSheetFont(TArray<FolderEntry> &folderdata, int width, int height
|
|||
Chars[i].OriginalPic = (*lump)->GetUseType() == ETextureType::FontChar? (*lump) : MakeGameTexture(pic, nullptr, ETextureType::FontChar);
|
||||
Chars[i].OriginalPic->SetUseType(ETextureType::FontChar);
|
||||
Chars[i].OriginalPic->CopySize(*lump, true);
|
||||
Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar1(pic->GetImage())), nullptr, ETextureType::FontChar);
|
||||
Chars[i].TranslatedPic->CopySize(*lump, true);
|
||||
Chars[i].TranslatedPic->SetUseType(ETextureType::FontChar);
|
||||
if (Chars[i].OriginalPic != *lump) TexMan.AddGameTexture(Chars[i].OriginalPic);
|
||||
TexMan.AddGameTexture(Chars[i].TranslatedPic);
|
||||
}
|
||||
Chars[i].XMove = width;
|
||||
}
|
||||
|
@ -458,7 +443,7 @@ void FFont::ReadSheetFont(TArray<FolderEntry> &folderdata, int width, int height
|
|||
// Move the Windows-1252 characters to their proper place.
|
||||
for (int i = 0x80; i < 0xa0; i++)
|
||||
{
|
||||
if (win1252map[i - 0x80] != i && Chars[i - minchar].TranslatedPic != nullptr && Chars[win1252map[i - 0x80] - minchar].TranslatedPic == nullptr)
|
||||
if (win1252map[i - 0x80] != i && Chars[i - minchar].OriginalPic != nullptr && Chars[win1252map[i - 0x80] - minchar].OriginalPic == nullptr)
|
||||
{
|
||||
std::swap(Chars[i - minchar], Chars[win1252map[i - 0x80] - minchar]);
|
||||
}
|
||||
|
@ -509,24 +494,24 @@ void FFont::CheckCase()
|
|||
}
|
||||
if (myislower(chr))
|
||||
{
|
||||
if (Chars[i].TranslatedPic != nullptr) lowercount++;
|
||||
if (Chars[i].OriginalPic != nullptr) lowercount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Chars[i].TranslatedPic != nullptr) uppercount++;
|
||||
if (Chars[i].OriginalPic != nullptr) uppercount++;
|
||||
}
|
||||
}
|
||||
if (lowercount == 0) return; // This is an uppercase-only font and we are done.
|
||||
|
||||
// The ß needs special treatment because it is far more likely to be supplied lowercase only, even in an uppercase font.
|
||||
if (Chars[0xdf - FirstChar].TranslatedPic != nullptr)
|
||||
if (Chars[0xdf - FirstChar].OriginalPic != nullptr)
|
||||
{
|
||||
if (LastChar < 0x1e9e)
|
||||
{
|
||||
Chars.Resize(0x1e9f - FirstChar);
|
||||
LastChar = 0x1e9e;
|
||||
}
|
||||
if (Chars[0x1e9e - FirstChar].TranslatedPic == nullptr)
|
||||
if (Chars[0x1e9e - FirstChar].OriginalPic == nullptr)
|
||||
{
|
||||
std::swap(Chars[0xdf - FirstChar], Chars[0x1e9e - FirstChar]);
|
||||
lowercount--;
|
||||
|
@ -596,75 +581,14 @@ void FFont::RecordAllTextureColors(uint32_t *usedcolors)
|
|||
{
|
||||
for (unsigned int i = 0; i < Chars.Size(); i++)
|
||||
{
|
||||
if (Chars[i].TranslatedPic)
|
||||
if (Chars[i].OriginalPic)
|
||||
{
|
||||
FFontChar1 *pic = static_cast<FFontChar1 *>(Chars[i].TranslatedPic->GetTexture()->GetImage());
|
||||
if (pic)
|
||||
{
|
||||
// The remap must be temporarily reset here because this can be called on an initialized font.
|
||||
auto sr = pic->ResetSourceRemap();
|
||||
RecordTextureColors(pic, usedcolors);
|
||||
pic->SetSourceRemap(sr);
|
||||
}
|
||||
auto pic = Chars[i].OriginalPic->GetTexture()->GetImage();
|
||||
if (pic) RecordTextureColors(pic, 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
|
||||
|
@ -688,160 +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 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;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FFont :: BuildTranslations
|
||||
//
|
||||
// Build color translations for this font. Luminosity is an array of
|
||||
// brightness levels. The ActiveColors member must be set to indicate how
|
||||
// large this array is. Identity is an array that remaps the colors to
|
||||
// their original values; it is only used for CR_UNTRANSLATED. Ranges
|
||||
// is an array of TranslationParm structs defining the ranges for every
|
||||
// possible color, in order. Palette is the colors to use for the
|
||||
// untranslated version of the font.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFont::BuildTranslations (const double *luminosity, const uint8_t *identity,
|
||||
const void *ranges, int total_colors, const PalEntry *palette, std::function<void(FRemapTable*)> post)
|
||||
{
|
||||
int i, j;
|
||||
const TranslationParm *parmstart = (const TranslationParm *)ranges;
|
||||
|
||||
FRemapTable remap(total_colors);
|
||||
remap.ForFont = true;
|
||||
|
||||
// Create different translations for different color ranges
|
||||
Translations.Clear();
|
||||
for (i = 0; i < NumTextColors; i++)
|
||||
{
|
||||
if (i == CR_UNTRANSLATED)
|
||||
{
|
||||
if (identity != nullptr)
|
||||
{
|
||||
memcpy(remap.Remap, identity, ActiveColors);
|
||||
if (palette != nullptr)
|
||||
{
|
||||
memcpy(remap.Palette, palette, ActiveColors * sizeof(PalEntry));
|
||||
}
|
||||
else
|
||||
{
|
||||
remap.Palette[0] = GPalette.BaseColors[identity[0]] & MAKEARGB(0, 255, 255, 255);
|
||||
for (j = 1; j < ActiveColors; ++j)
|
||||
{
|
||||
remap.Palette[j] = GPalette.BaseColors[identity[j]] | MAKEARGB(255, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
Translations.Push(GPalette.StoreTranslation(TRANSLATION_Internal, &remap));
|
||||
}
|
||||
else
|
||||
{
|
||||
Translations.Push(Translations[0]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(parmstart->RangeStart >= 0);
|
||||
|
||||
remap.Remap[0] = 0;
|
||||
remap.Palette[0] = 0;
|
||||
remap.ForFont = true;
|
||||
|
||||
for (j = 1; j < ActiveColors; j++)
|
||||
{
|
||||
int v = int(luminosity[j] * 256.0);
|
||||
|
||||
// Find the color range that this luminosity value lies within.
|
||||
const TranslationParm *parms = parmstart - 1;
|
||||
do
|
||||
{
|
||||
parms++;
|
||||
if (parms->RangeStart <= v && parms->RangeEnd >= v)
|
||||
break;
|
||||
}
|
||||
while (parms[1].RangeStart > parms[0].RangeEnd);
|
||||
|
||||
// Linearly interpolate to find out which color this luminosity level gets.
|
||||
int rangev = ((v - parms->RangeStart) << 8) / (parms->RangeEnd - parms->RangeStart);
|
||||
int r = ((parms->Start[0] << 8) + rangev * (parms->End[0] - parms->Start[0])) >> 8; // red
|
||||
int g = ((parms->Start[1] << 8) + rangev * (parms->End[1] - parms->Start[1])) >> 8; // green
|
||||
int b = ((parms->Start[2] << 8) + rangev * (parms->End[2] - parms->Start[2])) >> 8; // blue
|
||||
r = clamp(r, 0, 255);
|
||||
g = clamp(g, 0, 255);
|
||||
b = clamp(b, 0, 255);
|
||||
remap.Remap[j] = ColorMatcher.Pick(r, g, b);
|
||||
remap.Palette[j] = PalEntry(255,r,g,b);
|
||||
}
|
||||
if (post) post(&remap);
|
||||
Translations.Push(GPalette.StoreTranslation(TRANSLATION_Internal, &remap));
|
||||
|
||||
// Advance to the next color range.
|
||||
while (parmstart[1].RangeStart > parmstart[0].RangeEnd)
|
||||
{
|
||||
parmstart++;
|
||||
}
|
||||
parmstart++;
|
||||
}
|
||||
return 256;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -862,11 +666,10 @@ 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;
|
||||
//if (range == CR_UNTRANSLATED && !translateUntranslated) return nullptr;
|
||||
return Translations[range];
|
||||
}
|
||||
|
||||
|
@ -889,7 +692,7 @@ int FFont::GetCharCode(int code, bool needpic) const
|
|||
// regular chars turn negative when the 8th bit is set.
|
||||
code &= 255;
|
||||
}
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr))
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr))
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
@ -903,7 +706,7 @@ int FFont::GetCharCode(int code, bool needpic) const
|
|||
if (myislower(code))
|
||||
{
|
||||
code = upperforlower[code];
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr))
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr))
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
@ -912,7 +715,7 @@ int FFont::GetCharCode(int code, bool needpic) const
|
|||
while ((newcode = stripaccent(code)) != code)
|
||||
{
|
||||
code = newcode;
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr))
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr))
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
@ -926,7 +729,7 @@ int FFont::GetCharCode(int code, bool needpic) const
|
|||
while ((newcode = stripaccent(code)) != code)
|
||||
{
|
||||
code = newcode;
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr))
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr))
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
@ -944,7 +747,7 @@ int FFont::GetCharCode(int code, bool needpic) const
|
|||
while ((newcode = stripaccent(code)) != code)
|
||||
{
|
||||
code = newcode;
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr))
|
||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr))
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
@ -961,7 +764,7 @@ int FFont::GetCharCode(int code, bool needpic) const
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FGameTexture *FFont::GetChar (int code, int translation, int *const width, bool *redirected) const
|
||||
FGameTexture *FFont::GetChar (int code, int translation, int *const width) const
|
||||
{
|
||||
code = GetCharCode(code, true);
|
||||
int xmove = SpaceWidth;
|
||||
|
@ -979,19 +782,8 @@ FGameTexture *FFont::GetChar (int code, int translation, int *const width, bool
|
|||
if (code < 0) return nullptr;
|
||||
|
||||
|
||||
if ((translation == CR_UNTRANSLATED || translation == CR_UNDEFINED) && !forceremap)
|
||||
{
|
||||
bool redirect = Chars[code].OriginalPic && Chars[code].OriginalPic != Chars[code].TranslatedPic;
|
||||
if (redirected) *redirected = redirect;
|
||||
if (redirect)
|
||||
{
|
||||
assert(Chars[code].OriginalPic->GetUseType() == ETextureType::FontChar);
|
||||
return Chars[code].OriginalPic;
|
||||
}
|
||||
}
|
||||
if (redirected) *redirected = false;
|
||||
assert(Chars[code].TranslatedPic->GetUseType() == ETextureType::FontChar);
|
||||
return Chars[code].TranslatedPic;
|
||||
assert(Chars[code].OriginalPic->GetUseType() == ETextureType::FontChar);
|
||||
return Chars[code].OriginalPic;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -1172,31 +964,29 @@ 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++)
|
||||
{
|
||||
if (Chars[i].TranslatedPic)
|
||||
if (Chars[i].OriginalPic)
|
||||
{
|
||||
FFontChar1 *pic = static_cast<FFontChar1 *>(Chars[i].TranslatedPic->GetTexture()->GetImage());
|
||||
if (pic)
|
||||
{
|
||||
pic->SetSourceRemap(nullptr); // Force the FFontChar1 to return the same pixels as the base texture
|
||||
RecordTextureColors(pic, usedcolors);
|
||||
}
|
||||
auto pic = Chars[i].OriginalPic->GetTexture()->GetImage();
|
||||
if (pic) RecordTextureColors(pic, usedcolors);
|
||||
}
|
||||
}
|
||||
|
||||
ActiveColors = SimpleTranslation (usedcolors, PatchRemap, identity, Luminosity);
|
||||
int minlum = 0, maxlum = 0;
|
||||
GetLuminosity (usedcolors, Luminosity, &minlum, &maxlum);
|
||||
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
// Here we can set everything to a luminosity translation.
|
||||
|
||||
// Create different translations for different color ranges
|
||||
Translations.Resize(NumTextColors);
|
||||
for (int i = 0; i < NumTextColors; i++)
|
||||
{
|
||||
if(Chars[i].TranslatedPic)
|
||||
static_cast<FFontChar1 *>(Chars[i].TranslatedPic->GetTexture()->GetImage())->SetSourceRemap(PatchRemap);
|
||||
if (i == CR_UNTRANSLATED) Translations[i] = 0;
|
||||
else Translations[i] = LuminosityTranslation(i*2 + TranslationType, minlum, maxlum);
|
||||
}
|
||||
|
||||
BuildTranslations (Luminosity.Data(), identity, &TranslationParms[TranslationType][0], ActiveColors, nullptr);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <stdint.h>
|
||||
#include "tarray.h"
|
||||
|
||||
// This structure is used by BuildTranslations() to hold color information.
|
||||
struct TranslationParm
|
||||
{
|
||||
short RangeStart; // First level for this range
|
||||
|
|
|
@ -50,6 +50,8 @@ struct HexDataSource
|
|||
TArray<uint8_t> glyphdata;
|
||||
unsigned glyphmap[65536] = {};
|
||||
|
||||
PalEntry ConsolePal[18], SmallPal[18];
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// parse a HEX font
|
||||
|
@ -83,6 +85,17 @@ struct HexDataSource
|
|||
if (codepoint > LastChar) LastChar = codepoint;
|
||||
}
|
||||
}
|
||||
|
||||
ConsolePal[0] = SmallPal[0] = 0;
|
||||
for (int i = 1; i < 18; i++)
|
||||
{
|
||||
double lum = i == 1 ? 0.01 : 0.5 + (i - 2) * (0.5 / 17.);
|
||||
uint8_t lumb = (uint8_t(lum * 255));
|
||||
|
||||
ConsolePal[i] = PalEntry(255, lumb, lumb, lumb);
|
||||
lumb = i * 255 / 17;
|
||||
SmallPal[i] = PalEntry(255, lumb, lumb, lumb);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -95,6 +108,7 @@ public:
|
|||
FHexFontChar(uint8_t *sourcedata, int swidth, int width, int height);
|
||||
|
||||
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
|
||||
int CopyPixels(FBitmap* bmp, int conversion);
|
||||
|
||||
protected:
|
||||
int SourceWidth;
|
||||
|
@ -159,12 +173,23 @@ TArray<uint8_t> FHexFontChar::CreatePalettedPixels(int)
|
|||
return Pixels;
|
||||
}
|
||||
|
||||
int FHexFontChar::CopyPixels(FBitmap* bmp, int conversion)
|
||||
{
|
||||
if (conversion == luminance) conversion = normal; // luminance images have no use as an RGB source.
|
||||
PalEntry* palette = hexdata.ConsolePal;
|
||||
auto ppix = CreatePalettedPixels(conversion);
|
||||
bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, palette, nullptr);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
class FHexFontChar2 : public FHexFontChar
|
||||
{
|
||||
public:
|
||||
FHexFontChar2(uint8_t *sourcedata, int swidth, int width, int height);
|
||||
|
||||
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
|
||||
int CopyPixels(FBitmap* bmp, int conversion);
|
||||
};
|
||||
|
||||
|
||||
|
@ -228,6 +253,15 @@ TArray<uint8_t> FHexFontChar2::CreatePalettedPixels(int)
|
|||
return Pixels;
|
||||
}
|
||||
|
||||
int FHexFontChar2::CopyPixels(FBitmap* bmp, int conversion)
|
||||
{
|
||||
if (conversion == luminance) conversion = normal; // luminance images have no use as an RGB source.
|
||||
PalEntry* palette = hexdata.SmallPal;
|
||||
auto ppix = CreatePalettedPixels(conversion);
|
||||
bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, palette, nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class FHexFont : public FFont
|
||||
|
@ -267,10 +301,9 @@ public:
|
|||
{
|
||||
auto offset = hexdata.glyphmap[i];
|
||||
int size = hexdata.glyphdata[offset] / 16;
|
||||
Chars[i - FirstChar].TranslatedPic = MakeGameTexture(new FImageTexture(new FHexFontChar(&hexdata.glyphdata[offset + 1], size, size * 9, 16)), nullptr, ETextureType::FontChar);
|
||||
Chars[i - FirstChar].OriginalPic = Chars[i - FirstChar].TranslatedPic;
|
||||
Chars[i - FirstChar].OriginalPic = MakeGameTexture(new FImageTexture(new FHexFontChar(&hexdata.glyphdata[offset + 1], size, size * 9, 16)), nullptr, ETextureType::FontChar);
|
||||
Chars[i - FirstChar].XMove = size * spacing;
|
||||
TexMan.AddGameTexture(Chars[i - FirstChar].TranslatedPic);
|
||||
TexMan.AddGameTexture(Chars[i - FirstChar].OriginalPic);
|
||||
}
|
||||
else Chars[i - FirstChar].XMove = spacing;
|
||||
|
||||
|
@ -285,18 +318,15 @@ public:
|
|||
|
||||
void LoadTranslations()
|
||||
{
|
||||
double luminosity[256];
|
||||
int minlum = hexdata.ConsolePal[1].r;
|
||||
int maxlum = hexdata.ConsolePal[17].r;
|
||||
|
||||
memset (PatchRemap, 0, 256);
|
||||
for (int i = 0; i < 18; i++)
|
||||
{
|
||||
// Create a gradient similar to the old console font.
|
||||
PatchRemap[i] = i;
|
||||
luminosity[i] = i == 1? 0.01 : 0.5 + (i-2) * (0.5 / 17.);
|
||||
Translations.Resize(NumTextColors);
|
||||
for (int i = 0; i < NumTextColors; i++)
|
||||
{
|
||||
if (i == CR_UNTRANSLATED) Translations[i] = 0;
|
||||
else Translations[i] = LuminosityTranslation(i * 2 + 1, minlum, maxlum);
|
||||
}
|
||||
ActiveColors = 18;
|
||||
|
||||
BuildTranslations (luminosity, nullptr, &TranslationParms[1][0], ActiveColors, nullptr);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -338,10 +368,9 @@ public:
|
|||
{
|
||||
auto offset = hexdata.glyphmap[i];
|
||||
int size = hexdata.glyphdata[offset] / 16;
|
||||
Chars[i - FirstChar].TranslatedPic = MakeGameTexture(new FImageTexture(new FHexFontChar2(&hexdata.glyphdata[offset + 1], size, 2 + size * 8, 18)), nullptr, ETextureType::FontChar);
|
||||
Chars[i - FirstChar].OriginalPic = Chars[i - FirstChar].TranslatedPic;
|
||||
Chars[i - FirstChar].OriginalPic = MakeGameTexture(new FImageTexture(new FHexFontChar2(&hexdata.glyphdata[offset + 1], size, 2 + size * 8, 18)), nullptr, ETextureType::FontChar);
|
||||
Chars[i - FirstChar].XMove = size * spacing;
|
||||
TexMan.AddGameTexture(Chars[i - FirstChar].TranslatedPic);
|
||||
TexMan.AddGameTexture(Chars[i - FirstChar].OriginalPic);
|
||||
}
|
||||
else Chars[i - FirstChar].XMove = spacing;
|
||||
|
||||
|
@ -356,71 +385,16 @@ public:
|
|||
|
||||
void LoadTranslations() override
|
||||
{
|
||||
double luminosity[256];
|
||||
int minlum = hexdata.SmallPal[1].r;
|
||||
int maxlum = hexdata.SmallPal[17].r;
|
||||
|
||||
memset(PatchRemap, 0, 256);
|
||||
for (int i = 0; i < 18; i++)
|
||||
Translations.Resize(NumTextColors);
|
||||
for (int i = 0; i < NumTextColors; i++)
|
||||
{
|
||||
// Create a gradient similar to the old console font.
|
||||
PatchRemap[i] = i;
|
||||
luminosity[i] = i / 17.;
|
||||
if (i == CR_UNTRANSLATED) Translations[i] = 0;
|
||||
else Translations[i] = LuminosityTranslation(i * 2, minlum, maxlum);
|
||||
}
|
||||
ActiveColors = 18;
|
||||
|
||||
BuildTranslations(luminosity, nullptr, &TranslationParms[0][0], ActiveColors, nullptr);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -90,26 +90,22 @@ public:
|
|||
void RecordAllTextureColors(uint32_t* usedcolors) override;
|
||||
|
||||
protected:
|
||||
void CheckFON1Chars (double *luminosity);
|
||||
void BuildTranslations2 ();
|
||||
void FixupPalette (uint8_t *identity, double *luminosity, const uint8_t *palette,
|
||||
bool rescale, PalEntry *out_palette);
|
||||
void CheckFON1Chars ();
|
||||
void FixupPalette (uint8_t *identity, const PalEntry *palette, int* minlum ,int* maxlum);
|
||||
void LoadTranslations ();
|
||||
void LoadFON1 (int lump, const uint8_t *data);
|
||||
void LoadFON2 (int lump, const uint8_t *data);
|
||||
void LoadBMF (int lump, const uint8_t *data);
|
||||
void CreateFontFromPic (FTextureID picnum);
|
||||
|
||||
static int BMFCompare(const void *a, const void *b);
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
FONT1,
|
||||
FONT2,
|
||||
BMFFONT
|
||||
} FontType;
|
||||
uint8_t PaletteData[768];
|
||||
PalEntry Palette[256];
|
||||
bool RescalePalette;
|
||||
int ActiveColors = -1;
|
||||
};
|
||||
|
||||
|
||||
|
@ -160,29 +156,6 @@ FSingleLumpFont::FSingleLumpFont (const char *name, int lump) : FFont(lump)
|
|||
FirstFont = this;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FSingleLumpFont :: CreateFontFromPic
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FSingleLumpFont::CreateFontFromPic (FTextureID picnum)
|
||||
{
|
||||
auto pic = TexMan.GetGameTexture(picnum);
|
||||
|
||||
FontHeight = (int)pic->GetDisplayHeight ();
|
||||
SpaceWidth = (int)pic->GetDisplayWidth ();
|
||||
GlobalKerning = 0;
|
||||
|
||||
FirstChar = LastChar = 'A';
|
||||
Chars.Resize(1);
|
||||
Chars[0].TranslatedPic = pic;
|
||||
Chars[0].OriginalPic = pic;
|
||||
|
||||
// Only one color range. Don't bother with the others.
|
||||
ActiveColors = 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FSingleLumpFont :: LoadTranslations
|
||||
|
@ -191,28 +164,21 @@ void FSingleLumpFont::CreateFontFromPic (FTextureID picnum)
|
|||
|
||||
void FSingleLumpFont::LoadTranslations()
|
||||
{
|
||||
double luminosity[256];
|
||||
uint8_t identity[256];
|
||||
PalEntry local_palette[256];
|
||||
bool useidentity = true;
|
||||
bool usepalette = false;
|
||||
const void* ranges;
|
||||
unsigned int count = LastChar - FirstChar + 1;
|
||||
int minlum, maxlum;
|
||||
|
||||
switch(FontType)
|
||||
{
|
||||
case FONT1:
|
||||
useidentity = false;
|
||||
ranges = &TranslationParms[1][0];
|
||||
CheckFON1Chars (luminosity);
|
||||
CheckFON1Chars();
|
||||
minlum = 1;
|
||||
maxlum = 255;
|
||||
break;
|
||||
|
||||
case BMFFONT:
|
||||
case FONT2:
|
||||
usepalette = true;
|
||||
FixupPalette (identity, luminosity, PaletteData, RescalePalette, local_palette);
|
||||
|
||||
ranges = &TranslationParms[0][0];
|
||||
FixupPalette (identity, Palette, &minlum, &maxlum);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -223,11 +189,16 @@ void FSingleLumpFont::LoadTranslations()
|
|||
|
||||
for(unsigned int i = 0;i < count;++i)
|
||||
{
|
||||
if(Chars[i].TranslatedPic)
|
||||
static_cast<FFontChar2*>(Chars[i].TranslatedPic->GetTexture()->GetImage())->SetSourceRemap(PatchRemap);
|
||||
if(Chars[i].OriginalPic)
|
||||
static_cast<FFontChar2*>(Chars[i].OriginalPic->GetTexture()->GetImage())->SetSourceRemap(Palette);
|
||||
}
|
||||
|
||||
BuildTranslations (luminosity, useidentity ? identity : nullptr, ranges, ActiveColors, usepalette ? local_palette : nullptr);
|
||||
Translations.Resize(NumTextColors);
|
||||
for (int i = 0; i < NumTextColors; i++)
|
||||
{
|
||||
if (i == CR_UNTRANSLATED) Translations[i] = 0;
|
||||
else Translations[i] = LuminosityTranslation(i * 2 + (FontType == FONT1 ? 1 : 0), minlum, maxlum);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -262,19 +233,20 @@ void FSingleLumpFont::LoadFON1 (int lump, const uint8_t *data)
|
|||
// Move the Windows-1252 characters to their proper place.
|
||||
for (int i = 0x80; i < 0xa0; i++)
|
||||
{
|
||||
if (win1252map[i-0x80] != i && Chars[i].TranslatedPic != nullptr && Chars[win1252map[i - 0x80]].TranslatedPic == nullptr)
|
||||
if (win1252map[i-0x80] != i && Chars[i].OriginalPic != nullptr && Chars[win1252map[i - 0x80]].OriginalPic == nullptr)
|
||||
{
|
||||
std::swap(Chars[i], Chars[win1252map[i - 0x80]]);
|
||||
}
|
||||
}
|
||||
Palette[0] = 0;
|
||||
for (int i = 1; i < 256; i++) Palette[i] = PalEntry(255, i, i, i);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FSingleLumpFont :: LoadFON2
|
||||
//
|
||||
// FON2 is used for everything but the console font. The console font should
|
||||
// probably use FON2 as well, but oh well.
|
||||
// FON2 is used for everything but the console font.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
@ -340,7 +312,11 @@ void FSingleLumpFont::LoadFON2 (int lump, const uint8_t *data)
|
|||
SpaceWidth = totalwidth * 2 / (3 * count);
|
||||
}
|
||||
|
||||
memcpy(PaletteData, palette, ActiveColors*3);
|
||||
Palette[0] = 0;
|
||||
for (int i = 1; i < ActiveColors; i++)
|
||||
{
|
||||
Palette[i] = PalEntry(255, palette[i * 3], palette[i * 3 + 1], palette[i * 3 + 2]);
|
||||
}
|
||||
|
||||
data_p = palette + ActiveColors*3;
|
||||
|
||||
|
@ -350,14 +326,12 @@ void FSingleLumpFont::LoadFON2 (int lump, const uint8_t *data)
|
|||
Chars[i].XMove = widths2[i];
|
||||
if (destSize <= 0)
|
||||
{
|
||||
Chars[i].TranslatedPic = nullptr;
|
||||
Chars[i].OriginalPic = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar2 (lump, int(data_p - data), widths2[i], FontHeight)), nullptr, ETextureType::FontChar);
|
||||
Chars[i].OriginalPic = Chars[i].TranslatedPic;
|
||||
TexMan.AddGameTexture(Chars[i].TranslatedPic);
|
||||
Chars[i].OriginalPic = MakeGameTexture(new FImageTexture(new FFontChar2 (lump, int(data_p - data), widths2[i], FontHeight)), nullptr, ETextureType::FontChar);
|
||||
TexMan.AddGameTexture(Chars[i].OriginalPic);
|
||||
do
|
||||
{
|
||||
int8_t code = *data_p++;
|
||||
|
@ -396,8 +370,6 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data)
|
|||
int numchars, count, totalwidth, nwidth;
|
||||
int infolen;
|
||||
int i, chari;
|
||||
uint8_t raw_palette[256*3];
|
||||
PalEntry sort_palette[256];
|
||||
|
||||
FontType = BMFFONT;
|
||||
FontHeight = data[5];
|
||||
|
@ -439,31 +411,14 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data)
|
|||
count = LastChar - FirstChar + 1;
|
||||
Chars.Resize(count);
|
||||
// BMF palettes are only six bits per component. Fix that.
|
||||
for (i = 0; i < ActiveColors*3; ++i)
|
||||
{
|
||||
raw_palette[i+3] = (data[17 + i] << 2) | (data[17 + i] >> 4);
|
||||
}
|
||||
ActiveColors++;
|
||||
|
||||
// Sort the palette by increasing brightness
|
||||
for (i = 0; i < ActiveColors; ++i)
|
||||
{
|
||||
PalEntry *pal = &sort_palette[i];
|
||||
pal->a = i; // Use alpha part to point back to original entry
|
||||
pal->r = raw_palette[i*3 + 0];
|
||||
pal->g = raw_palette[i*3 + 1];
|
||||
pal->b = raw_palette[i*3 + 2];
|
||||
int r = (data[17 + i * 3] << 2) | (data[17 + i * 3] >> 4);
|
||||
int g = (data[18 + i * 3] << 2) | (data[18 + i * 3] >> 4);
|
||||
int b = (data[19 + i * 3] << 2) | (data[19 + i * 3] >> 4);
|
||||
Palette[i + 1] = PalEntry(255, r, g, b); // entry 0 (transparent) is not stored in the font file.
|
||||
}
|
||||
qsort(sort_palette + 1, ActiveColors - 1, sizeof(PalEntry), BMFCompare);
|
||||
|
||||
// Create the PatchRemap table from the sorted "alpha" values.
|
||||
PatchRemap[0] = 0;
|
||||
for (i = 1; i < ActiveColors; ++i)
|
||||
{
|
||||
PatchRemap[sort_palette[i].a] = i;
|
||||
}
|
||||
|
||||
memcpy(PaletteData, raw_palette, 768);
|
||||
ActiveColors++;
|
||||
|
||||
// Now scan through the characters again, creating glyphs for each one.
|
||||
for (i = chari = 0; i < numchars; ++i, chari += 6 + chardata[chari+1] * chardata[chari+2])
|
||||
|
@ -489,7 +444,6 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data)
|
|||
-(int8_t)chardata[chari+3], // x offset
|
||||
-(int8_t)chardata[chari+4] // y offset
|
||||
)), nullptr, ETextureType::FontChar);
|
||||
Chars[chardata[chari] - FirstChar].TranslatedPic = tex;
|
||||
Chars[chardata[chari] - FirstChar].OriginalPic = tex;
|
||||
TexMan.AddGameTexture(tex);
|
||||
}
|
||||
|
@ -510,55 +464,33 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data)
|
|||
FixXMoves();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FSingleLumpFont :: BMFCompare STATIC
|
||||
//
|
||||
// Helper to sort BMF palettes.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FSingleLumpFont::BMFCompare(const void *a, const void *b)
|
||||
{
|
||||
const PalEntry *pa = (const PalEntry *)a;
|
||||
const PalEntry *pb = (const PalEntry *)b;
|
||||
|
||||
return (pa->r * 299 + pa->g * 587 + pa->b * 114) -
|
||||
(pb->r * 299 + pb->g * 587 + pb->b * 114);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FSingleLumpFont::CheckFON1Chars (double *luminosity)
|
||||
void FSingleLumpFont::CheckFON1Chars()
|
||||
{
|
||||
FileData memLump = fileSystem.ReadFile(Lump);
|
||||
const uint8_t* data = (const uint8_t*) memLump.GetMem();
|
||||
const uint8_t* data = (const uint8_t*)memLump.GetMem();
|
||||
const uint8_t* data_p;
|
||||
|
||||
uint8_t used[256], reverse[256];
|
||||
const uint8_t *data_p;
|
||||
int i, j;
|
||||
|
||||
memset (used, 0, 256);
|
||||
data_p = data + 8;
|
||||
|
||||
for (i = 0; i < 256; ++i)
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
int destSize = SpaceWidth * FontHeight;
|
||||
|
||||
if(!Chars[i].TranslatedPic)
|
||||
if (!Chars[i].OriginalPic)
|
||||
{
|
||||
Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar2 (Lump, int(data_p - data), SpaceWidth, FontHeight)), nullptr, ETextureType::FontChar);
|
||||
Chars[i].OriginalPic = Chars[i].TranslatedPic;
|
||||
Chars[i].OriginalPic = MakeGameTexture(new FImageTexture(new FFontChar2(Lump, int(data_p - data), SpaceWidth, FontHeight)), nullptr, ETextureType::FontChar);
|
||||
Chars[i].XMove = SpaceWidth;
|
||||
TexMan.AddGameTexture(Chars[i].TranslatedPic);
|
||||
TexMan.AddGameTexture(Chars[i].OriginalPic);
|
||||
}
|
||||
|
||||
// Advance to next char's data and count the used colors.
|
||||
|
@ -567,87 +499,52 @@ void FSingleLumpFont::CheckFON1Chars (double *luminosity)
|
|||
int8_t code = *data_p++;
|
||||
if (code >= 0)
|
||||
{
|
||||
destSize -= code+1;
|
||||
destSize -= code + 1;
|
||||
while (code-- >= 0)
|
||||
{
|
||||
used[*data_p++] = 1;
|
||||
data_p++;
|
||||
}
|
||||
}
|
||||
else if (code != -128)
|
||||
{
|
||||
used[*data_p++] = 1;
|
||||
data_p++;
|
||||
destSize -= 1 - code;
|
||||
}
|
||||
} while (destSize > 0);
|
||||
}
|
||||
|
||||
memset (PatchRemap, 0, 256);
|
||||
reverse[0] = 0;
|
||||
for (i = 1, j = 1; i < 256; ++i)
|
||||
{
|
||||
if (used[i])
|
||||
{
|
||||
reverse[j++] = i;
|
||||
}
|
||||
}
|
||||
for (i = 1; i < j; ++i)
|
||||
{
|
||||
PatchRemap[reverse[i]] = i;
|
||||
luminosity[i] = (reverse[i] - 1) / 254.0;
|
||||
}
|
||||
ActiveColors = j;
|
||||
ActiveColors = 256;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FSingleLumpFont :: FixupPalette
|
||||
//
|
||||
// Finds the best matches for the colors used by a FON2 font and sets up
|
||||
// some tables like SimpleTranslation.
|
||||
// Finds the best matches for the colors used by a FON2 font and finds thr
|
||||
// used luminosity range
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FSingleLumpFont::FixupPalette (uint8_t *identity, double *luminosity, const uint8_t *palette, bool rescale, PalEntry *out_palette)
|
||||
void FSingleLumpFont::FixupPalette (uint8_t *identity, const PalEntry *palette, int *pminlum, int *pmaxlum)
|
||||
{
|
||||
int i;
|
||||
double maxlum = 0.0;
|
||||
double minlum = 100000000.0;
|
||||
double diver;
|
||||
|
||||
identity[0] = 0;
|
||||
palette += 3; // Skip the transparent color
|
||||
|
||||
for (i = 1; i < ActiveColors; ++i, palette += 3)
|
||||
for (int i = 1; i < ActiveColors; ++i, palette ++)
|
||||
{
|
||||
int r = palette[0];
|
||||
int g = palette[1];
|
||||
int b = palette[2];
|
||||
int r = palette->r;
|
||||
int g = palette->g;
|
||||
int b = palette->b;
|
||||
double lum = r*0.299 + g*0.587 + b*0.114;
|
||||
identity[i] = ColorMatcher.Pick(r, g, b);
|
||||
luminosity[i] = lum;
|
||||
out_palette[i].r = r;
|
||||
out_palette[i].g = g;
|
||||
out_palette[i].b = b;
|
||||
out_palette[i].a = 255;
|
||||
if (lum > maxlum)
|
||||
maxlum = lum;
|
||||
if (lum < minlum)
|
||||
minlum = lum;
|
||||
if (lum > maxlum) maxlum = lum;
|
||||
if (lum < minlum) minlum = lum;
|
||||
}
|
||||
out_palette[0] = 0;
|
||||
|
||||
if (rescale)
|
||||
{
|
||||
diver = 1.0 / (maxlum - minlum);
|
||||
}
|
||||
else
|
||||
{
|
||||
diver = 1.0 / 255.0;
|
||||
}
|
||||
for (i = 1; i < ActiveColors; ++i)
|
||||
{
|
||||
luminosity[i] = (luminosity[i] - minlum) * diver;
|
||||
}
|
||||
if (pminlum) *pminlum = int(minlum);
|
||||
if (pmaxlum) *pmaxlum = int(maxlum);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -661,13 +558,11 @@ void FSingleLumpFont::FixupPalette (uint8_t *identity, double *luminosity, const
|
|||
|
||||
void FSingleLumpFont::RecordAllTextureColors(uint32_t* usedcolors)
|
||||
{
|
||||
double luminosity[256];
|
||||
uint8_t identity[256];
|
||||
PalEntry local_palette[256];
|
||||
|
||||
if (FontType == BMFFONT || FontType == FONT2)
|
||||
{
|
||||
FixupPalette(identity, luminosity, PaletteData, RescalePalette, local_palette);
|
||||
FixupPalette(identity, Palette, nullptr, nullptr);
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
if (identity[i] != 0) usedcolors[identity[i]]++;
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
FSinglePicFont(const char *picname);
|
||||
|
||||
// FFont interface
|
||||
FGameTexture *GetChar(int code, int translation, int *const width, bool *redirected = nullptr) const override;
|
||||
FGameTexture *GetChar(int code, int translation, int *const width) const override;
|
||||
int GetCharWidth (int code) const override;
|
||||
|
||||
protected:
|
||||
|
@ -79,7 +79,6 @@ FSinglePicFont::FSinglePicFont(const char *picname) :
|
|||
SpaceWidth = (int)pic->GetDisplayWidth();
|
||||
GlobalKerning = 0;
|
||||
FirstChar = LastChar = 'A';
|
||||
ActiveColors = 0;
|
||||
PicNum = picnum;
|
||||
|
||||
Next = FirstFont;
|
||||
|
@ -94,10 +93,9 @@ FSinglePicFont::FSinglePicFont(const char *picname) :
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FGameTexture *FSinglePicFont::GetChar (int code, int translation, int *const width, bool *redirected) const
|
||||
FGameTexture *FSinglePicFont::GetChar (int code, int translation, int *const width) const
|
||||
{
|
||||
*width = SpaceWidth;
|
||||
if (redirected) *redirected = false;
|
||||
if (code == 'a' || code == 'A')
|
||||
{
|
||||
return TexMan.GetGameTexture(PicNum, true);
|
||||
|
|
|
@ -108,26 +108,18 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FGameTexture
|
|||
Chars[i].OriginalPic = MakeGameTexture(pic->GetTexture(), nullptr, ETextureType::FontChar);
|
||||
Chars[i].OriginalPic->CopySize(pic, true);
|
||||
TexMan.AddGameTexture(Chars[i].OriginalPic);
|
||||
|
||||
if (!noTranslate)
|
||||
{
|
||||
Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar1 (charlumps[i]->GetTexture()->GetImage())), nullptr, ETextureType::FontChar);
|
||||
Chars[i].TranslatedPic->CopySize(charlumps[i], true);
|
||||
TexMan.AddGameTexture(Chars[i].TranslatedPic);
|
||||
}
|
||||
else Chars[i].TranslatedPic = Chars[i].OriginalPic;
|
||||
Chars[i].XMove = (int)Chars[i].TranslatedPic->GetDisplayWidth();
|
||||
if (sysCallbacks.FontCharCreated) sysCallbacks.FontCharCreated(pic, Chars[i].OriginalPic, Chars[i].TranslatedPic);
|
||||
Chars[i].XMove = (int)Chars[i].OriginalPic->GetDisplayWidth();
|
||||
if (sysCallbacks.FontCharCreated) sysCallbacks.FontCharCreated(pic, Chars[i].OriginalPic, Chars[i].OriginalPic);
|
||||
}
|
||||
else
|
||||
{
|
||||
Chars[i].TranslatedPic = nullptr;
|
||||
Chars[i].OriginalPic = nullptr;
|
||||
Chars[i].XMove = INT_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
// Special fonts normally don't have all characters so be careful here!
|
||||
if ('N'-first >= 0 && 'N'-first < count && Chars['N' - first].TranslatedPic != nullptr)
|
||||
if ('N'-first >= 0 && 'N'-first < count && Chars['N' - first].OriginalPic != nullptr)
|
||||
{
|
||||
SpaceWidth = (Chars['N' - first].XMove + 1) / 2;
|
||||
}
|
||||
|
@ -137,11 +129,6 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FGameTexture
|
|||
}
|
||||
|
||||
FixXMoves();
|
||||
|
||||
if (noTranslate)
|
||||
{
|
||||
ActiveColors = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -152,65 +139,56 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FGameTexture
|
|||
|
||||
void FSpecialFont::LoadTranslations()
|
||||
{
|
||||
int count = LastChar - FirstChar + 1;
|
||||
uint32_t usedcolors[256] = {};
|
||||
uint8_t identity[256];
|
||||
TArray<double> Luminosity;
|
||||
int TotalColors;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (Chars[i].TranslatedPic)
|
||||
{
|
||||
FFontChar1 *pic = static_cast<FFontChar1 *>(Chars[i].TranslatedPic->GetTexture()->GetImage());
|
||||
if (pic)
|
||||
{
|
||||
pic->SetSourceRemap(nullptr); // Force the FFontChar1 to return the same pixels as the base texture
|
||||
RecordTextureColors(pic, usedcolors);
|
||||
}
|
||||
}
|
||||
}
|
||||
FFont::LoadTranslations();
|
||||
|
||||
bool needsnotrans = false;
|
||||
// exclude the non-translated colors from the translation calculation
|
||||
for (i = 0; i < 256; i++)
|
||||
if (notranslate[i])
|
||||
usedcolors[i] = false;
|
||||
|
||||
TotalColors = ActiveColors = SimpleTranslation (usedcolors, PatchRemap, identity, Luminosity);
|
||||
|
||||
// Map all untranslated colors into the table of used colors
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
if (notranslate[i])
|
||||
{
|
||||
PatchRemap[i] = TotalColors;
|
||||
identity[TotalColors] = i;
|
||||
TotalColors++;
|
||||
needsnotrans = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
// If we have no non-translateable colors, we can use the base data as-is.
|
||||
if (!needsnotrans)
|
||||
{
|
||||
if(Chars[i].TranslatedPic)
|
||||
static_cast<FFontChar1 *>(Chars[i].TranslatedPic->GetTexture()->GetImage())->SetSourceRemap(PatchRemap);
|
||||
return;
|
||||
}
|
||||
|
||||
BuildTranslations(Luminosity.Data(), identity, &TranslationParms[0][0], TotalColors, nullptr, [=](FRemapTable* remap)
|
||||
{
|
||||
// add the untranslated colors to the Ranges tables
|
||||
if (ActiveColors < TotalColors)
|
||||
{
|
||||
for (int j = ActiveColors; j < TotalColors; ++j)
|
||||
{
|
||||
remap->Remap[j] = identity[j];
|
||||
remap->Palette[j] = GPalette.BaseColors[identity[j]];
|
||||
remap->Palette[j].a = 0xff;
|
||||
}
|
||||
}
|
||||
});
|
||||
// we only need to add special handling if there's colors that should not be translated.
|
||||
// Obviously 'notranslate' should only be used on data that uses the base palette, otherwise results are undefined!
|
||||
for (auto &trans : Translations)
|
||||
{
|
||||
if (!IsLuminosityTranslation(trans)) continue; // this should only happen for CR_UNTRANSLATED.
|
||||
|
||||
ActiveColors = TotalColors;
|
||||
FRemapTable remap(256);
|
||||
remap.ForFont = true;
|
||||
|
||||
uint8_t workpal[1024];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
workpal[i * 4 + 0] = GPalette.BaseColors[i].b;
|
||||
workpal[i * 4 + 1] = GPalette.BaseColors[i].g;
|
||||
workpal[i * 4 + 2] = GPalette.BaseColors[i].r;
|
||||
workpal[i * 4 + 3] = GPalette.BaseColors[i].a;
|
||||
}
|
||||
V_ApplyLuminosityTranslation(trans, workpal, 256);
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
if (!notranslate[i])
|
||||
{
|
||||
remap.Palette[i] = PalEntry(workpal[i * 4 + 3], workpal[i * 4 + 2], workpal[i * 4 + 1], workpal[i * 4 + 0]);
|
||||
remap.Remap[i] = ColorMatcher.Pick(remap.Palette[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
remap.Palette[i] = GPalette.BaseColors[i];
|
||||
remap.Remap[i] = i;
|
||||
}
|
||||
}
|
||||
trans = GPalette.StoreTranslation(TRANSLATION_Internal, &remap);
|
||||
}
|
||||
}
|
||||
|
||||
FFont *CreateSpecialFont (const char *name, int first, int count, FGameTexture **lumplist, const bool *notranslate, int lump, bool donttranslate)
|
||||
|
|
|
@ -586,6 +586,176 @@ EColorRange V_FindFontColor (FName name)
|
|||
return CR_UNTRANSLATED;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CreateLuminosityTranslationRanges
|
||||
//
|
||||
// Create universal remap ranges for hardware rendering.
|
||||
//
|
||||
//==========================================================================
|
||||
static PalEntry* paletteptr;
|
||||
|
||||
static void CreateLuminosityTranslationRanges()
|
||||
{
|
||||
paletteptr = (PalEntry*)ImageArena.Alloc(256 * ((NumTextColors * 2)) * sizeof(PalEntry));
|
||||
for (int l = 0; l < 2; l++)
|
||||
{
|
||||
auto parmstart = &TranslationParms[l][0];
|
||||
// Put the data into the image arena where it gets deleted with the rest of the texture data.
|
||||
for (int p = 0; p < NumTextColors; p++)
|
||||
{
|
||||
// Intended storage order is Range 1, variant 1 - Range 1, variant 2, Range 2, variant 1, and so on.
|
||||
// The storage of the ranges forces us to go through this differently...
|
||||
PalEntry* palette = paletteptr + p * 512 + l * 256;
|
||||
for (int v = 0; v < 256; v++)
|
||||
{
|
||||
palette[v].b = palette[v].g = palette[v].r = (uint8_t)v;
|
||||
}
|
||||
if (p != CR_UNTRANSLATED) // This table skips the untranslated entry. Do I need to say that the stored data format is garbage? >)
|
||||
{
|
||||
for (int v = 0; v < 256; v++)
|
||||
{
|
||||
// Find the color range that this luminosity value lies within.
|
||||
const TranslationParm* parms = parmstart - 1;
|
||||
do
|
||||
{
|
||||
parms++;
|
||||
if (parms->RangeStart <= v && parms->RangeEnd >= v)
|
||||
break;
|
||||
} while (parms[1].RangeStart > parms[0].RangeEnd);
|
||||
|
||||
// Linearly interpolate to find out which color this luminosity level gets.
|
||||
int rangev = ((v - parms->RangeStart) << 8) / (parms->RangeEnd - parms->RangeStart);
|
||||
int r = ((parms->Start[0] << 8) + rangev * (parms->End[0] - parms->Start[0])) >> 8; // red
|
||||
int g = ((parms->Start[1] << 8) + rangev * (parms->End[1] - parms->Start[1])) >> 8; // green
|
||||
int b = ((parms->Start[2] << 8) + rangev * (parms->End[2] - parms->Start[2])) >> 8; // blue
|
||||
palette[v].r = (uint8_t)clamp(r, 0, 255);
|
||||
palette[v].g = (uint8_t)clamp(g, 0, 255);
|
||||
palette[v].b = (uint8_t)clamp(b, 0, 255);
|
||||
}
|
||||
// Advance to the next color range.
|
||||
while (parmstart[1].RangeStart > parmstart[0].RangeEnd)
|
||||
{
|
||||
parmstart++;
|
||||
}
|
||||
parmstart++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// V_ApplyLuminosityTranslation
|
||||
//
|
||||
// Applies the translation to a bitmap for texture generation.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void V_ApplyLuminosityTranslation(int translation, uint8_t* pixel, int size)
|
||||
{
|
||||
int colorrange = (translation >> 16) & 0x3fff;
|
||||
if (colorrange >= NumTextColors * 2) return;
|
||||
int lum_min = (translation >> 8) & 0xff;
|
||||
int lum_max = translation & 0xff;
|
||||
int lum_range = (lum_max - lum_min + 1);
|
||||
PalEntry* remap = paletteptr + colorrange * 256;
|
||||
|
||||
for (int i = 0; i < size; i++, pixel += 4)
|
||||
{
|
||||
// we must also process the transparent pixels here to ensure proper filtering on the characters' edges.
|
||||
int gray = PalEntry(255, pixel[2], pixel[1], pixel[0]).Luminance();
|
||||
int lumadjust = (gray - lum_min) * 255 / lum_range;
|
||||
int index = clamp(lumadjust, 0, 255);
|
||||
PalEntry newcol = remap[index];
|
||||
// extend the range if we find colors outside what initial analysis provided.
|
||||
if (gray < lum_min)
|
||||
{
|
||||
newcol.r = newcol.r * gray / lum_min;
|
||||
newcol.g = newcol.g * gray / lum_min;
|
||||
newcol.b = newcol.b * gray / lum_min;
|
||||
}
|
||||
else if (gray > lum_max)
|
||||
{
|
||||
newcol.r = clamp(newcol.r * gray / lum_max, 0, 255);
|
||||
newcol.g = clamp(newcol.g * gray / lum_max, 0, 255);
|
||||
newcol.b = clamp(newcol.b * gray / lum_max, 0, 255);
|
||||
}
|
||||
pixel[0] = newcol.b;
|
||||
pixel[1] = newcol.g;
|
||||
pixel[2] = newcol.r;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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
|
||||
|
@ -676,6 +846,7 @@ EColorRange V_ParseFontColor (const uint8_t *&color_value, int normalcolor, int
|
|||
|
||||
void V_InitFonts()
|
||||
{
|
||||
CreateLuminosityTranslationRanges();
|
||||
V_InitCustomFonts();
|
||||
|
||||
FFont *CreateHexLumpFont(const char *fontname, int lump);
|
||||
|
@ -827,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)
|
||||
|
|
|
@ -97,7 +97,7 @@ public:
|
|||
FFont (const char *fontname, const char *nametemplate, const char *filetemplate, int first, int count, int base, int fdlump, int spacewidth=-1, bool notranslate = false, bool iwadonly = false, bool doomtemplate = false, GlyphSet *baseGlpyphs = nullptr);
|
||||
virtual ~FFont ();
|
||||
|
||||
virtual FGameTexture *GetChar (int code, int translation, int *const width, bool *redirected = nullptr) const;
|
||||
virtual FGameTexture *GetChar (int code, int translation, int *const width) const;
|
||||
virtual int GetCharWidth (int code) const;
|
||||
int GetColorTranslation (EColorRange range, PalEntry *color = nullptr) const;
|
||||
int GetLump() const { return Lump; }
|
||||
|
@ -128,22 +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 BuildTranslations (const double *luminosity, const uint8_t *identity,
|
||||
const void *ranges, int total_colors, const PalEntry *palette, std::function<void(FRemapTable*)> post = nullptr);
|
||||
void FixXMoves();
|
||||
|
||||
static int SimpleTranslation (uint32_t *colorsused, uint8_t *translation,
|
||||
uint8_t *identity, TArray<double> &Luminosity);
|
||||
|
||||
void ReadSheetFont(TArray<FolderEntry> &folderdata, int width, int height, const DVector2 &Scale);
|
||||
|
||||
EFontType Type = EFontType::Unknown;
|
||||
|
@ -161,12 +156,10 @@ protected:
|
|||
bool forceremap = false;
|
||||
struct CharData
|
||||
{
|
||||
FGameTexture *TranslatedPic = nullptr; // Texture for use with font translations.
|
||||
FGameTexture *OriginalPic = nullptr; // Texture for use with CR_UNTRANSLATED or font colorization.
|
||||
FGameTexture *OriginalPic = nullptr;
|
||||
int XMove = INT_MIN;
|
||||
};
|
||||
TArray<CharData> Chars;
|
||||
int ActiveColors = -1;
|
||||
TArray<int> Translations;
|
||||
uint8_t PatchRemap[256];
|
||||
|
||||
|
@ -192,6 +185,8 @@ EColorRange V_ParseFontColor (const uint8_t *&color_value, int normalcolor, int
|
|||
FFont *V_GetFont(const char *fontname, const char *fontlumpname = nullptr);
|
||||
void V_InitFontColors();
|
||||
char* CleanseString(char* str);
|
||||
void V_ApplyLuminosityTranslation(int translation, uint8_t* pixel, int size);
|
||||
void V_LoadTranslations();
|
||||
class FBitmap;
|
||||
|
||||
|
||||
|
|
|
@ -198,6 +198,11 @@ public:
|
|||
return Pitch;
|
||||
}
|
||||
|
||||
int GetBufferSize() const
|
||||
{
|
||||
return Pitch * Height;
|
||||
}
|
||||
|
||||
const uint8_t *GetPixels() const
|
||||
{
|
||||
return data;
|
||||
|
|
|
@ -41,48 +41,6 @@
|
|||
#include "fontchars.h"
|
||||
#include "engineerrors.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FFontChar1 :: FFontChar1
|
||||
//
|
||||
// Used by fonts made from textures.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FFontChar1::FFontChar1 (FImageSource *sourcelump)
|
||||
: BaseTexture(sourcelump), SourceRemap (nullptr)
|
||||
{
|
||||
// now copy all the properties from the base texture
|
||||
assert(BaseTexture != nullptr);
|
||||
CopySize(*BaseTexture);
|
||||
bUseGamePalette = false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FFontChar1 :: GetPixels
|
||||
//
|
||||
// Render style is not relevant for fonts. This must not use it!
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
TArray<uint8_t> FFontChar1::CreatePalettedPixels (int)
|
||||
{
|
||||
// Make the texture as normal, then remap it so that all the colors
|
||||
// are at the low end of the palette
|
||||
// Why? It only creates unnecessary work!
|
||||
auto Pixels = BaseTexture->GetPalettedPixels(normal);
|
||||
|
||||
if (SourceRemap)
|
||||
{
|
||||
for (int x = 0; x < Width*Height; ++x)
|
||||
{
|
||||
Pixels[x] = SourceRemap[Pixels[x]];
|
||||
}
|
||||
}
|
||||
return Pixels;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FFontChar2 :: FFontChar2
|
||||
|
@ -91,8 +49,8 @@ TArray<uint8_t> FFontChar1::CreatePalettedPixels (int)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FFontChar2::FFontChar2 (int sourcelump, int sourcepos, int width, int height, int leftofs, int topofs)
|
||||
: SourceLump (sourcelump), SourcePos (sourcepos), SourceRemap(nullptr)
|
||||
FFontChar2::FFontChar2(int sourcelump, int sourcepos, int width, int height, int leftofs, int topofs)
|
||||
: SourceLump(sourcelump), SourcePos(sourcepos)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
|
@ -100,17 +58,6 @@ FFontChar2::FFontChar2 (int sourcelump, int sourcepos, int width, int height, in
|
|||
TopOffset = topofs;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FFontChar2 :: SetSourceRemap
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFontChar2::SetSourceRemap(const uint8_t *sourceremap)
|
||||
{
|
||||
SourceRemap = sourceremap;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FFontChar2 :: Get8BitPixels
|
||||
|
@ -121,7 +68,7 @@ void FFontChar2::SetSourceRemap(const uint8_t *sourceremap)
|
|||
|
||||
TArray<uint8_t> FFontChar2::CreatePalettedPixels(int)
|
||||
{
|
||||
auto lump = fileSystem.OpenFileReader (SourceLump);
|
||||
auto lump = fileSystem.OpenFileReader(SourceLump);
|
||||
int destSize = Width * Height;
|
||||
uint8_t max = 255;
|
||||
bool rle = true;
|
||||
|
@ -129,23 +76,23 @@ TArray<uint8_t> FFontChar2::CreatePalettedPixels(int)
|
|||
// This is to "fix" bad fonts
|
||||
{
|
||||
uint8_t buff[16];
|
||||
lump.Read (buff, 4);
|
||||
lump.Read(buff, 4);
|
||||
if (buff[3] == '2')
|
||||
{
|
||||
lump.Read (buff, 7);
|
||||
lump.Read(buff, 7);
|
||||
max = buff[6];
|
||||
lump.Seek (SourcePos - 11, FileReader::SeekCur);
|
||||
lump.Seek(SourcePos - 11, FileReader::SeekCur);
|
||||
}
|
||||
else if (buff[3] == 0x1A)
|
||||
{
|
||||
lump.Read(buff, 13);
|
||||
max = buff[12] - 1;
|
||||
lump.Seek (SourcePos - 17, FileReader::SeekCur);
|
||||
lump.Seek(SourcePos - 17, FileReader::SeekCur);
|
||||
rle = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
lump.Seek (SourcePos - 4, FileReader::SeekCur);
|
||||
lump.Seek(SourcePos - 4, FileReader::SeekCur);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,7 +100,7 @@ TArray<uint8_t> FFontChar2::CreatePalettedPixels(int)
|
|||
|
||||
int runlen = 0, setlen = 0;
|
||||
uint8_t setval = 0; // Shut up, GCC!
|
||||
uint8_t *dest_p = Pixels.Data();
|
||||
uint8_t* dest_p = Pixels.Data();
|
||||
int dest_adv = Height;
|
||||
int dest_rew = destSize - 1;
|
||||
|
||||
|
@ -166,11 +113,7 @@ TArray<uint8_t> FFontChar2::CreatePalettedPixels(int)
|
|||
if (runlen != 0)
|
||||
{
|
||||
uint8_t color = lump.ReadUInt8();
|
||||
color = MIN (color, max);
|
||||
if (SourceRemap != nullptr)
|
||||
{
|
||||
color = SourceRemap[color];
|
||||
}
|
||||
color = MIN(color, max);
|
||||
*dest_p = color;
|
||||
dest_p += dest_adv;
|
||||
x--;
|
||||
|
@ -194,11 +137,7 @@ TArray<uint8_t> FFontChar2::CreatePalettedPixels(int)
|
|||
{
|
||||
uint8_t color = lump.ReadUInt8();
|
||||
setlen = (-code) + 1;
|
||||
setval = MIN (color, max);
|
||||
if (SourceRemap != nullptr)
|
||||
{
|
||||
setval = SourceRemap[setval];
|
||||
}
|
||||
setval = MIN(color, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -216,10 +155,6 @@ TArray<uint8_t> FFontChar2::CreatePalettedPixels(int)
|
|||
{
|
||||
color = max;
|
||||
}
|
||||
if (SourceRemap != nullptr)
|
||||
{
|
||||
color = SourceRemap[color];
|
||||
}
|
||||
*dest_p = color;
|
||||
dest_p += dest_adv;
|
||||
}
|
||||
|
@ -230,10 +165,17 @@ TArray<uint8_t> FFontChar2::CreatePalettedPixels(int)
|
|||
if (destSize < 0)
|
||||
{
|
||||
char name[9];
|
||||
fileSystem.GetFileShortName (name, SourceLump);
|
||||
fileSystem.GetFileShortName(name, SourceLump);
|
||||
name[8] = 0;
|
||||
I_FatalError ("The font %s is corrupt", name);
|
||||
I_FatalError("The font %s is corrupt", name);
|
||||
}
|
||||
return Pixels;
|
||||
}
|
||||
|
||||
int FFontChar2::CopyPixels(FBitmap* bmp, int conversion)
|
||||
{
|
||||
if (conversion == luminance) conversion = normal; // luminance images have no use as an RGB source.
|
||||
auto ppix = CreatePalettedPixels(conversion);
|
||||
bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, SourceRemap, nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,5 @@
|
|||
|
||||
|
||||
// This is a font character that loads a texture and recolors it.
|
||||
class FFontChar1 : public FImageSource
|
||||
{
|
||||
public:
|
||||
FFontChar1 (FImageSource *sourcelump);
|
||||
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
|
||||
void SetSourceRemap(const uint8_t *sourceremap) { SourceRemap = sourceremap; }
|
||||
const uint8_t *ResetSourceRemap() { auto p = SourceRemap; SourceRemap = nullptr; return p; }
|
||||
FImageSource *GetBase() const { return BaseTexture; }
|
||||
|
||||
protected:
|
||||
|
||||
FImageSource *BaseTexture;
|
||||
const uint8_t *SourceRemap;
|
||||
};
|
||||
|
||||
// This is a font character that reads RLE compressed data.
|
||||
class FFontChar2 : public FImageSource
|
||||
{
|
||||
|
@ -23,10 +7,15 @@ public:
|
|||
FFontChar2 (int sourcelump, int sourcepos, int width, int height, int leftofs=0, int topofs=0);
|
||||
|
||||
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
|
||||
void SetSourceRemap(const uint8_t *sourceremap);
|
||||
int CopyPixels(FBitmap* bmp, int conversion);
|
||||
|
||||
void SetSourceRemap(const PalEntry* sourceremap)
|
||||
{
|
||||
SourceRemap = sourceremap;
|
||||
}
|
||||
|
||||
protected:
|
||||
int SourceLump;
|
||||
int SourcePos;
|
||||
const uint8_t *SourceRemap;
|
||||
const PalEntry *SourceRemap;
|
||||
};
|
||||
|
|
|
@ -65,8 +65,16 @@ private:
|
|||
// This is needed for allowing the client to allocate slots that aren't matched to a palette, e.g. Build's indexed variants.
|
||||
if (translation >= 0)
|
||||
{
|
||||
auto remap = GPalette.TranslationToTable(translation);
|
||||
translation = remap == nullptr ? 0 : remap->Index;
|
||||
if (!IsLuminosityTranslation(translation))
|
||||
{
|
||||
auto remap = GPalette.TranslationToTable(translation);
|
||||
translation = remap == nullptr ? 0 : remap->Index;
|
||||
}
|
||||
else
|
||||
{
|
||||
// only needs to preserve the color range plus an identifier for marking this a luminosity translation.
|
||||
translation = ((translation >> 16) & 0x3fff) | 0xff0000;
|
||||
}
|
||||
}
|
||||
else translation &= ~0x7fffffff;
|
||||
|
||||
|
|
|
@ -321,6 +321,7 @@ bool FTexture::ProcessData(unsigned char* buffer, int w, int h, bool ispatch)
|
|||
// Initializes the buffer for the texture data
|
||||
//
|
||||
//===========================================================================
|
||||
void V_ApplyLuminosityTranslation(int translation, uint8_t *buffer, int size);
|
||||
|
||||
FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
|
||||
{
|
||||
|
@ -356,7 +357,7 @@ FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
|
|||
buffer = new unsigned char[W * (H + 1) * 4];
|
||||
memset(buffer, 0, W * (H + 1) * 4);
|
||||
|
||||
auto remap = translation <= 0 ? nullptr : GPalette.TranslationToTable(translation);
|
||||
auto remap = translation <= 0 || IsLuminosityTranslation(translation) ? nullptr : GPalette.TranslationToTable(translation);
|
||||
if (remap && remap->Inactive) remap = nullptr;
|
||||
if (remap) translation = remap->Index;
|
||||
FBitmap bmp(buffer, W * 4, W, H);
|
||||
|
@ -364,6 +365,10 @@ FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
|
|||
int trans;
|
||||
auto Pixels = GetBgraBitmap(remap ? remap->Palette : nullptr, &trans);
|
||||
bmp.Blit(exx, exx, Pixels);
|
||||
if (IsLuminosityTranslation(translation))
|
||||
{
|
||||
V_ApplyLuminosityTranslation(translation, buffer, W * H);
|
||||
}
|
||||
|
||||
if (remap == nullptr)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue