/* =========================================================================== 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); } }