From 3b222499ca22fe56db0b011fdfbab1b75e893d64 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Thu, 28 May 2026 20:06:52 -0700 Subject: [PATCH] Added missing file. --- neo/engine/renderer/Font.cpp | 508 +++++++++++++++++++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 neo/engine/renderer/Font.cpp diff --git a/neo/engine/renderer/Font.cpp b/neo/engine/renderer/Font.cpp new file mode 100644 index 00000000..b218c062 --- /dev/null +++ b/neo/engine/renderer/Font.cpp @@ -0,0 +1,508 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "precompiled.h" +#pragma hdrstop + +#include "tr_local.h" + +const char* DEFAULT_FONT = "Gothic821_Cn_BT"; + +static const float old_scale2 = 0.6f; +static const float old_scale1 = 0.3f; + +/* +============================== +Old_SelectValueForScale +============================== +*/ +ID_INLINE float Old_SelectValueForScale(float scale, float v0, float v1, float v2) { + return (scale >= old_scale2) ? v2 : (scale >= old_scale1) ? v1 : v0; +} + +static void Font_ReadBigInt(idFile *fd, int &value) { + fd->Read(&value, sizeof(value)); + value = BigLong(value); +} + +static void Font_ReadBigShort(idFile *fd, short &value) { + fd->Read(&value, sizeof(value)); + value = BigShort(value); +} + +static bool Font_ReadTGAHeader(const char* filename, int &width, int &height) { + width = 0; + height = 0; + + idFile* fd = fileSystem->OpenFileRead(filename); + if (fd == NULL) { + return false; + } + + byte header[18]; + const int bytesRead = fd->Read(header, sizeof(header)); + fileSystem->CloseFile(fd); + + if (bytesRead != sizeof(header)) { + return false; + } + + width = header[12] | (header[13] << 8); + height = header[14] | (header[15] << 8); + return width > 0 && height > 0; +} + +/* +============================== +R_NormalizeFontName +============================== +*/ +static idStr R_NormalizeFontName(const char* fontName) { + idStr cleanName = fontName ? fontName : ""; + cleanName.StripQuotes(); + cleanName.BackSlashesToSlashes(); + cleanName.StripFileExtension(); + + if (cleanName.IsEmpty() || cleanName.Icmp("fonts") == 0 || cleanName.Icmp("default") == 0) { + return DEFAULT_FONT; + } + + const char* newFontPrefix = "newfonts/"; + if (idStr::Icmpn(cleanName.c_str(), newFontPrefix, strlen(newFontPrefix)) == 0) { + idStr rest = cleanName.c_str() + strlen(newFontPrefix); + int slash = rest.Find('/'); + if (slash >= 0) { + rest = rest.Left(slash); + } + return rest.IsEmpty() ? DEFAULT_FONT : rest; + } + + cleanName.StripPath(); + return cleanName.IsEmpty() ? DEFAULT_FONT : cleanName; +} + +/* +============================== +idRenderSystemLocal::RegisterFont +============================== +*/ +idFont* idRenderSystemLocal::RegisterFont(const char* fontName) { + idStr cleanName = R_NormalizeFontName(fontName); + + for (int i = 0; i < fonts.Num(); i++) { + if (idStr::Icmp(fonts[i]->GetName(), cleanName.c_str()) == 0) { + fonts[i]->Touch(); + return fonts[i]; + } + } + + idFont* font = new idFont(cleanName.c_str()); + fonts.Append(font); + return font; +} + +/* +============================== +idRenderSystemLocal::FreeFonts +============================== +*/ +void idRenderSystemLocal::FreeFonts() { + fonts.DeleteContents(true); +} + +/* +============================== +idFont::RemapFont +============================== +*/ +idFont* idFont::RemapFont(const char* baseName) { + idStr cleanName = R_NormalizeFontName(baseName); + + if (cleanName.Icmp(DEFAULT_FONT) == 0) { + return NULL; + } + + return NULL; +} + +/* +============================== +idFont::~idFont +============================== +*/ +idFont::~idFont() { + if (fontInfo != NULL) { + Mem_Free(fontInfo->glyphData); + Mem_Free(fontInfo->charIndex); + delete fontInfo; + } +} + +/* +============================== +idFont::idFont +============================== +*/ +idFont::idFont(const char* n) : name(R_NormalizeFontName(n)) { + fontInfo = NULL; + alias = RemapFont(name.c_str()); + + if (alias != NULL) { + for (idFont* f = alias; f != NULL; f = f->alias) { + if (f == this) { + common->FatalError("Font alias \"%s\" is a circular reference!", n); + } + } + return; + } + + if (!LoadFont()) { + if (name.Icmp(DEFAULT_FONT) == 0) { + common->FatalError("Could not load default font \"%s\"", DEFAULT_FONT); + } + else { + common->Warning("Could not load font %s", n); + alias = renderSystem->RegisterFont(DEFAULT_FONT); + } + } +} + +struct oldGlyphInfo_t { + int height; + int top; + int bottom; + int pitch; + int xSkip; + int imageWidth; + int imageHeight; + float s; + float t; + float s2; + float t2; + int junk; + char materialName[32]; +}; +static const int GLYPHS_PER_FONT = 256; + +/* +============================== +LoadOldGlyphData +============================== +*/ +bool LoadOldGlyphData(const char* filename, oldGlyphInfo_t glyphInfo[GLYPHS_PER_FONT]) { + idFile* fd = fileSystem->OpenFileRead(filename); + if (fd == NULL) { + return false; + } + fd->Read(glyphInfo, GLYPHS_PER_FONT * sizeof(oldGlyphInfo_t)); + for (int i = 0; i < GLYPHS_PER_FONT; i++) { + glyphInfo[i].height = LittleLong(glyphInfo[i].height); + glyphInfo[i].top = LittleLong(glyphInfo[i].top); + glyphInfo[i].bottom = LittleLong(glyphInfo[i].bottom); + glyphInfo[i].pitch = LittleLong(glyphInfo[i].pitch); + glyphInfo[i].xSkip = LittleLong(glyphInfo[i].xSkip); + glyphInfo[i].imageWidth = LittleLong(glyphInfo[i].imageWidth); + glyphInfo[i].imageHeight = LittleLong(glyphInfo[i].imageHeight); + glyphInfo[i].s = LittleFloat(glyphInfo[i].s); + glyphInfo[i].t = LittleFloat(glyphInfo[i].t); + glyphInfo[i].s2 = LittleFloat(glyphInfo[i].s2); + glyphInfo[i].t2 = LittleFloat(glyphInfo[i].t2); + assert(glyphInfo[i].imageWidth == glyphInfo[i].pitch); + assert(glyphInfo[i].imageHeight == glyphInfo[i].height); + assert(glyphInfo[i].imageWidth == (glyphInfo[i].s2 - glyphInfo[i].s) * 256); + assert(glyphInfo[i].imageHeight == (glyphInfo[i].t2 - glyphInfo[i].t) * 256); + assert(glyphInfo[i].junk == 0); + } + fileSystem->CloseFile(fd); + return true; +} + +/* +============================== +idFont::LoadFont +============================== +*/ +bool idFont::LoadFont() { + idStr fontName = va("newfonts/%s/48.dat", GetName()); + idFile* fd = fileSystem->OpenFileRead(fontName); + if (fd == NULL) { + return false; + } + + const int FONT_INFO_VERSION = 42; + const int FONT_INFO_MAGIC = (FONT_INFO_VERSION | ('i' << 24) | ('d' << 16) | ('f' << 8)); + + int version = 0; + Font_ReadBigInt(fd, version); + if (version != FONT_INFO_MAGIC) { + idLib::Warning("Wrong version in %s", GetName()); + fileSystem->CloseFile(fd); + return false; + } + + fontInfo = new fontInfo_t; + memset(fontInfo, 0, sizeof(*fontInfo)); + + short pointSize = 0; + + Font_ReadBigShort(fd, pointSize); + assert(pointSize == 48); + + Font_ReadBigShort(fd, fontInfo->ascender); + Font_ReadBigShort(fd, fontInfo->descender); + + Font_ReadBigShort(fd, fontInfo->numGlyphs); + + fontInfo->glyphData = (glyphInfo_t*)Mem_Alloc(sizeof(glyphInfo_t) * fontInfo->numGlyphs); + fontInfo->charIndex = (uint32_t*)Mem_Alloc(sizeof(uint32_t) * fontInfo->numGlyphs); + + const int GLYPH_RECORD_SIZE = 10; + for (int i = 0; i < fontInfo->numGlyphs; i++) { + byte glyphBytes[GLYPH_RECORD_SIZE]; + if (fd->Read(glyphBytes, sizeof(glyphBytes)) != sizeof(glyphBytes)) { + common->Warning("Short glyph data in %s", fontName.c_str()); + fileSystem->CloseFile(fd); + return false; + } + + fontInfo->glyphData[i].width = glyphBytes[0]; + fontInfo->glyphData[i].height = glyphBytes[1]; + fontInfo->glyphData[i].top = (char)glyphBytes[2]; + fontInfo->glyphData[i].left = (char)glyphBytes[3]; + fontInfo->glyphData[i].xSkip = glyphBytes[4]; + fontInfo->glyphData[i].s = glyphBytes[6] | (glyphBytes[7] << 8); + fontInfo->glyphData[i].t = glyphBytes[8] | (glyphBytes[9] << 8); + } + + fd->Read(fontInfo->charIndex, fontInfo->numGlyphs * sizeof(uint32_t)); + for (int i = 0; i < fontInfo->numGlyphs; i++) { + fontInfo->charIndex[i] = LittleLong(fontInfo->charIndex[i]); + } + + memset(fontInfo->ascii, -1, sizeof(fontInfo->ascii)); + for (int i = 0; i < fontInfo->numGlyphs; i++) { + if (fontInfo->charIndex[i] < 128) { + fontInfo->ascii[fontInfo->charIndex[i]] = i; + } + else { + break; + } + } + + idStr fontTextureName = fontName; + fontTextureName.SetFileExtension("tga"); + + fontInfo->material = declManager->FindMaterial(fontTextureName); + fontInfo->material->SetSort(SS_GUI); + if (!Font_ReadTGAHeader(fontTextureName.c_str(), fontInfo->materialWidth, fontInfo->materialHeight)) { + fontInfo->materialWidth = fontInfo->material->GetImageWidth(); + fontInfo->materialHeight = fontInfo->material->GetImageHeight(); + } + + int pointSizes[3] = { 12, 24, 48 }; + float scales[3] = { 4.0f, 2.0f, 1.0f }; + for (int i = 0; i < 3; i++) { + oldGlyphInfo_t oldGlyphInfo[GLYPHS_PER_FONT]; + const char* oldFileName = va("newfonts/%s/old_%d.dat", GetName(), pointSizes[i]); + if (LoadOldGlyphData(oldFileName, oldGlyphInfo)) { + int mh = 0; + int mw = 0; + for (int g = 0; g < GLYPHS_PER_FONT; g++) { + if (mh < oldGlyphInfo[g].height) { + mh = oldGlyphInfo[g].height; + } + if (mw < oldGlyphInfo[g].xSkip) { + mw = oldGlyphInfo[g].xSkip; + } + } + fontInfo->oldInfo[i].maxWidth = scales[i] * mw; + fontInfo->oldInfo[i].maxHeight = scales[i] * mh; + } + else { + int mh = 0; + int mw = 0; + for (int g = 0; g < fontInfo->numGlyphs; g++) { + if (mh < fontInfo->glyphData[g].height) { + mh = fontInfo->glyphData[g].height; + } + if (mw < fontInfo->glyphData[g].xSkip) { + mw = fontInfo->glyphData[g].xSkip; + } + } + fontInfo->oldInfo[i].maxWidth = mw; + fontInfo->oldInfo[i].maxHeight = mh; + } + } + fileSystem->CloseFile(fd); + return true; +} + +/* +============================== +idFont::GetGlyphIndex +============================== +*/ +int idFont::GetGlyphIndex(uint32_t idx) const { + if (idx < 128) { + return fontInfo->ascii[idx]; + } + if (fontInfo->numGlyphs == 0) { + return -1; + } + if (fontInfo->charIndex == NULL) { + return idx; + } + int len = fontInfo->numGlyphs; + int mid = fontInfo->numGlyphs; + int offset = 0; + while (mid > 0) { + mid = len >> 1; + if (fontInfo->charIndex[offset + mid] <= idx) { + offset += mid; + } + len -= mid; + } + return (fontInfo->charIndex[offset] == idx) ? offset : -1; +} + +/* +============================== +idFont::GetLineHeight +============================== +*/ +float idFont::GetLineHeight(float scale) const { + if (alias != NULL) { + return alias->GetLineHeight(scale); + } + if (fontInfo != NULL) { + return scale * Old_SelectValueForScale(scale, fontInfo->oldInfo[0].maxHeight, fontInfo->oldInfo[1].maxHeight, fontInfo->oldInfo[2].maxHeight); + } + return 0.0f; +} + +/* +============================== +idFont::GetAscender +============================== +*/ +float idFont::GetAscender(float scale) const { + if (alias != NULL) { + return alias->GetAscender(scale); + } + if (fontInfo != NULL) { + return scale * fontInfo->ascender; + } + return 0.0f; +} + +/* +============================== +idFont::GetMaxCharWidth +============================== +*/ +float idFont::GetMaxCharWidth(float scale) const { + if (alias != NULL) { + return alias->GetMaxCharWidth(scale); + } + if (fontInfo != NULL) { + return scale * Old_SelectValueForScale(scale, fontInfo->oldInfo[0].maxWidth, fontInfo->oldInfo[1].maxWidth, fontInfo->oldInfo[2].maxWidth); + } + return 0.0f; +} + +/* +============================== +idFont::GetGlyphWidth +============================== +*/ +float idFont::GetGlyphWidth(float scale, uint32_t idx) const { + if (alias != NULL) { + return alias->GetGlyphWidth(scale, idx); + } + if (fontInfo != NULL) { + int i = GetGlyphIndex(idx); + const int asterisk = 42; + if (i == -1 && idx != asterisk) { + i = GetGlyphIndex(asterisk); + } + if (i >= 0) { + return scale * fontInfo->glyphData[i].xSkip; + } + } + return 0.0f; +} + +/* +============================== +idFont::GetScaledGlyph +============================== +*/ +void idFont::GetScaledGlyph(float scale, uint32_t idx, scaledGlyphInfo_t& glyphInfo) const { + if (alias != NULL) { + return alias->GetScaledGlyph(scale, idx, glyphInfo); + } + if (fontInfo != NULL) { + int i = GetGlyphIndex(idx); + const int asterisk = 42; + if (i == -1 && idx != asterisk) { + i = GetGlyphIndex(asterisk); + } + if (i >= 0) { + float invMaterialWidth = 1.0f / fontInfo->materialWidth; + float invMaterialHeight = 1.0f / fontInfo->materialHeight; + glyphInfo_t& gi = fontInfo->glyphData[i]; + glyphInfo.xSkip = scale * gi.xSkip; + glyphInfo.top = scale * gi.top; + glyphInfo.left = scale * gi.left; + glyphInfo.width = scale * gi.width; + glyphInfo.height = scale * gi.height; + glyphInfo.s1 = (gi.s - 0.5f) * invMaterialWidth; + glyphInfo.t1 = (gi.t - 0.5f) * invMaterialHeight; + glyphInfo.s2 = (gi.s + gi.width + 0.5f) * invMaterialWidth; + glyphInfo.t2 = (gi.t + gi.height + 0.5f) * invMaterialHeight; + glyphInfo.material = fontInfo->material; + return; + } + } + memset(&glyphInfo, 0, sizeof(glyphInfo)); +} + +/* +============================== +idFont::Touch +============================== +*/ +void idFont::Touch() { + if (alias != NULL) { + alias->Touch(); + } + if (fontInfo != NULL) { + const_cast(fontInfo->material)->EnsureNotPurged(); + fontInfo->material->SetSort(SS_GUI); + } +}