/* =========================================================================== 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. =========================================================================== */ #pragma hdrstop #include "../idlib/precompiled.h" #include "Font.h" const char * DEFAULT_FONT = "Arial_Narrow"; 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; } /* ============================== idFont::RemapFont ============================== */ idFont * idFont::RemapFont( const char * baseName ) { idStr cleanName = baseName; if ( cleanName == DEFAULT_FONT ) { return NULL; } const char * remapped = idLocalization::FindString( "#font_" + cleanName ); if ( remapped != NULL ) { return renderSystem->RegisterFont( remapped ); } const char * wildcard = idLocalization::FindString( "#font_*" ); if ( wildcard != NULL && cleanName.Icmp( wildcard ) != 0 ) { return renderSystem->RegisterFont( wildcard ); } // Note single | so both sides are always executed if ( cleanName.ReplaceChar( ' ', '_' ) | cleanName.ReplaceChar( '-', '_' ) ) { return renderSystem->RegisterFont( cleanName ); } return NULL; } /* ============================== idFont::~idFont ============================== */ idFont::~idFont() { delete fontInfo; } /* ============================== idFont::idFont ============================== */ idFont::idFont( const char * n ) : name( n ) { fontInfo = NULL; alias = RemapFont( n ); if ( alias != NULL ) { // Make sure we don't have a circular reference for ( idFont * f = alias; f != NULL; f = f->alias ) { if ( f == this ) { idLib::FatalError( "Font alias \"%s\" is a circular reference!", n ); } } return; } if ( !LoadFont() ) { if ( name.Icmp( DEFAULT_FONT ) == 0 ) { idLib::FatalError( "Could not load default font \"%s\"", DEFAULT_FONT ); } else { idLib::Warning( "Could not load font %s", n ); alias = renderSystem->RegisterFont( DEFAULT_FONT ); } } } struct oldGlyphInfo_t { int height; // number of scan lines int top; // top of glyph in buffer int bottom; // bottom of glyph in buffer int pitch; // width for copying int xSkip; // x adjustment int imageWidth; // width of actual image int imageHeight; // height of actual image float s; // x offset in image where glyph starts float t; // y offset in image where glyph starts 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++ ) { idSwap::Little( glyphInfo[i].height ); idSwap::Little( glyphInfo[i].top ); idSwap::Little( glyphInfo[i].bottom ); idSwap::Little( glyphInfo[i].pitch ); idSwap::Little( glyphInfo[i].xSkip ); idSwap::Little( glyphInfo[i].imageWidth ); idSwap::Little( glyphInfo[i].imageHeight ); idSwap::Little( glyphInfo[i].s ); idSwap::Little( glyphInfo[i].t ); idSwap::Little( glyphInfo[i].s2 ); idSwap::Little( 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 ); } delete 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 ) ); uint32 version = 0; fd->ReadBig( version ); if ( version != FONT_INFO_MAGIC ) { idLib::Warning( "Wrong version in %s", GetName() ); delete fd; return false; } fontInfo = new (TAG_FONT) fontInfo_t; short pointSize = 0; fd->ReadBig( pointSize ); assert( pointSize == 48 ); fd->ReadBig( fontInfo->ascender ); fd->ReadBig( fontInfo->descender ); fd->ReadBig( fontInfo->numGlyphs ); fontInfo->glyphData = (glyphInfo_t *)Mem_Alloc( sizeof( glyphInfo_t ) * fontInfo->numGlyphs, TAG_FONT ); fontInfo->charIndex = (uint32 *)Mem_Alloc( sizeof( uint32 ) * fontInfo->numGlyphs, TAG_FONT ); fd->Read( fontInfo->glyphData, fontInfo->numGlyphs * sizeof( glyphInfo_t ) ); for( int i = 0; i < fontInfo->numGlyphs; i++ ) { idSwap::Little( fontInfo->glyphData[i].width ); idSwap::Little( fontInfo->glyphData[i].height ); idSwap::Little( fontInfo->glyphData[i].top ); idSwap::Little( fontInfo->glyphData[i].left ); idSwap::Little( fontInfo->glyphData[i].xSkip ); idSwap::Little( fontInfo->glyphData[i].s ); idSwap::Little( fontInfo->glyphData[i].t ); } fd->Read( fontInfo->charIndex, fontInfo->numGlyphs * sizeof( uint32 ) ); idSwap::LittleArray( fontInfo->charIndex, fontInfo->numGlyphs ); 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 { // Since the characters are sorted, as soon as we find a non-ascii character, we can stop break; } } idStr fontTextureName = fontName; fontTextureName.SetFileExtension( "tga" ); fontInfo->material = declManager->FindMaterial( fontTextureName ); fontInfo->material->SetSort( SS_GUI ); // Load the old glyph data because we want our new fonts to fit in the old glyph metrics 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; } } delete fd; return true; } /* ============================== idFont::GetGlyphIndex ============================== */ int idFont::GetGlyphIndex( uint32 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 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 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->material->GetImageWidth(); float invMaterialHeight = 1.0f / fontInfo->material->GetImageHeight(); 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 ); } }