mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-11-28 06:41:59 +00:00
415 lines
12 KiB
C++
415 lines
12 KiB
C++
|
/*
|
|||
|
===========================================================================
|
|||
|
|
|||
|
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 <http://www.gnu.org/licenses/>.
|
|||
|
|
|||
|
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<idMaterial *>( fontInfo->material )->EnsureNotPurged();
|
|||
|
fontInfo->material->SetSort( SS_GUI );
|
|||
|
}
|
|||
|
}
|