mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-10 23:01:59 +00:00
- split the FFont base class into its own file.
This commit is contained in:
parent
0963156c0a
commit
95e62e91bb
4 changed files with 867 additions and 797 deletions
|
@ -1128,6 +1128,7 @@ set (PCH_SOURCES
|
||||||
gamedata/fonts/singlelumpfont.cpp
|
gamedata/fonts/singlelumpfont.cpp
|
||||||
gamedata/fonts/singlepicfont.cpp
|
gamedata/fonts/singlepicfont.cpp
|
||||||
gamedata/fonts/specialfont.cpp
|
gamedata/fonts/specialfont.cpp
|
||||||
|
gamedata/fonts/font.cpp
|
||||||
gamedata/fonts/v_font.cpp
|
gamedata/fonts/v_font.cpp
|
||||||
gamedata/fonts/v_text.cpp
|
gamedata/fonts/v_text.cpp
|
||||||
gamedata/p_xlat.cpp
|
gamedata/p_xlat.cpp
|
||||||
|
|
836
src/gamedata/fonts/font.cpp
Normal file
836
src/gamedata/fonts/font.cpp
Normal file
|
@ -0,0 +1,836 @@
|
||||||
|
/*
|
||||||
|
** v_font.cpp
|
||||||
|
** Font management
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 1998-2016 Randy Heit
|
||||||
|
** Copyright 2005-2019 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
// HEADER FILES ------------------------------------------------------------
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "templates.h"
|
||||||
|
#include "doomtype.h"
|
||||||
|
#include "m_swap.h"
|
||||||
|
#include "v_font.h"
|
||||||
|
#include "v_video.h"
|
||||||
|
#include "w_wad.h"
|
||||||
|
#include "gi.h"
|
||||||
|
#include "cmdlib.h"
|
||||||
|
#include "sc_man.h"
|
||||||
|
#include "hu_stuff.h"
|
||||||
|
#include "gstrings.h"
|
||||||
|
#include "v_text.h"
|
||||||
|
#include "vm.h"
|
||||||
|
#include "image.h"
|
||||||
|
#include "utf8.h"
|
||||||
|
#include "textures/formats/fontchars.h"
|
||||||
|
|
||||||
|
#include "fontinternals.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: FFont
|
||||||
|
//
|
||||||
|
// Loads a multi-texture font.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FFont::FFont (const char *name, const char *nametemplate, const char *filetemplate, int lfirst, int lcount, int start, int fdlump, int spacewidth, bool notranslate)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
FTextureID lump;
|
||||||
|
char buffer[12];
|
||||||
|
int maxyoffs;
|
||||||
|
bool doomtemplate = (nametemplate && (gameinfo.gametype & GAME_DoomChex)) ? strncmp (nametemplate, "STCFN", 5) == 0 : false;
|
||||||
|
DVector2 Scale = { 1, 1 };
|
||||||
|
|
||||||
|
noTranslate = notranslate;
|
||||||
|
Lump = fdlump;
|
||||||
|
FontHeight = 0;
|
||||||
|
GlobalKerning = false;
|
||||||
|
FontName = name;
|
||||||
|
Next = FirstFont;
|
||||||
|
FirstFont = this;
|
||||||
|
Cursor = '_';
|
||||||
|
ActiveColors = 0;
|
||||||
|
SpaceWidth = 0;
|
||||||
|
FontHeight = 0;
|
||||||
|
uint8_t pp = 0;
|
||||||
|
for (auto &p : PatchRemap) p = pp++;
|
||||||
|
translateUntranslated = false;
|
||||||
|
|
||||||
|
maxyoffs = 0;
|
||||||
|
|
||||||
|
TMap<int, FTexture*> charMap;
|
||||||
|
int minchar = INT_MAX;
|
||||||
|
int maxchar = INT_MIN;
|
||||||
|
|
||||||
|
// Read the font's configuration.
|
||||||
|
// This will not be done for the default fonts, because they are not atomic and the default content does not need it.
|
||||||
|
|
||||||
|
TArray<FolderEntry> folderdata;
|
||||||
|
if (filetemplate != nullptr)
|
||||||
|
{
|
||||||
|
FStringf path("fonts/%s/", filetemplate);
|
||||||
|
// If a name template is given, collect data from all resource files.
|
||||||
|
// For anything else, each folder is being treated as an atomic, self-contained unit and mixing from different glyph sets is blocked.
|
||||||
|
Wads.GetLumpsInFolder(path, folderdata, nametemplate == nullptr);
|
||||||
|
|
||||||
|
if (nametemplate == nullptr)
|
||||||
|
{
|
||||||
|
// Only take font.inf from the actual folder we are processing but not from an older folder that may have been superseded.
|
||||||
|
FStringf infpath("fonts/%s/font.inf", filetemplate);
|
||||||
|
|
||||||
|
unsigned index = folderdata.FindEx([=](const FolderEntry &entry)
|
||||||
|
{
|
||||||
|
return infpath.CompareNoCase(entry.name) == 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (index < folderdata.Size())
|
||||||
|
{
|
||||||
|
FScanner sc;
|
||||||
|
sc.OpenLumpNum(folderdata[index].lumpnum);
|
||||||
|
while (sc.GetToken())
|
||||||
|
{
|
||||||
|
sc.TokenMustBe(TK_Identifier);
|
||||||
|
if (sc.Compare("Kerning"))
|
||||||
|
{
|
||||||
|
sc.MustGetValue(false);
|
||||||
|
GlobalKerning = sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("Scale"))
|
||||||
|
{
|
||||||
|
sc.MustGetValue(true);
|
||||||
|
Scale.Y = Scale.X = sc.Float;
|
||||||
|
if (sc.CheckToken(','))
|
||||||
|
{
|
||||||
|
sc.MustGetValue(true);
|
||||||
|
Scale.Y = sc.Float;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sc.Compare("SpaceWidth"))
|
||||||
|
{
|
||||||
|
sc.MustGetValue(false);
|
||||||
|
SpaceWidth = sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("FontHeight"))
|
||||||
|
{
|
||||||
|
sc.MustGetValue(false);
|
||||||
|
FontHeight = sc.Number;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("Translationtype"))
|
||||||
|
{
|
||||||
|
sc.MustGetToken(TK_Identifier);
|
||||||
|
if (sc.Compare("console"))
|
||||||
|
{
|
||||||
|
TranslationType = 1;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("standard"))
|
||||||
|
{
|
||||||
|
TranslationType = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sc.ScriptError("Unknown translation type %s", sc.String);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (nametemplate != nullptr)
|
||||||
|
{
|
||||||
|
for (i = 0; i < lcount; i++)
|
||||||
|
{
|
||||||
|
int position = '!' + i;
|
||||||
|
mysnprintf(buffer, countof(buffer), nametemplate, i + start);
|
||||||
|
|
||||||
|
lump = TexMan.CheckForTexture(buffer, ETextureType::MiscPatch);
|
||||||
|
if (doomtemplate && lump.isValid() && i + start == 121)
|
||||||
|
{ // HACKHACK: Don't load STCFN121 in doom(2), because
|
||||||
|
// it's not really a lower-case 'y' but a '|'.
|
||||||
|
// Because a lot of wads with their own font seem to foolishly
|
||||||
|
// copy STCFN121 and make it a '|' themselves, wads must
|
||||||
|
// provide STCFN120 (x) and STCFN122 (z) for STCFN121 to load as a 'y'.
|
||||||
|
if (!TexMan.CheckForTexture("STCFN120", ETextureType::MiscPatch).isValid() ||
|
||||||
|
!TexMan.CheckForTexture("STCFN122", ETextureType::MiscPatch).isValid())
|
||||||
|
{
|
||||||
|
// insert the incorrectly named '|' graphic in its correct position.
|
||||||
|
position = 124;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lump.isValid())
|
||||||
|
{
|
||||||
|
if (position < minchar) minchar = position;
|
||||||
|
if (position > maxchar) maxchar = position;
|
||||||
|
charMap.Insert(position, TexMan.GetTexture(lump));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (folderdata.Size() > 0)
|
||||||
|
{
|
||||||
|
// all valid lumps must be named with a hex number that represents its Unicode character index.
|
||||||
|
for (auto &entry : folderdata)
|
||||||
|
{
|
||||||
|
char *endp;
|
||||||
|
auto base = ExtractFileBase(entry.name);
|
||||||
|
auto position = strtoll(base.GetChars(), &endp, 16);
|
||||||
|
if ((*endp == 0 || (*endp == '.' && position >= '!' && position < 0xffff)))
|
||||||
|
{
|
||||||
|
auto lump = TexMan.CheckForTexture(entry.name, ETextureType::MiscPatch);
|
||||||
|
if (lump.isValid())
|
||||||
|
{
|
||||||
|
if ((int)position < minchar) minchar = (int)position;
|
||||||
|
if ((int)position > maxchar) maxchar = (int)position;
|
||||||
|
auto tex = TexMan.GetTexture(lump);
|
||||||
|
tex->SetScale(Scale);
|
||||||
|
charMap.Insert((int)position, tex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FirstChar = minchar;
|
||||||
|
LastChar = maxchar;
|
||||||
|
auto count = maxchar - minchar + 1;
|
||||||
|
Chars.Resize(count);
|
||||||
|
int fontheight = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
auto lump = charMap.CheckKey(FirstChar + i);
|
||||||
|
if (lump != nullptr)
|
||||||
|
{
|
||||||
|
FTexture *pic = *lump;
|
||||||
|
if (pic != nullptr)
|
||||||
|
{
|
||||||
|
int height = pic->GetDisplayHeight();
|
||||||
|
int yoffs = pic->GetDisplayTopOffset();
|
||||||
|
|
||||||
|
if (yoffs > maxyoffs)
|
||||||
|
{
|
||||||
|
maxyoffs = yoffs;
|
||||||
|
}
|
||||||
|
height += abs(yoffs);
|
||||||
|
if (height > fontheight)
|
||||||
|
{
|
||||||
|
fontheight = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pic->SetUseType(ETextureType::FontChar);
|
||||||
|
if (!noTranslate)
|
||||||
|
{
|
||||||
|
Chars[i].OriginalPic = pic;
|
||||||
|
Chars[i].TranslatedPic = new FImageTexture(new FFontChar1 (pic->GetImage()), "");
|
||||||
|
Chars[i].TranslatedPic->CopySize(pic);
|
||||||
|
Chars[i].TranslatedPic->SetUseType(ETextureType::FontChar);
|
||||||
|
TexMan.AddTexture(Chars[i].TranslatedPic);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Chars[i].TranslatedPic = pic;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chars[i].XMove = Chars[i].TranslatedPic->GetDisplayWidth();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Chars[i].TranslatedPic = nullptr;
|
||||||
|
Chars[i].XMove = INT_MIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SpaceWidth == 0) // An explicit override from the .inf file must always take precedence
|
||||||
|
{
|
||||||
|
if (spacewidth != -1)
|
||||||
|
{
|
||||||
|
SpaceWidth = spacewidth;
|
||||||
|
}
|
||||||
|
else if ('N'-FirstChar >= 0 && 'N'-FirstChar < count && Chars['N' - FirstChar].TranslatedPic != nullptr)
|
||||||
|
{
|
||||||
|
SpaceWidth = (Chars['N' - FirstChar].XMove + 1) / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SpaceWidth = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (FontHeight == 0) FontHeight = fontheight;
|
||||||
|
|
||||||
|
FixXMoves();
|
||||||
|
|
||||||
|
if (!noTranslate) LoadTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: ~FFont
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FFont::~FFont ()
|
||||||
|
{
|
||||||
|
FFont **prev = &FirstFont;
|
||||||
|
FFont *font = *prev;
|
||||||
|
|
||||||
|
while (font != nullptr && font != this)
|
||||||
|
{
|
||||||
|
prev = &font->Next;
|
||||||
|
font = *prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font != nullptr)
|
||||||
|
{
|
||||||
|
*prev = font->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: FindFont
|
||||||
|
//
|
||||||
|
// Searches for the named font in the list of loaded fonts, returning the
|
||||||
|
// font if it was found. The disk is not checked if it cannot be found.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FFont *FFont::FindFont (FName name)
|
||||||
|
{
|
||||||
|
if (name == NAME_None)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
FFont *font = FirstFont;
|
||||||
|
|
||||||
|
while (font != nullptr)
|
||||||
|
{
|
||||||
|
if (font->FontName == name) return font;
|
||||||
|
font = font->Next;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// RecordTextureColors
|
||||||
|
//
|
||||||
|
// Given a 256 entry buffer, sets every entry that corresponds to a color
|
||||||
|
// used by the texture to 1.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void RecordTextureColors (FImageSource *pic, uint8_t *usedcolors)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
|
||||||
|
auto pixels = pic->GetPalettedPixels(false);
|
||||||
|
auto size = pic->GetWidth() * pic->GetHeight();
|
||||||
|
|
||||||
|
for(x = 0;x < size; x++)
|
||||||
|
{
|
||||||
|
usedcolors[pixels[x]]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// compare
|
||||||
|
//
|
||||||
|
// Used for sorting colors by brightness.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
static int compare (const void *arg1, const void *arg2)
|
||||||
|
{
|
||||||
|
if (RPART(GPalette.BaseColors[*((uint8_t *)arg1)]) * 299 +
|
||||||
|
GPART(GPalette.BaseColors[*((uint8_t *)arg1)]) * 587 +
|
||||||
|
BPART(GPalette.BaseColors[*((uint8_t *)arg1)]) * 114 <
|
||||||
|
RPART(GPalette.BaseColors[*((uint8_t *)arg2)]) * 299 +
|
||||||
|
GPART(GPalette.BaseColors[*((uint8_t *)arg2)]) * 587 +
|
||||||
|
BPART(GPalette.BaseColors[*((uint8_t *)arg2)]) * 114)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int FFont::SimpleTranslation (uint8_t *colorsused, uint8_t *translation, uint8_t *reverse, TArray<double> &Luminosity)
|
||||||
|
{
|
||||||
|
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[0] = 0.0; // [BL] Prevent uninitalized memory
|
||||||
|
max = 0.0;
|
||||||
|
min = 100000000.0;
|
||||||
|
for (i = 1; i < j; 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];
|
||||||
|
}
|
||||||
|
diver = 1.0 / (max - min);
|
||||||
|
for (i = 1; i < j; i++)
|
||||||
|
{
|
||||||
|
Luminosity[i] = (Luminosity[i] - min) * diver;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
const TranslationParm *parmstart = (const TranslationParm *)ranges;
|
||||||
|
|
||||||
|
FRemapTable remap(total_colors);
|
||||||
|
|
||||||
|
// Create different translations for different color ranges
|
||||||
|
Ranges.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remap = Ranges[0];
|
||||||
|
}
|
||||||
|
Ranges.Push(remap);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(parmstart->RangeStart >= 0);
|
||||||
|
|
||||||
|
remap.Remap[0] = 0;
|
||||||
|
remap.Palette[0] = 0;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
Ranges.Push(remap);
|
||||||
|
|
||||||
|
// Advance to the next color range.
|
||||||
|
while (parmstart[1].RangeStart > parmstart[0].RangeEnd)
|
||||||
|
{
|
||||||
|
parmstart++;
|
||||||
|
}
|
||||||
|
parmstart++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: GetColorTranslation
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FRemapTable *FFont::GetColorTranslation (EColorRange range, PalEntry *color) const
|
||||||
|
{
|
||||||
|
if (noTranslate)
|
||||||
|
{
|
||||||
|
PalEntry retcolor = PalEntry(255, 255, 255, 255);
|
||||||
|
if (range >= 0 && range < NumTextColors && range != CR_UNTRANSLATED)
|
||||||
|
{
|
||||||
|
retcolor = TranslationColors[range];
|
||||||
|
retcolor.a = 255;
|
||||||
|
}
|
||||||
|
if (color != nullptr) *color = retcolor;
|
||||||
|
}
|
||||||
|
if (ActiveColors == 0)
|
||||||
|
return nullptr;
|
||||||
|
else if (range >= NumTextColors)
|
||||||
|
range = CR_UNTRANSLATED;
|
||||||
|
//if (range == CR_UNTRANSLATED && !translateUntranslated) return nullptr;
|
||||||
|
return &Ranges[range];
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: GetCharCode
|
||||||
|
//
|
||||||
|
// If the character code is in the font, returns it. If it is not, but it
|
||||||
|
// is lowercase and has an uppercase variant present, return that. Otherwise
|
||||||
|
// return -1.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int FFont::GetCharCode(int code, bool needpic) const
|
||||||
|
{
|
||||||
|
if (code < 0 && code >= -128)
|
||||||
|
{
|
||||||
|
// regular chars turn negative when the 8th bit is set.
|
||||||
|
code &= 255;
|
||||||
|
}
|
||||||
|
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr))
|
||||||
|
{
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
int originalcode = code;
|
||||||
|
int newcode;
|
||||||
|
|
||||||
|
// Try stripping accents from accented characters. This may repeat to allow multi-step fallbacks.
|
||||||
|
while ((newcode = stripaccent(code)) != code)
|
||||||
|
{
|
||||||
|
code = newcode;
|
||||||
|
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr))
|
||||||
|
{
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myislower(code))
|
||||||
|
{
|
||||||
|
int upper = upperforlower[code];
|
||||||
|
// Stripping accents did not help - now try uppercase for lowercase
|
||||||
|
if (upper != code) return GetCharCode(upper, needpic);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: GetChar
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FTexture *FFont::GetChar (int code, int translation, int *const width, bool *redirected) const
|
||||||
|
{
|
||||||
|
code = GetCharCode(code, false);
|
||||||
|
int xmove = SpaceWidth;
|
||||||
|
|
||||||
|
if (code >= 0)
|
||||||
|
{
|
||||||
|
code -= FirstChar;
|
||||||
|
xmove = Chars[code].XMove;
|
||||||
|
if (Chars[code].TranslatedPic == nullptr)
|
||||||
|
{
|
||||||
|
code = GetCharCode(code + FirstChar, true);
|
||||||
|
if (code >= 0)
|
||||||
|
{
|
||||||
|
code -= FirstChar;
|
||||||
|
xmove = Chars[code].XMove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (width != nullptr)
|
||||||
|
{
|
||||||
|
*width = xmove;
|
||||||
|
}
|
||||||
|
if (code < 0) return nullptr;
|
||||||
|
|
||||||
|
|
||||||
|
if (translation == CR_UNTRANSLATED)
|
||||||
|
{
|
||||||
|
bool redirect = Chars[code].OriginalPic && Chars[code].OriginalPic != Chars[code].TranslatedPic;
|
||||||
|
if (redirected) *redirected = redirect;
|
||||||
|
if (redirect)
|
||||||
|
{
|
||||||
|
assert(Chars[code].OriginalPic->UseType == ETextureType::FontChar);
|
||||||
|
return Chars[code].OriginalPic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (redirected) *redirected = false;
|
||||||
|
assert(Chars[code].TranslatedPic->UseType == ETextureType::FontChar);
|
||||||
|
return Chars[code].TranslatedPic;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: GetCharWidth
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int FFont::GetCharWidth (int code) const
|
||||||
|
{
|
||||||
|
code = GetCharCode(code, false);
|
||||||
|
return (code < 0) ? SpaceWidth : Chars[code - FirstChar].XMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
double GetBottomAlignOffset(FFont *font, int c)
|
||||||
|
{
|
||||||
|
int w;
|
||||||
|
FTexture *tex_zero = font->GetChar('0', CR_UNDEFINED, &w);
|
||||||
|
FTexture *texc = font->GetChar(c, CR_UNDEFINED, &w);
|
||||||
|
double offset = 0;
|
||||||
|
if (texc) offset += texc->GetDisplayTopOffsetDouble();
|
||||||
|
if (tex_zero) offset += -tex_zero->GetDisplayTopOffsetDouble() + tex_zero->GetDisplayHeightDouble();
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Find string width using this font
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int FFont::StringWidth(const uint8_t *string) const
|
||||||
|
{
|
||||||
|
int w = 0;
|
||||||
|
int maxw = 0;
|
||||||
|
|
||||||
|
while (*string)
|
||||||
|
{
|
||||||
|
auto chr = GetCharFromString(string);
|
||||||
|
if (chr == TEXTCOLOR_ESCAPE)
|
||||||
|
{
|
||||||
|
// We do not need to check for UTF-8 in here.
|
||||||
|
if (*string == '[')
|
||||||
|
{
|
||||||
|
while (*string != '\0' && *string != ']')
|
||||||
|
{
|
||||||
|
++string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*string != '\0')
|
||||||
|
{
|
||||||
|
++string;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (chr == '\n')
|
||||||
|
{
|
||||||
|
if (w > maxw)
|
||||||
|
maxw = w;
|
||||||
|
w = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
w += GetCharWidth(chr) + GlobalKerning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MAX(maxw, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: LoadTranslations
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FFont::LoadTranslations()
|
||||||
|
{
|
||||||
|
unsigned int count = LastChar - FirstChar + 1;
|
||||||
|
uint8_t usedcolors[256], identity[256];
|
||||||
|
TArray<double> Luminosity;
|
||||||
|
|
||||||
|
memset (usedcolors, 0, 256);
|
||||||
|
for (unsigned int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (Chars[i].TranslatedPic)
|
||||||
|
{
|
||||||
|
FFontChar1 *pic = static_cast<FFontChar1 *>(Chars[i].TranslatedPic->GetImage());
|
||||||
|
if (pic)
|
||||||
|
{
|
||||||
|
pic->SetSourceRemap(nullptr); // Force the FFontChar1 to return the same pixels as the base texture
|
||||||
|
RecordTextureColors(pic, usedcolors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fixme: This needs to build a translation based on the source palette, not some intermediate 'ordered' table.
|
||||||
|
|
||||||
|
ActiveColors = SimpleTranslation (usedcolors, PatchRemap, identity, Luminosity);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if(Chars[i].TranslatedPic)
|
||||||
|
static_cast<FFontChar1 *>(Chars[i].TranslatedPic->GetImage())->SetSourceRemap(PatchRemap);
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildTranslations (Luminosity.Data(), identity, &TranslationParms[TranslationType][0], ActiveColors, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: FFont - default constructor
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FFont::FFont (int lump)
|
||||||
|
{
|
||||||
|
Lump = lump;
|
||||||
|
FontName = NAME_None;
|
||||||
|
Cursor = '_';
|
||||||
|
noTranslate = false;
|
||||||
|
uint8_t pp = 0;
|
||||||
|
for (auto &p : PatchRemap) p = pp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFont :: FixXMoves
|
||||||
|
//
|
||||||
|
// If a font has gaps in its characters, set the missing characters'
|
||||||
|
// XMoves to either SpaceWidth or the unaccented or uppercase variant's
|
||||||
|
// XMove. Missing XMoves must be initialized with INT_MIN beforehand.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FFont::FixXMoves()
|
||||||
|
{
|
||||||
|
for (int i = 0; i <= LastChar - FirstChar; ++i)
|
||||||
|
{
|
||||||
|
if (Chars[i].XMove == INT_MIN)
|
||||||
|
{
|
||||||
|
// Try an uppercase character.
|
||||||
|
if (myislower(i + FirstChar))
|
||||||
|
{
|
||||||
|
int upper = upperforlower[FirstChar + i];
|
||||||
|
if (upper >= FirstChar && upper <= LastChar )
|
||||||
|
{
|
||||||
|
Chars[i].XMove = Chars[upper - FirstChar].XMove;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Try an unnaccented character.
|
||||||
|
int noaccent = stripaccent(i + FirstChar);
|
||||||
|
if (noaccent != i + FirstChar)
|
||||||
|
{
|
||||||
|
noaccent -= FirstChar;
|
||||||
|
if (noaccent >= 0)
|
||||||
|
{
|
||||||
|
Chars[i].XMove = Chars[noaccent].XMove;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Chars[i].XMove = SpaceWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,33 @@ struct TranslationParm
|
||||||
uint8_t End[3]; // End color for this range
|
uint8_t End[3]; // End color for this range
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TempParmInfo
|
||||||
|
{
|
||||||
|
unsigned int StartParm[2];
|
||||||
|
unsigned int ParmLen[2];
|
||||||
|
int Index;
|
||||||
|
};
|
||||||
|
struct TempColorInfo
|
||||||
|
{
|
||||||
|
FName Name;
|
||||||
|
unsigned int ParmInfo;
|
||||||
|
PalEntry LogColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TranslationMap
|
||||||
|
{
|
||||||
|
FName Name;
|
||||||
|
int Number;
|
||||||
|
};
|
||||||
|
|
||||||
extern TArray<TranslationParm> TranslationParms[2];
|
extern TArray<TranslationParm> TranslationParms[2];
|
||||||
|
extern TArray<TranslationMap> TranslationLookup;
|
||||||
|
extern TArray<PalEntry> TranslationColors;
|
||||||
|
extern uint16_t lowerforupper[65536];
|
||||||
|
extern uint16_t upperforlower[65536];
|
||||||
|
|
||||||
|
class FImageSource;
|
||||||
|
|
||||||
void RecordTextureColors (FImageSource *pic, uint8_t *usedcolors);
|
void RecordTextureColors (FImageSource *pic, uint8_t *usedcolors);
|
||||||
|
bool myislower(int code);
|
||||||
|
int stripaccent(int code);
|
||||||
|
|
|
@ -65,25 +65,6 @@
|
||||||
// TYPES -------------------------------------------------------------------
|
// TYPES -------------------------------------------------------------------
|
||||||
void RecordTextureColors (FImageSource *pic, uint8_t *colorsused);
|
void RecordTextureColors (FImageSource *pic, uint8_t *colorsused);
|
||||||
|
|
||||||
struct TempParmInfo
|
|
||||||
{
|
|
||||||
unsigned int StartParm[2];
|
|
||||||
unsigned int ParmLen[2];
|
|
||||||
int Index;
|
|
||||||
};
|
|
||||||
struct TempColorInfo
|
|
||||||
{
|
|
||||||
FName Name;
|
|
||||||
unsigned int ParmInfo;
|
|
||||||
PalEntry LogColor;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TranslationMap
|
|
||||||
{
|
|
||||||
FName Name;
|
|
||||||
int Number;
|
|
||||||
};
|
|
||||||
|
|
||||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||||
|
|
||||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||||
|
@ -104,8 +85,8 @@ int NumTextColors;
|
||||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||||
|
|
||||||
TArray<TranslationParm> TranslationParms[2];
|
TArray<TranslationParm> TranslationParms[2];
|
||||||
static TArray<TranslationMap> TranslationLookup;
|
TArray<TranslationMap> TranslationLookup;
|
||||||
static TArray<PalEntry> TranslationColors;
|
TArray<PalEntry> TranslationColors;
|
||||||
|
|
||||||
// CODE --------------------------------------------------------------------
|
// CODE --------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -803,7 +784,7 @@ void InitLowerUpper()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool myislower(int code)
|
bool myislower(int code)
|
||||||
{
|
{
|
||||||
if (code >= 0 && code < 65536) return islowermap[code];
|
if (code >= 0 && code < 65536) return islowermap[code];
|
||||||
return false;
|
return false;
|
||||||
|
@ -811,7 +792,7 @@ static bool myislower(int code)
|
||||||
|
|
||||||
// Returns a character without an accent mark (or one with a similar looking accent in some cases where direct support is unlikely.
|
// Returns a character without an accent mark (or one with a similar looking accent in some cases where direct support is unlikely.
|
||||||
|
|
||||||
static int stripaccent(int code)
|
int stripaccent(int code)
|
||||||
{
|
{
|
||||||
if (code < 0x8a)
|
if (code < 0x8a)
|
||||||
return code;
|
return code;
|
||||||
|
@ -926,780 +907,6 @@ FFont *V_GetFont(const char *name, const char *fontlumpname)
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: FFont
|
|
||||||
//
|
|
||||||
// Loads a multi-texture font.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FFont::FFont (const char *name, const char *nametemplate, const char *filetemplate, int lfirst, int lcount, int start, int fdlump, int spacewidth, bool notranslate)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
FTextureID lump;
|
|
||||||
char buffer[12];
|
|
||||||
int maxyoffs;
|
|
||||||
bool doomtemplate = (nametemplate && (gameinfo.gametype & GAME_DoomChex)) ? strncmp (nametemplate, "STCFN", 5) == 0 : false;
|
|
||||||
DVector2 Scale = { 1, 1 };
|
|
||||||
|
|
||||||
noTranslate = notranslate;
|
|
||||||
Lump = fdlump;
|
|
||||||
FontHeight = 0;
|
|
||||||
GlobalKerning = false;
|
|
||||||
FontName = name;
|
|
||||||
Next = FirstFont;
|
|
||||||
FirstFont = this;
|
|
||||||
Cursor = '_';
|
|
||||||
ActiveColors = 0;
|
|
||||||
SpaceWidth = 0;
|
|
||||||
FontHeight = 0;
|
|
||||||
uint8_t pp = 0;
|
|
||||||
for (auto &p : PatchRemap) p = pp++;
|
|
||||||
translateUntranslated = false;
|
|
||||||
|
|
||||||
maxyoffs = 0;
|
|
||||||
|
|
||||||
TMap<int, FTexture*> charMap;
|
|
||||||
int minchar = INT_MAX;
|
|
||||||
int maxchar = INT_MIN;
|
|
||||||
|
|
||||||
// Read the font's configuration.
|
|
||||||
// This will not be done for the default fonts, because they are not atomic and the default content does not need it.
|
|
||||||
|
|
||||||
TArray<FolderEntry> folderdata;
|
|
||||||
if (filetemplate != nullptr)
|
|
||||||
{
|
|
||||||
FStringf path("fonts/%s/", filetemplate);
|
|
||||||
// If a name template is given, collect data from all resource files.
|
|
||||||
// For anything else, each folder is being treated as an atomic, self-contained unit and mixing from different glyph sets is blocked.
|
|
||||||
Wads.GetLumpsInFolder(path, folderdata, nametemplate == nullptr);
|
|
||||||
|
|
||||||
if (nametemplate == nullptr)
|
|
||||||
{
|
|
||||||
// Only take font.inf from the actual folder we are processing but not from an older folder that may have been superseded.
|
|
||||||
FStringf infpath("fonts/%s/font.inf", filetemplate);
|
|
||||||
|
|
||||||
unsigned index = folderdata.FindEx([=](const FolderEntry &entry)
|
|
||||||
{
|
|
||||||
return infpath.CompareNoCase(entry.name) == 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (index < folderdata.Size())
|
|
||||||
{
|
|
||||||
FScanner sc;
|
|
||||||
sc.OpenLumpNum(folderdata[index].lumpnum);
|
|
||||||
while (sc.GetToken())
|
|
||||||
{
|
|
||||||
sc.TokenMustBe(TK_Identifier);
|
|
||||||
if (sc.Compare("Kerning"))
|
|
||||||
{
|
|
||||||
sc.MustGetValue(false);
|
|
||||||
GlobalKerning = sc.Number;
|
|
||||||
}
|
|
||||||
else if (sc.Compare("Scale"))
|
|
||||||
{
|
|
||||||
sc.MustGetValue(true);
|
|
||||||
Scale.Y = Scale.X = sc.Float;
|
|
||||||
if (sc.CheckToken(','))
|
|
||||||
{
|
|
||||||
sc.MustGetValue(true);
|
|
||||||
Scale.Y = sc.Float;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (sc.Compare("SpaceWidth"))
|
|
||||||
{
|
|
||||||
sc.MustGetValue(false);
|
|
||||||
SpaceWidth = sc.Number;
|
|
||||||
}
|
|
||||||
else if (sc.Compare("FontHeight"))
|
|
||||||
{
|
|
||||||
sc.MustGetValue(false);
|
|
||||||
FontHeight = sc.Number;
|
|
||||||
}
|
|
||||||
else if (sc.Compare("Translationtype"))
|
|
||||||
{
|
|
||||||
sc.MustGetToken(TK_Identifier);
|
|
||||||
if (sc.Compare("console"))
|
|
||||||
{
|
|
||||||
TranslationType = 1;
|
|
||||||
}
|
|
||||||
else if (sc.Compare("standard"))
|
|
||||||
{
|
|
||||||
TranslationType = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sc.ScriptError("Unknown translation type %s", sc.String);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (nametemplate != nullptr)
|
|
||||||
{
|
|
||||||
for (i = 0; i < lcount; i++)
|
|
||||||
{
|
|
||||||
int position = '!' + i;
|
|
||||||
mysnprintf(buffer, countof(buffer), nametemplate, i + start);
|
|
||||||
|
|
||||||
lump = TexMan.CheckForTexture(buffer, ETextureType::MiscPatch);
|
|
||||||
if (doomtemplate && lump.isValid() && i + start == 121)
|
|
||||||
{ // HACKHACK: Don't load STCFN121 in doom(2), because
|
|
||||||
// it's not really a lower-case 'y' but a '|'.
|
|
||||||
// Because a lot of wads with their own font seem to foolishly
|
|
||||||
// copy STCFN121 and make it a '|' themselves, wads must
|
|
||||||
// provide STCFN120 (x) and STCFN122 (z) for STCFN121 to load as a 'y'.
|
|
||||||
if (!TexMan.CheckForTexture("STCFN120", ETextureType::MiscPatch).isValid() ||
|
|
||||||
!TexMan.CheckForTexture("STCFN122", ETextureType::MiscPatch).isValid())
|
|
||||||
{
|
|
||||||
// insert the incorrectly named '|' graphic in its correct position.
|
|
||||||
position = 124;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lump.isValid())
|
|
||||||
{
|
|
||||||
if (position < minchar) minchar = position;
|
|
||||||
if (position > maxchar) maxchar = position;
|
|
||||||
charMap.Insert(position, TexMan.GetTexture(lump));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (folderdata.Size() > 0)
|
|
||||||
{
|
|
||||||
// all valid lumps must be named with a hex number that represents its Unicode character index.
|
|
||||||
for (auto &entry : folderdata)
|
|
||||||
{
|
|
||||||
char *endp;
|
|
||||||
auto base = ExtractFileBase(entry.name);
|
|
||||||
auto position = strtoll(base.GetChars(), &endp, 16);
|
|
||||||
if ((*endp == 0 || (*endp == '.' && position >= '!' && position < 0xffff)))
|
|
||||||
{
|
|
||||||
auto lump = TexMan.CheckForTexture(entry.name, ETextureType::MiscPatch);
|
|
||||||
if (lump.isValid())
|
|
||||||
{
|
|
||||||
if ((int)position < minchar) minchar = (int)position;
|
|
||||||
if ((int)position > maxchar) maxchar = (int)position;
|
|
||||||
auto tex = TexMan.GetTexture(lump);
|
|
||||||
tex->SetScale(Scale);
|
|
||||||
charMap.Insert((int)position, tex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FirstChar = minchar;
|
|
||||||
LastChar = maxchar;
|
|
||||||
auto count = maxchar - minchar + 1;
|
|
||||||
Chars.Resize(count);
|
|
||||||
int fontheight = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
auto lump = charMap.CheckKey(FirstChar + i);
|
|
||||||
if (lump != nullptr)
|
|
||||||
{
|
|
||||||
FTexture *pic = *lump;
|
|
||||||
if (pic != nullptr)
|
|
||||||
{
|
|
||||||
int height = pic->GetDisplayHeight();
|
|
||||||
int yoffs = pic->GetDisplayTopOffset();
|
|
||||||
|
|
||||||
if (yoffs > maxyoffs)
|
|
||||||
{
|
|
||||||
maxyoffs = yoffs;
|
|
||||||
}
|
|
||||||
height += abs(yoffs);
|
|
||||||
if (height > fontheight)
|
|
||||||
{
|
|
||||||
fontheight = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pic->SetUseType(ETextureType::FontChar);
|
|
||||||
if (!noTranslate)
|
|
||||||
{
|
|
||||||
Chars[i].OriginalPic = pic;
|
|
||||||
Chars[i].TranslatedPic = new FImageTexture(new FFontChar1 (pic->GetImage()), "");
|
|
||||||
Chars[i].TranslatedPic->CopySize(pic);
|
|
||||||
Chars[i].TranslatedPic->SetUseType(ETextureType::FontChar);
|
|
||||||
TexMan.AddTexture(Chars[i].TranslatedPic);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Chars[i].TranslatedPic = pic;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chars[i].XMove = Chars[i].TranslatedPic->GetDisplayWidth();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Chars[i].TranslatedPic = nullptr;
|
|
||||||
Chars[i].XMove = INT_MIN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SpaceWidth == 0) // An explicit override from the .inf file must always take precedence
|
|
||||||
{
|
|
||||||
if (spacewidth != -1)
|
|
||||||
{
|
|
||||||
SpaceWidth = spacewidth;
|
|
||||||
}
|
|
||||||
else if ('N'-FirstChar >= 0 && 'N'-FirstChar < count && Chars['N' - FirstChar].TranslatedPic != nullptr)
|
|
||||||
{
|
|
||||||
SpaceWidth = (Chars['N' - FirstChar].XMove + 1) / 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SpaceWidth = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (FontHeight == 0) FontHeight = fontheight;
|
|
||||||
|
|
||||||
FixXMoves();
|
|
||||||
|
|
||||||
if (!noTranslate) LoadTranslations();
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: ~FFont
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FFont::~FFont ()
|
|
||||||
{
|
|
||||||
FFont **prev = &FirstFont;
|
|
||||||
FFont *font = *prev;
|
|
||||||
|
|
||||||
while (font != nullptr && font != this)
|
|
||||||
{
|
|
||||||
prev = &font->Next;
|
|
||||||
font = *prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (font != nullptr)
|
|
||||||
{
|
|
||||||
*prev = font->Next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: FindFont
|
|
||||||
//
|
|
||||||
// Searches for the named font in the list of loaded fonts, returning the
|
|
||||||
// font if it was found. The disk is not checked if it cannot be found.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FFont *FFont::FindFont (FName name)
|
|
||||||
{
|
|
||||||
if (name == NAME_None)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
FFont *font = FirstFont;
|
|
||||||
|
|
||||||
while (font != nullptr)
|
|
||||||
{
|
|
||||||
if (font->FontName == name) return font;
|
|
||||||
font = font->Next;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// RecordTextureColors
|
|
||||||
//
|
|
||||||
// Given a 256 entry buffer, sets every entry that corresponds to a color
|
|
||||||
// used by the texture to 1.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void RecordTextureColors (FImageSource *pic, uint8_t *usedcolors)
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
|
|
||||||
auto pixels = pic->GetPalettedPixels(false);
|
|
||||||
auto size = pic->GetWidth() * pic->GetHeight();
|
|
||||||
|
|
||||||
for(x = 0;x < size; x++)
|
|
||||||
{
|
|
||||||
usedcolors[pixels[x]]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// compare
|
|
||||||
//
|
|
||||||
// Used for sorting colors by brightness.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
static int compare (const void *arg1, const void *arg2)
|
|
||||||
{
|
|
||||||
if (RPART(GPalette.BaseColors[*((uint8_t *)arg1)]) * 299 +
|
|
||||||
GPART(GPalette.BaseColors[*((uint8_t *)arg1)]) * 587 +
|
|
||||||
BPART(GPalette.BaseColors[*((uint8_t *)arg1)]) * 114 <
|
|
||||||
RPART(GPalette.BaseColors[*((uint8_t *)arg2)]) * 299 +
|
|
||||||
GPART(GPalette.BaseColors[*((uint8_t *)arg2)]) * 587 +
|
|
||||||
BPART(GPalette.BaseColors[*((uint8_t *)arg2)]) * 114)
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FFont::SimpleTranslation (uint8_t *colorsused, uint8_t *translation, uint8_t *reverse, TArray<double> &Luminosity)
|
|
||||||
{
|
|
||||||
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[0] = 0.0; // [BL] Prevent uninitalized memory
|
|
||||||
max = 0.0;
|
|
||||||
min = 100000000.0;
|
|
||||||
for (i = 1; i < j; 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];
|
|
||||||
}
|
|
||||||
diver = 1.0 / (max - min);
|
|
||||||
for (i = 1; i < j; i++)
|
|
||||||
{
|
|
||||||
Luminosity[i] = (Luminosity[i] - min) * diver;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
const TranslationParm *parmstart = (const TranslationParm *)ranges;
|
|
||||||
|
|
||||||
FRemapTable remap(total_colors);
|
|
||||||
|
|
||||||
// Create different translations for different color ranges
|
|
||||||
Ranges.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
remap = Ranges[0];
|
|
||||||
}
|
|
||||||
Ranges.Push(remap);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(parmstart->RangeStart >= 0);
|
|
||||||
|
|
||||||
remap.Remap[0] = 0;
|
|
||||||
remap.Palette[0] = 0;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
Ranges.Push(remap);
|
|
||||||
|
|
||||||
// Advance to the next color range.
|
|
||||||
while (parmstart[1].RangeStart > parmstart[0].RangeEnd)
|
|
||||||
{
|
|
||||||
parmstart++;
|
|
||||||
}
|
|
||||||
parmstart++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: GetColorTranslation
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FRemapTable *FFont::GetColorTranslation (EColorRange range, PalEntry *color) const
|
|
||||||
{
|
|
||||||
if (noTranslate)
|
|
||||||
{
|
|
||||||
PalEntry retcolor = PalEntry(255, 255, 255, 255);
|
|
||||||
if (range >= 0 && range < NumTextColors && range != CR_UNTRANSLATED)
|
|
||||||
{
|
|
||||||
retcolor = TranslationColors[range];
|
|
||||||
retcolor.a = 255;
|
|
||||||
}
|
|
||||||
if (color != nullptr) *color = retcolor;
|
|
||||||
}
|
|
||||||
if (ActiveColors == 0)
|
|
||||||
return nullptr;
|
|
||||||
else if (range >= NumTextColors)
|
|
||||||
range = CR_UNTRANSLATED;
|
|
||||||
//if (range == CR_UNTRANSLATED && !translateUntranslated) return nullptr;
|
|
||||||
return &Ranges[range];
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: GetCharCode
|
|
||||||
//
|
|
||||||
// If the character code is in the font, returns it. If it is not, but it
|
|
||||||
// is lowercase and has an uppercase variant present, return that. Otherwise
|
|
||||||
// return -1.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FFont::GetCharCode(int code, bool needpic) const
|
|
||||||
{
|
|
||||||
if (code < 0 && code >= -128)
|
|
||||||
{
|
|
||||||
// regular chars turn negative when the 8th bit is set.
|
|
||||||
code &= 255;
|
|
||||||
}
|
|
||||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr))
|
|
||||||
{
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
int originalcode = code;
|
|
||||||
int newcode;
|
|
||||||
|
|
||||||
// Try stripping accents from accented characters. This may repeat to allow multi-step fallbacks.
|
|
||||||
while ((newcode = stripaccent(code)) != code)
|
|
||||||
{
|
|
||||||
code = newcode;
|
|
||||||
if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr))
|
|
||||||
{
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (myislower(code))
|
|
||||||
{
|
|
||||||
int upper = upperforlower[code];
|
|
||||||
// Stripping accents did not help - now try uppercase for lowercase
|
|
||||||
if (upper != code) return GetCharCode(upper, needpic);
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: GetChar
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FTexture *FFont::GetChar (int code, int translation, int *const width, bool *redirected) const
|
|
||||||
{
|
|
||||||
code = GetCharCode(code, false);
|
|
||||||
int xmove = SpaceWidth;
|
|
||||||
|
|
||||||
if (code >= 0)
|
|
||||||
{
|
|
||||||
code -= FirstChar;
|
|
||||||
xmove = Chars[code].XMove;
|
|
||||||
if (Chars[code].TranslatedPic == nullptr)
|
|
||||||
{
|
|
||||||
code = GetCharCode(code + FirstChar, true);
|
|
||||||
if (code >= 0)
|
|
||||||
{
|
|
||||||
code -= FirstChar;
|
|
||||||
xmove = Chars[code].XMove;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (width != nullptr)
|
|
||||||
{
|
|
||||||
*width = xmove;
|
|
||||||
}
|
|
||||||
if (code < 0) return nullptr;
|
|
||||||
|
|
||||||
|
|
||||||
if (translation == CR_UNTRANSLATED)
|
|
||||||
{
|
|
||||||
bool redirect = Chars[code].OriginalPic && Chars[code].OriginalPic != Chars[code].TranslatedPic;
|
|
||||||
if (redirected) *redirected = redirect;
|
|
||||||
if (redirect)
|
|
||||||
{
|
|
||||||
assert(Chars[code].OriginalPic->UseType == ETextureType::FontChar);
|
|
||||||
return Chars[code].OriginalPic;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (redirected) *redirected = false;
|
|
||||||
assert(Chars[code].TranslatedPic->UseType == ETextureType::FontChar);
|
|
||||||
return Chars[code].TranslatedPic;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: GetCharWidth
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FFont::GetCharWidth (int code) const
|
|
||||||
{
|
|
||||||
code = GetCharCode(code, false);
|
|
||||||
return (code < 0) ? SpaceWidth : Chars[code - FirstChar].XMove;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
double GetBottomAlignOffset(FFont *font, int c)
|
|
||||||
{
|
|
||||||
int w;
|
|
||||||
FTexture *tex_zero = font->GetChar('0', CR_UNDEFINED, &w);
|
|
||||||
FTexture *texc = font->GetChar(c, CR_UNDEFINED, &w);
|
|
||||||
double offset = 0;
|
|
||||||
if (texc) offset += texc->GetDisplayTopOffsetDouble();
|
|
||||||
if (tex_zero) offset += -tex_zero->GetDisplayTopOffsetDouble() + tex_zero->GetDisplayHeightDouble();
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// Find string width using this font
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int FFont::StringWidth(const uint8_t *string) const
|
|
||||||
{
|
|
||||||
int w = 0;
|
|
||||||
int maxw = 0;
|
|
||||||
|
|
||||||
while (*string)
|
|
||||||
{
|
|
||||||
auto chr = GetCharFromString(string);
|
|
||||||
if (chr == TEXTCOLOR_ESCAPE)
|
|
||||||
{
|
|
||||||
// We do not need to check for UTF-8 in here.
|
|
||||||
if (*string == '[')
|
|
||||||
{
|
|
||||||
while (*string != '\0' && *string != ']')
|
|
||||||
{
|
|
||||||
++string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*string != '\0')
|
|
||||||
{
|
|
||||||
++string;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (chr == '\n')
|
|
||||||
{
|
|
||||||
if (w > maxw)
|
|
||||||
maxw = w;
|
|
||||||
w = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
w += GetCharWidth(chr) + GlobalKerning;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MAX(maxw, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: LoadTranslations
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void FFont::LoadTranslations()
|
|
||||||
{
|
|
||||||
unsigned int count = LastChar - FirstChar + 1;
|
|
||||||
uint8_t usedcolors[256], identity[256];
|
|
||||||
TArray<double> Luminosity;
|
|
||||||
|
|
||||||
memset (usedcolors, 0, 256);
|
|
||||||
for (unsigned int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
if (Chars[i].TranslatedPic)
|
|
||||||
{
|
|
||||||
FFontChar1 *pic = static_cast<FFontChar1 *>(Chars[i].TranslatedPic->GetImage());
|
|
||||||
if (pic)
|
|
||||||
{
|
|
||||||
pic->SetSourceRemap(nullptr); // Force the FFontChar1 to return the same pixels as the base texture
|
|
||||||
RecordTextureColors(pic, usedcolors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fixme: This needs to build a translation based on the source palette, not some intermediate 'ordered' table.
|
|
||||||
|
|
||||||
ActiveColors = SimpleTranslation (usedcolors, PatchRemap, identity, Luminosity);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
if(Chars[i].TranslatedPic)
|
|
||||||
static_cast<FFontChar1 *>(Chars[i].TranslatedPic->GetImage())->SetSourceRemap(PatchRemap);
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildTranslations (Luminosity.Data(), identity, &TranslationParms[TranslationType][0], ActiveColors, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: FFont - default constructor
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FFont::FFont (int lump)
|
|
||||||
{
|
|
||||||
Lump = lump;
|
|
||||||
FontName = NAME_None;
|
|
||||||
Cursor = '_';
|
|
||||||
noTranslate = false;
|
|
||||||
uint8_t pp = 0;
|
|
||||||
for (auto &p : PatchRemap) p = pp++;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FFont :: FixXMoves
|
|
||||||
//
|
|
||||||
// If a font has gaps in its characters, set the missing characters'
|
|
||||||
// XMoves to either SpaceWidth or the unaccented or uppercase variant's
|
|
||||||
// XMove. Missing XMoves must be initialized with INT_MIN beforehand.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void FFont::FixXMoves()
|
|
||||||
{
|
|
||||||
for (int i = 0; i <= LastChar - FirstChar; ++i)
|
|
||||||
{
|
|
||||||
if (Chars[i].XMove == INT_MIN)
|
|
||||||
{
|
|
||||||
// Try an uppercase character.
|
|
||||||
if (myislower(i + FirstChar))
|
|
||||||
{
|
|
||||||
int upper = upperforlower[FirstChar + i];
|
|
||||||
if (upper >= FirstChar && upper <= LastChar )
|
|
||||||
{
|
|
||||||
Chars[i].XMove = Chars[upper - FirstChar].XMove;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Try an unnaccented character.
|
|
||||||
int noaccent = stripaccent(i + FirstChar);
|
|
||||||
if (noaccent != i + FirstChar)
|
|
||||||
{
|
|
||||||
noaccent -= FirstChar;
|
|
||||||
if (noaccent >= 0)
|
|
||||||
{
|
|
||||||
Chars[i].XMove = Chars[noaccent].XMove;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Chars[i].XMove = SpaceWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// V_InitCustomFonts
|
// V_InitCustomFonts
|
||||||
|
|
Loading…
Reference in a new issue