From 45d75745f0e0b1914b6671ea34b92b095a961f02 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 17 Feb 2019 19:15:57 +0100 Subject: [PATCH] - reworked console font loading to use the glyph sheets directly and allowing to load more than one per font. --- src/gamedata/fonts/font.cpp | 304 ++++++++++++++++------- src/gamedata/fonts/v_font.cpp | 45 ++-- src/gamedata/fonts/v_font.h | 4 + wadsrc/static/confont.lmp | Bin 10302 -> 0 bytes wadsrc/static/fonts/consolefont/0000.png | Bin 0 -> 3827 bytes wadsrc/static/fonts/consolefont/0400.png | Bin 0 -> 2073 bytes wadsrc/static/fonts/consolefont/font.inf | 3 + 7 files changed, 239 insertions(+), 117 deletions(-) delete mode 100644 wadsrc/static/confont.lmp create mode 100644 wadsrc/static/fonts/consolefont/0000.png create mode 100644 wadsrc/static/fonts/consolefont/0400.png create mode 100644 wadsrc/static/fonts/consolefont/font.inf diff --git a/src/gamedata/fonts/font.cpp b/src/gamedata/fonts/font.cpp index 4b7b5b8da..a151f1095 100644 --- a/src/gamedata/fonts/font.cpp +++ b/src/gamedata/fonts/font.cpp @@ -55,6 +55,7 @@ #include "image.h" #include "utf8.h" #include "textures/formats/fontchars.h" +#include "textures/formats/multipatchtexture.h" #include "fontinternals.h" @@ -91,6 +92,7 @@ FFont::FFont (const char *name, const char *nametemplate, const char *filetempla uint8_t pp = 0; for (auto &p : PatchRemap) p = pp++; translateUntranslated = false; + int FixedWidth = 0; maxyoffs = 0; @@ -151,6 +153,14 @@ FFont::FFont (const char *name, const char *nametemplate, const char *filetempla sc.MustGetValue(false); FontHeight = sc.Number; } + else if (sc.Compare("CellSize")) + { + sc.MustGetValue(false); + FixedWidth = sc.Number; + sc.MustGetToken(','); + sc.MustGetValue(false); + FontHeight = sc.Number; + } else if (sc.Compare("Translationtype")) { sc.MustGetToken(TK_Identifier); @@ -172,130 +182,236 @@ FFont::FFont (const char *name, const char *nametemplate, const char *filetempla } } - - if (nametemplate != nullptr) + if (FixedWidth > 0) { - 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)); - } - } + ReadSheetFont(folderdata, FixedWidth, FontHeight, Scale); } - if (folderdata.Size() > 0) + else { - // all valid lumps must be named with a hex number that represents its Unicode character index. - for (auto &entry : folderdata) + if (nametemplate != nullptr) { - char *endp; - auto base = ExtractFileBase(entry.name); - auto position = strtoll(base.GetChars(), &endp, 16); - if ((*endp == 0 || (*endp == '.' && position >= '!' && position < 0xffff))) + for (i = 0; i < lcount; i++) { - auto lump = TexMan.CheckForTexture(entry.name, ETextureType::MiscPatch); + 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 ((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); + 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(); +} + +void FFont::ReadSheetFont(TArray &folderdata, int width, int height, const DVector2 &Scale) +{ + // all valid lumps must be named with a hex number that represents the Unicode character index for its first character, + TArray part(1, true); + TMap charMap; + int minchar = INT_MAX; + int maxchar = INT_MIN; + for (auto &entry : folderdata) + { + char *endp; + auto base = ExtractFileBase(entry.name); + auto position = strtoll(base.GetChars(), &endp, 16); + if ((*endp == 0 || (*endp == '.' && position >= 0 && position < 0xffff))) // Sheet fonts may fill in the low control chars. + { + auto lump = TexMan.CheckForTexture(entry.name, ETextureType::MiscPatch); + if (lump.isValid()) + { + auto tex = TexMan.GetTexture(lump); + int numtex_x = tex->GetWidth() / width; + int numtex_y = tex->GetHeight() / height; + int maxinsheet = int(position) + numtex_x * numtex_y - 1; + if (minchar > position) minchar = int(position); + if (maxchar < maxinsheet) maxchar = maxinsheet; + + for (int y = 0; y < numtex_y; y++) + { + for (int x = 0; x < numtex_x; x++) + { + part[0].OriginX = -width * x; + part[0].OriginY = -height * y; + part[0].Image = tex->GetImage(); + FMultiPatchTexture *image = new FMultiPatchTexture(width, height, part, false, false); + FImageTexture *tex = new FImageTexture(image, ""); + tex->SetUseType(ETextureType::FontChar); + tex->bMultiPatch = true; + tex->Width = width; + tex->Height = height; + tex->_LeftOffset[0] = + tex->_LeftOffset[1] = + tex->_TopOffset[0] = + tex->_TopOffset[1] = 0; + tex->Scale = Scale; + tex->bMasked = true; + tex->bTranslucent = -1; + tex->bWorldPanning = true; + tex->bNoDecals = false; + tex->SourceLump = -1; // We do not really care. + TexMan.AddTexture(tex); + charMap.Insert(position + x + y * numtex_x, tex); + } } } } } + FirstChar = minchar; + bool map1252 = false; + if (minchar < 0x80 && maxchar >= 0xa0) // should be a settable option, but that'd probably cause more problems than it'd solve. + { + if (maxchar < 0x2122) maxchar = 0x2122; + map1252 = true; + } LastChar = maxchar; auto count = maxchar - minchar + 1; Chars.Resize(count); int fontheight = 0; - for (i = 0; i < count; i++) + for (int 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; - } - } + auto b = pic->Get8BitPixels(false); - 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(); + 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].XMove = width; + } + + if (map1252) + { + // Move the Windows-1252 characters to their proper place. + for (int i = 0x80; i < 0xa0; i++) { - Chars[i].TranslatedPic = nullptr; - Chars[i].XMove = INT_MIN; + if (win1252map[i - 0x80] != i && Chars[i - minchar].TranslatedPic != nullptr && Chars[win1252map[i - 0x80] - minchar].TranslatedPic == nullptr) + { + std::swap(Chars[i - minchar], Chars[win1252map[i - 0x80] - minchar]); + } } } - - 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(); + SpaceWidth = width; } //========================================================================== diff --git a/src/gamedata/fonts/v_font.cpp b/src/gamedata/fonts/v_font.cpp index 4dbcc5317..4e4a5382f 100644 --- a/src/gamedata/fonts/v_font.cpp +++ b/src/gamedata/fonts/v_font.cpp @@ -1415,50 +1415,50 @@ EColorRange V_ParseFontColor (const uint8_t *&color_value, int normalcolor, int void V_InitFonts() { InitLowerUpper(); - V_InitCustomFonts (); + V_InitCustomFonts(); // load the heads-up font if (!(SmallFont = V_GetFont("SmallFont", "SMALLFNT"))) { - if (Wads.CheckNumForName ("FONTA_S") >= 0) + if (Wads.CheckNumForName("FONTA_S") >= 0) { - SmallFont = new FFont ("SmallFont", "FONTA%02u", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, 1, -1); + SmallFont = new FFont("SmallFont", "FONTA%02u", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, 1, -1); SmallFont->SetCursor('['); } - else if (Wads.CheckNumForName ("STCFN033", ns_graphics) >= 0) + else if (Wads.CheckNumForName("STCFN033", ns_graphics) >= 0) { - SmallFont = new FFont ("SmallFont", "STCFN%.3d", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1); + SmallFont = new FFont("SmallFont", "STCFN%.3d", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1); } } if (!(SmallFont2 = V_GetFont("SmallFont2"))) // Only used by Strife { - if (Wads.CheckNumForName ("STBFN033", ns_graphics) >= 0) + if (Wads.CheckNumForName("STBFN033", ns_graphics) >= 0) { - SmallFont2 = new FFont ("SmallFont2", "STBFN%.3d", "defsmallfont2", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1); - } + SmallFont2 = new FFont("SmallFont2", "STBFN%.3d", "defsmallfont2", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1); } + } if (!(BigFont = V_GetFont("BigFont"))) { if (gameinfo.gametype & GAME_Raven) { - BigFont = new FFont ("BigFont", "FONTB%02u", "defbigfont", HU_FONTSTART, HU_FONTSIZE, 1, -1); - } + BigFont = new FFont("BigFont", "FONTB%02u", "defbigfont", HU_FONTSTART, HU_FONTSIZE, 1, -1); } + } if (!(ConFont = V_GetFont("ConsoleFont", "CONFONT"))) - { + { ConFont = SmallFont; - } + } if (!(IntermissionFont = FFont::FindFont("IntermissionFont"))) - { + { if (gameinfo.gametype & GAME_DoomChex) - { + { IntermissionFont = FFont::FindFont("IntermissionFont_Doom"); - } - if (IntermissionFont == nullptr) - { - IntermissionFont = BigFont; - } } + if (IntermissionFont == nullptr) + { + IntermissionFont = BigFont; + } + } // This can only happen if gzdoom.pk3 is corrupted. ConFont should always be present. if (ConFont == nullptr) { @@ -1472,12 +1472,11 @@ void V_InitFonts() if (SmallFont2 == nullptr) { SmallFont2 = SmallFont; - } + } if (BigFont == nullptr) - { + { BigFont = SmallFont; - } - + } } void V_ClearFonts() diff --git a/src/gamedata/fonts/v_font.h b/src/gamedata/fonts/v_font.h index a4bf7c738..0702781e9 100644 --- a/src/gamedata/fonts/v_font.h +++ b/src/gamedata/fonts/v_font.h @@ -35,6 +35,8 @@ #define __V_FONT_H__ #include "doomtype.h" +#include "w_wad.h" +#include "vectors.h" class DCanvas; struct FRemapTable; @@ -114,6 +116,8 @@ protected: static int SimpleTranslation (uint8_t *colorsused, uint8_t *translation, uint8_t *identity, TArray &Luminosity); + void ReadSheetFont(TArray &folderdata, int width, int height, const DVector2 &Scale); + int FirstChar, LastChar; int SpaceWidth; int FontHeight; diff --git a/wadsrc/static/confont.lmp b/wadsrc/static/confont.lmp deleted file mode 100644 index 6b68df2e3e3004a2752dc9b53f65d3a06d0a7ec0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10302 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 FZ~!MbAB6w_ diff --git a/wadsrc/static/fonts/consolefont/0000.png b/wadsrc/static/fonts/consolefont/0000.png new file mode 100644 index 0000000000000000000000000000000000000000..aa78736f71935569ba563838e0ed9c69c660156e GIT binary patch literal 3827 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 literal 0 HcmV?d00001 diff --git a/wadsrc/static/fonts/consolefont/0400.png b/wadsrc/static/fonts/consolefont/0400.png new file mode 100644 index 0000000000000000000000000000000000000000..d3f3786eed6c88af93f7068731763ed5a9932be5 GIT binary patch literal 2073 zcmeAS@N?(olHy`uVBq!ia0y~yU}#`qU@+idW?*1=r`6QMz`($g?&#~tz_78O`%fY( z0|SFXvPY0F14ES>14Ba#1H&(no)-)Zr3MTPuM!v-tY$DUh!@P+6==i2z|0Wf6XMFi zz`)4J*w@$B-`_uf{`~dp*YDrI|NQy$_wV0-|NcG0C>RZaVI2Z$w_D{I7#JB!g8YL2 zk8HpY{GDBufq{Xuz$3Dlfr0N32s4Umc!5eCi4xa{lHmNblJdl&REC1Y%)Ao4ywnl} z6Foyc(-ilJAO;5JU{4pvkO=p;(;pVyHsEP~tm1q5dwl}$4~L+Aum8QfwL8+7Q>Z27 zsHW)qZFLnf(mTuwav$7kFfZ7D|1l?*+WI2#nwz0oOHy*|t}z{2*v5BnZ{zdN6?WHu z{jjjPHmO)|)}B*yt+!^+J*++};mgc+3Ez^~$)~F}Nt;jOG%ZmM;GXeWKV;>a;wn1B80s`%i|cdtEv zUU;JC{uyVFh@HWLQ-Zn|7{+9EUJPDUleO)3)&71X-r7UGcP4!K`|iY+f4Q}H+@38w zy^GB{`RatQHM}nZxBcXte^L6!uOKyZ*7z%vq@I|Ct^MS2USn~{M|}(SOMOzJ3<Uk})t^^kh)=u}59nXsS0&?#JUf^A~_|VMXw^O)oncewfxsJ7A#bp+?;7q@1#SM@vL-gw<)GQa zd}fnf!l5rizh=&7dKugP%ydrBcVPjenc5Py&#vCF@aOkCd(84cf9cnkoHrRXYvo1e z%9mgID|g|