source-engine/utils/xbox/FontMaker/glyphs.cpp

1485 lines
38 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//-----------------------------------------------------------------------------
// Name: Glyphs.cpp
//
// Desc: Functions and global variables for keeping track of font glyphs
//
// Hist: 09.06.02 - Revised Fontmaker sample
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "Glyphs.h"
#include "FontMaker.h"
const COLORREF COLOR_WHITE = RGB(255,255,255);
const COLORREF COLOR_BLACK = RGB( 0, 0, 0);
const COLORREF COLOR_BLUE = RGB( 0, 0,255);
//-----------------------------------------------------------------------------
// Name: CTextureFont()
// Desc: Constructor
//-----------------------------------------------------------------------------
CTextureFont::CTextureFont()
{
ZeroMemory( this, sizeof( *this ) );
m_bIncludeNullCharacter = TRUE;
m_bAntialiasEffect = TRUE;
// Texture info
m_dwTextureWidth = 256;
m_dwTextureHeight = 256;
// Default glyph range
WORD wStartGlyph = 32;
WORD wEndGlyph = 255;
ExtractValidGlyphsFromRange( wStartGlyph, wEndGlyph );
}
//-----------------------------------------------------------------------------
// Name: ~CTextureFont()
// Desc: Destructor
//-----------------------------------------------------------------------------
CTextureFont::~CTextureFont()
{
DestroyObjects();
if ( m_hFont )
DeleteObject( m_hFont );
if ( m_pBits )
delete[] m_pBits;
m_pBits = NULL;
m_hFont = NULL;
}
//-----------------------------------------------------------------------------
// ClearFont
//-----------------------------------------------------------------------------
void CTextureFont::ClearFont()
{
DestroyObjects();
ZeroMemory( &m_LogFont, sizeof(LOGFONT) );
m_strFontName[0] = '\0';
m_hFont = NULL;
m_dwTextureWidth = 256;
m_dwTextureHeight = 256;
m_bAntialiasEffect = FALSE;
m_bShadowEffect = FALSE;
m_bOutlineEffect = FALSE;
m_nBlur = 0;
m_nScanlines = 0;
}
//-----------------------------------------------------------------------------
// Name: DestroyObjects()
// Desc: Cleans up all allocated resources for the class
//-----------------------------------------------------------------------------
VOID CTextureFont::DestroyObjects()
{
if ( m_pGlyphs )
delete[] m_pGlyphs;
if ( m_ValidGlyphs )
delete[] m_ValidGlyphs;
if ( m_TranslatorTable )
delete[] m_TranslatorTable;
if ( m_pCustomFilename )
{
TL_Free( (void*)m_pCustomFilename );
m_pCustomFilename = NULL;
for (DWORD i=0; i<m_dwNumGlyphs; i++)
{
if ( m_pCustomGlyphFiles[i] )
{
TL_Free( m_pCustomGlyphFiles[i] );
m_pCustomGlyphFiles[i] = NULL;
}
}
}
m_cMaxGlyph = 0;
m_dwNumGlyphs = 0;
m_pGlyphs = NULL;
m_ValidGlyphs = NULL;
m_TranslatorTable = NULL;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HRESULT CTextureFont::InsertGlyph( WORD wGlyph )
{
m_cMaxGlyph = 0;
m_dwNumGlyphs = 0;
m_ValidGlyphs[wGlyph] = 1;
for ( DWORD c=0; c<=65535; c++ )
{
if ( m_ValidGlyphs[c] )
{
m_dwNumGlyphs++;
m_cMaxGlyph = (WCHAR)c;
}
}
BuildTranslatorTable();
theApp.CalculateAndRenderGlyphs();
return S_OK;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
HRESULT CTextureFont::DeleteGlyph( WORD wGlyph )
{
m_cMaxGlyph = 0;
m_dwNumGlyphs = 0;
m_ValidGlyphs[wGlyph] = 0;
for ( DWORD c=0; c<=65535; c++ )
{
if ( m_ValidGlyphs[c] )
{
m_dwNumGlyphs++;
m_cMaxGlyph = (WCHAR)c;
}
}
BuildTranslatorTable();
theApp.CalculateAndRenderGlyphs();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: ExtractValidGlyphsFromRange()
// Desc: Set global variables to indicate we will be drawing all glyphs in the
// range specified.
//-----------------------------------------------------------------------------
HRESULT CTextureFont::ExtractValidGlyphsFromRange( WORD wStartGlyph, WORD wEndGlyph )
{
// Cleanup any previous entries
if( m_ValidGlyphs )
delete[] m_ValidGlyphs;
m_cMaxGlyph = 0;
m_dwNumGlyphs = 0;
m_ValidGlyphs = NULL;
// Allocate memory for the array of vaild glyphs
m_ValidGlyphs = new BYTE[65536];
ZeroMemory( m_ValidGlyphs, 65536 );
for( DWORD c=(DWORD)wStartGlyph; c<=(DWORD)wEndGlyph; c++ )
{
m_ValidGlyphs[c] = 1;
m_dwNumGlyphs++;
}
m_cMaxGlyph = wEndGlyph;
BuildTranslatorTable();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: ExtractValidGlyphsFromRange()
// Desc: Set global variables to indicate we will be drawing all glyphs that
// are present in the specified text file.
//-----------------------------------------------------------------------------
HRESULT CTextureFont::ExtractValidGlyphsFromFile( const CHAR* strFileName )
{
// Open the file
FILE* file = fopen( strFileName, "rb" );
if( NULL == file )
return E_FAIL;
// Cleanup any previous entries
if( m_ValidGlyphs )
delete[] m_ValidGlyphs;
m_cMaxGlyph = 0;
m_dwNumGlyphs = 0;
m_ValidGlyphs = NULL;
// Allocate memory for the array of vaild glyphs
m_ValidGlyphs = new BYTE[65536];
ZeroMemory( m_ValidGlyphs, 65536 );
// Skip the unicode marker
BOOL bIsUnicode = (fgetwc(file) == 0xfeff) ? TRUE : FALSE;
if( bIsUnicode == FALSE )
rewind( file );
// Record which glyphs are valid
WCHAR c;
while( (WCHAR)EOF != ( c = bIsUnicode ? fgetwc(file) : fgetc(file) ) )
{
while( c == L'\\' )
{
c = bIsUnicode ? fgetwc(file) : fgetc(file);
// Handle octal-coded characters
if( isdigit(c) )
{
int code = (c - L'0');
c = bIsUnicode ? fgetwc(file) : fgetc(file);
if( isdigit(c) )
{
code = code*8 + (c - L'0');
c = bIsUnicode ? fgetwc(file) : fgetc(file);
if( isdigit(c) )
{
code = code*8 + (c - L'0');
c = bIsUnicode ? fgetwc(file) : fgetc(file);
}
}
if( m_ValidGlyphs[code] == 0 )
{
if( code > m_cMaxGlyph )
m_cMaxGlyph = (WCHAR)code;
m_dwNumGlyphs++;
m_ValidGlyphs[code] = 2;
}
}
else
{
// Add the backslash character
if( L'\\' > m_cMaxGlyph )
m_cMaxGlyph = L'\\';
if( m_ValidGlyphs[L'\\'] == 0 )
{
m_dwNumGlyphs++;
m_ValidGlyphs[L'\\'] = 1;
}
}
}
if( m_ValidGlyphs[c] == 0 )
{
// If the character is a printable one, add it
if( c != L'\n' && c != L'\r' && c != 0xffff )
{
m_dwNumGlyphs++;
m_ValidGlyphs[c] = 1;
if( c > m_cMaxGlyph )
m_cMaxGlyph = c;
}
}
}
// Done with the file
fclose( file );
BuildTranslatorTable();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: BuildTranslatorTable()
// Desc: Builds a table to translate from a WCHAR to a glyph index.
//-----------------------------------------------------------------------------
HRESULT CTextureFont::BuildTranslatorTable()
{
if ( m_TranslatorTable )
delete[] m_TranslatorTable;
// Insure the \0 is there
if ( m_bIncludeNullCharacter && 0 == m_ValidGlyphs[0] )
{
m_dwNumGlyphs++;
m_ValidGlyphs[0] = 1;
}
// Fill the string of all valid glyphs and build the translator table
m_TranslatorTable = new WORD[m_cMaxGlyph+1];
ZeroMemory( m_TranslatorTable, sizeof(WORD)*(m_cMaxGlyph+1) );
if ( !m_pCustomFilename )
{
// ttf has glyphs that are sequential
DWORD dwGlyph = 0;
for ( DWORD i=0; i<65536; i++ )
{
if ( m_ValidGlyphs[i] )
{
m_TranslatorTable[i] = (WORD)dwGlyph;
dwGlyph++;
}
}
}
else
{
// custom font has glyphs that are scattered
DWORD dwGlyph = 0;
for ( DWORD i=0; i<m_dwNumGlyphs; i++ )
{
if ( !i && m_bIncludeNullCharacter )
{
m_TranslatorTable[0] = 0;
dwGlyph++;
continue;
}
m_TranslatorTable[m_customGlyphs[i-1]] = (WORD)dwGlyph;
dwGlyph++;
}
}
return S_OK;
}
void StripQuotedToken( char *pToken )
{
int len = strlen( pToken );
if ( !len )
return;
if ( pToken[0] == '\"' && pToken[len-1] == '\"' )
{
memcpy( pToken, pToken+1, len-1 );
pToken[len-2] = '\0';
}
}
//-----------------------------------------------------------------------------
// ReadCustomFontFile
//-----------------------------------------------------------------------------
HRESULT CTextureFont::ReadCustomFontFile( CHAR* strFileName )
{
char *pToken;
char fontName[128];
bool bSuccess;
unsigned char glyphs[256];
char *glyphFiles[512];
char basePath[MAX_PATH];
int numGlyphs;
char filename[MAX_PATH];
bSuccess = false;
numGlyphs = 0;
ClearFont();
TL_LoadScriptFile( strFileName );
strcpy( basePath, strFileName );
TL_StripFilename( basePath );
TL_AddSeperatorToPath( basePath, basePath );
fontName[0] = '\0';
while ( 1 )
{
pToken = TL_GetToken( true );
if ( !pToken || !pToken[0] )
break;
StripQuotedToken( pToken );
// get font name
if ( !stricmp( pToken, "fontName" ) )
{
pToken = TL_GetToken( true );
if ( !pToken || !pToken[0] )
goto cleanUp;
StripQuotedToken( pToken );
strcpy( fontName, pToken );
continue;
}
// get glyph
if ( strlen( pToken ) != 1 )
goto cleanUp;
glyphs[numGlyphs] = pToken[0];
// get glyph file
pToken = TL_GetToken( true );
if ( !pToken || !pToken[0] )
goto cleanUp;
StripQuotedToken( pToken );
sprintf( filename, "%s%s", basePath, pToken );
glyphFiles[numGlyphs] = TL_CopyString( filename );
numGlyphs++;
if ( numGlyphs >= 256 )
break;
}
if ( numGlyphs == 0 )
goto cleanUp;
m_pCustomFilename = TL_CopyString( strFileName );
strcpy ( m_strFontName, fontName );
m_dwTextureWidth = 256;
m_dwTextureHeight = 256;
m_ValidGlyphs = new BYTE[65536];
ZeroMemory( m_ValidGlyphs, 65536 );
m_dwNumGlyphs = numGlyphs;
m_cMaxGlyph = 0;
for (int i=0; i<numGlyphs; i++)
{
m_ValidGlyphs[glyphs[i]] = 1;
if ( m_cMaxGlyph < glyphs[i] )
m_cMaxGlyph = glyphs[i];
m_customGlyphs[i] = glyphs[i];
m_pCustomGlyphFiles[i] = glyphFiles[i];
}
BuildTranslatorTable();
bSuccess = true;
cleanUp:
TL_FreeScriptFile();
return bSuccess ? S_OK : E_FAIL;
}
//-----------------------------------------------------------------------------
// Name: ReadFontInfoFile()
// Desc: Loads the font's glyph info from a file
//-----------------------------------------------------------------------------
HRESULT CTextureFont::ReadFontInfoFile( CHAR* strFileName )
{
BitmapFont_t bitmapFont;
// open the info file
FILE* file = fopen( strFileName, "rb" );
if ( NULL == file )
return E_FAIL;
memset( &bitmapFont, 0, sizeof( BitmapFont_t ) );
fread( &bitmapFont, 1, sizeof( BitmapFont_t ), file );
if ( bitmapFont.m_id != BITMAPFONT_ID || bitmapFont.m_Version != BITMAPFONT_VERSION )
{
return E_FAIL;
}
theApp.SetTextureSize( bitmapFont.m_PageWidth, bitmapFont.m_PageHeight );
ZeroMemory( m_ValidGlyphs, 65536 );
m_dwNumGlyphs = 0;
m_cMaxGlyph = 0;
for (int i=0; i<256; i++)
{
if ( bitmapFont.m_TranslateTable[i] )
{
m_ValidGlyphs[i] = 1;
m_cMaxGlyph = i;
m_dwNumGlyphs++;
}
}
BuildTranslatorTable();
// success
fclose( file );
theApp.OnGlyphsCustom();
theApp.CalculateAndRenderGlyphs();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: WriteFontInfoFile()
// Desc: Writes the font's glyph info to a file
//-----------------------------------------------------------------------------
HRESULT CTextureFont::WriteFontInfoFile( CHAR* strFileName )
{
BitmapFont_t bitmapFont;
BitmapGlyph_t bitmapGlyph;
// Create the info file
FILE* file = fopen( strFileName, "wb" );
if ( NULL == file )
return E_FAIL;
bitmapFont.m_id = BITMAPFONT_ID;
bitmapFont.m_Version = BITMAPFONT_VERSION;
bitmapFont.m_PageWidth = (short)m_dwTextureWidth;
bitmapFont.m_PageHeight = (short)m_dwTextureHeight;
bitmapFont.m_Ascent = 0;
bitmapFont.m_NumGlyphs = (short)m_dwNumGlyphs;
// generate flags
bitmapFont.m_Flags = 0;
if ( m_bAntialiasEffect )
{
bitmapFont.m_Flags |= BF_ANTIALIASED;
}
if ( m_bShadowEffect )
{
bitmapFont.m_Flags |= BF_DROPSHADOW;
}
if ( m_bOutlineEffect )
{
bitmapFont.m_Flags |= BF_OUTLINED;
}
if ( m_nBlur )
{
bitmapFont.m_Flags |= BF_BLURRED;
}
if ( m_nScanlines )
{
bitmapFont.m_Flags |= BF_SCANLINES;
}
if ( m_LogFont.lfItalic )
{
bitmapFont.m_Flags |= BF_ITALIC;
}
if ( m_LogFont.lfWeight > 400 )
{
bitmapFont.m_Flags |= BF_BOLD;
}
if ( m_pCustomFilename )
{
bitmapFont.m_Flags |= BF_CUSTOM;
}
// determine max char width from all glyphs
bitmapFont.m_MaxCharWidth = 0;
for (unsigned int i=0; i<m_dwNumGlyphs; i++ )
{
if ( bitmapFont.m_MaxCharWidth < m_pGlyphs[i].w )
{
bitmapFont.m_MaxCharWidth = m_pGlyphs[i].w;
}
}
bitmapFont.m_MaxCharHeight = 0;
for (unsigned int i=0; i<m_dwNumGlyphs; i++ )
{
if ( bitmapFont.m_MaxCharHeight < m_pGlyphs[i].h )
{
bitmapFont.m_MaxCharHeight = m_pGlyphs[i].h;
}
}
// maps a char index to its actual glyph
for (int i=0; i<256; i++)
{
if ( i <= m_cMaxGlyph )
{
bitmapFont.m_TranslateTable[i] = (unsigned char)m_TranslatorTable[i];
}
else
{
bitmapFont.m_TranslateTable[i] = 0;
}
}
// write out the header
fwrite( &bitmapFont, sizeof( BitmapFont_t ), 1, file );
// Write out the vertical padding caused by effects
// FLOAT fTopPadding = ( m_bOutlineEffect ? 1.0f : 0.0f );
// FLOAT fBottomPadding = ( m_bOutlineEffect ? ( m_bShadowEffect ? 2.0f : 1.0f ) : ( m_bShadowEffect ? 2.0f : 0.0f ) );
// FLOAT fFontYAdvance = fFontHeight - fTopPadding - fBottomPadding;
// Write the glyph attributes to the file
for (unsigned int i=0; i<m_dwNumGlyphs; i++ )
{
bitmapGlyph.x = m_pGlyphs[i].x;
bitmapGlyph.y = m_pGlyphs[i].y;
bitmapGlyph.w = m_pGlyphs[i].w;
bitmapGlyph.h = m_pGlyphs[i].h;
bitmapGlyph.a = m_pGlyphs[i].a;
bitmapGlyph.b = m_pGlyphs[i].b;
bitmapGlyph.c = m_pGlyphs[i].c;
fwrite( &bitmapGlyph, sizeof( BitmapGlyph_t ), 1, file );
}
// success
fclose( file );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: WriteTargaFile()
// Desc: Writes 32-bit RGBA data to a .tga file
//-----------------------------------------------------------------------------
HRESULT WriteTargaFile( CHAR* strFileName, DWORD dwWidth, DWORD dwHeight,
DWORD* pRGBAData )
{
struct TargaHeader
{
BYTE IDLength;
BYTE ColormapType;
BYTE ImageType;
BYTE ColormapSpecification[5];
WORD XOrigin;
WORD YOrigin;
WORD ImageWidth;
WORD ImageHeight;
BYTE PixelDepth;
BYTE ImageDescriptor;
} tga;
// Create the file
FILE* file = fopen( strFileName, "wb" );
if( NULL == file )
return E_FAIL;
// Write the TGA header
ZeroMemory( &tga, sizeof(tga) );
tga.IDLength = 0;
tga.ImageType = 2;
tga.ImageWidth = (WORD)dwWidth;
tga.ImageHeight = (WORD)dwHeight;
tga.PixelDepth = 32;
tga.ImageDescriptor = 0x28;
fwrite( &tga, sizeof(TargaHeader), 1, file );
// Write the pixels
fwrite( pRGBAData, sizeof(DWORD), dwHeight*dwWidth, file );
// Close the file and return okay
fclose( file );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: WriteFontImageFile()
// Desc: Writes 32-bit RGBA data to a .tga file
//-----------------------------------------------------------------------------
HRESULT CTextureFont::WriteFontImageFile( CHAR* strFileName, bool bAdditiveMode, bool bCustomFont )
{
// Convert the bits to have an alpha channel
DWORD* pRGBAData = new DWORD[m_dwTextureWidth*m_dwTextureHeight];
FLOAT l;
for ( DWORD i=0; i<m_dwTextureWidth*m_dwTextureHeight; i++ )
{
FLOAT a = ( ( 0xff000000 & m_pBits[i] ) >> 24L ) / 255.0f;
FLOAT r = ( ( 0x00ff0000 & m_pBits[i] ) >> 16L ) / 255.0f;
FLOAT g = ( ( 0x0000ff00 & m_pBits[i] ) >> 8L ) / 255.0f;
FLOAT b = ( ( 0x000000ff & m_pBits[i] ) >> 0L ) / 255.0f;
if ( bCustomFont )
{
if ( a == 0.0f && b == 1.0f )
{
// pure transluscent
a = 0;
r = g = b = 0.0f;
}
int red = (int)(r * 255.0f);
int green = (int)(g * 255.0f);
int blue = (int)(b * 255.0f);
int alpha = (int)(a * 255.0f);
pRGBAData[i] = (alpha<<24L) | (red<<16L) | (green<<8L) | (blue<<0L);
}
else
{
if ( bAdditiveMode )
{
// all channels should be same
if (( r != g ) || ( r != b ))
{
}
l = r;
a = 1.0f;
}
else
{
a = r + (1-b);
if ( a )
l = r / a;
else
l = 1;
}
DWORD alpha = (DWORD)( a * 255.0f );
DWORD lum = (DWORD)( l * 255.0f );
pRGBAData[i] = (alpha<<24L) | (lum<<16L) | (lum<<8L) | (lum<<0L);
}
}
// Write the file
HRESULT hr = WriteTargaFile( strFileName, m_dwTextureWidth,
m_dwTextureHeight, pRGBAData );
// Cleanup and return
delete[] pRGBAData;
return hr;
}
void GetBitmapBits2( HBITMAP hBitmap, int width, int height, void *pBits )
{
memset( pBits, 0, width*height*4 );
HDC hDC = CreateCompatibleDC( NULL );
BITMAPINFO bitmapInfo = {0};
bitmapInfo.bmiHeader.biSize = sizeof( bitmapInfo.bmiHeader );
bitmapInfo.bmiHeader.biWidth = width;
bitmapInfo.bmiHeader.biHeight = -height;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
GetDIBits( hDC, hBitmap, 0, height, pBits, &bitmapInfo ,DIB_RGB_COLORS );
DeleteDC( hDC );
}
void SetBitmapBits2( HBITMAP hBitmap, int width, int height, void *pBits )
{
HDC hDC = CreateCompatibleDC( NULL );
BITMAPINFO bitmapInfo = {0};
bitmapInfo.bmiHeader.biSize = sizeof( bitmapInfo.bmiHeader );
bitmapInfo.bmiHeader.biWidth = width;
bitmapInfo.bmiHeader.biHeight = -height;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
SetDIBits( hDC, hBitmap, 0, height, pBits, &bitmapInfo ,DIB_RGB_COLORS );
DeleteDC( hDC );
}
//-----------------------------------------------------------------------------
// SetTextureBits
//
// Blit the rect back into the bitmap
//-----------------------------------------------------------------------------
void SetTextureBits( HBITMAP hBitmap, int bitmapWidth, int bitmapHeight, int x, int y, int w, int h, unsigned char *pRGBA )
{
// get the enitre bitmap
unsigned char *pBitmapBits = (unsigned char *)malloc( bitmapWidth * bitmapHeight * 4);
GetBitmapBits2( hBitmap, bitmapWidth, bitmapHeight, pBitmapBits );
// copy into bitmap bits
unsigned char *pSrc = pRGBA;
for (int yy=y; yy<y+h; yy++)
{
if ( yy >= bitmapHeight )
{
// past end of bitmap
break;
}
unsigned char *pDst = pBitmapBits + (yy*bitmapWidth + x)*4;
for (int xx=0; xx<w; xx++)
{
if ( xx+x < bitmapWidth )
{
pDst[0] = pSrc[0];
pDst[1] = pSrc[1];
pDst[2] = pSrc[2];
pDst[3] = pSrc[3];
}
pSrc += 4;
pDst += 4;
}
}
SetBitmapBits2( hBitmap, bitmapWidth, bitmapHeight, pBitmapBits );
free( pBitmapBits );
}
//-----------------------------------------------------------------------------
// GetTextureBits
//
// Blit the rect out of the bitmap
//-----------------------------------------------------------------------------
unsigned char *GetTextureBits( HBITMAP hBitmap, int bitmapWidth, int bitmapHeight, int x, int y, int w, int h )
{
// get the enitre bitmap
unsigned char *pBitmapBits = new unsigned char[bitmapWidth * bitmapHeight * 4];
GetBitmapBits2( hBitmap, bitmapWidth, bitmapHeight, pBitmapBits );
unsigned char *pRGBA = new unsigned char[w * h * 4];
memset( pRGBA, 0, w*h*4 );
// copy out bits
unsigned char *pDst = pRGBA;
for (int yy=y; yy<y+h; yy++)
{
if ( yy >= bitmapHeight )
{
// past last row of bitmap
break;
}
unsigned char *pSrc = pBitmapBits + (yy*bitmapWidth + x)*4;
for (int xx=0; xx<w; xx++)
{
if ( xx + x < bitmapWidth )
{
pDst[0] = pSrc[0];
pDst[1] = pSrc[1];
pDst[2] = pSrc[2];
pDst[3] = pSrc[3];
}
pSrc += 4;
pDst += 4;
}
}
delete [] pBitmapBits;
return pRGBA;
}
int g_blur;
float *g_pGaussianDistribution;
//-----------------------------------------------------------------------------
// Purpose: Gets the blur value for a single pixel
//-----------------------------------------------------------------------------
void GetBlurValueForPixel(unsigned char *src, int blur, float *gaussianDistribution, int srcX, int srcY, int srcWide, int srcTall, unsigned char *dest)
{
int r = 0, g = 0, b = 0, a = 0;
float accum = 0.0f;
// scan the positive x direction
int maxX = min(srcX + blur, srcWide);
int minX = max(srcX - blur, 0);
for (int x = minX; x <= maxX; x++)
{
int maxY = min(srcY + blur, srcTall - 1);
int minY = max(srcY - blur, 0);
for (int y = minY; y <= maxY; y++)
{
unsigned char *srcPos = src + ((x + (y * srcWide)) * 4);
unsigned char red = srcPos[2];
unsigned char green = srcPos[1];
unsigned char blue = srcPos[0];
// muliply by the value matrix
float weight = gaussianDistribution[x - srcX + blur];
float weight2 = gaussianDistribution[y - srcY + blur];
accum += (red * (weight * weight2));
}
}
// blurring decreased the range, for xbox kick some back
accum *= 1.30f;
// all the values are the same for fonts, just use the calculated alpha
r = g = b = (int)accum;
dest[0] = min(b, 255);
dest[1] = min(g, 255);
dest[2] = min(r, 255);
}
//-----------------------------------------------------------------------------
// ApplyGaussianBlurToTexture
//-----------------------------------------------------------------------------
void ApplyGaussianBlurToTexture( int blur, int rgbaX, int rgbaY, int rgbaWide, int rgbaTall, unsigned char *rgba)
{
// calculate our gaussian distribution for if we're blurred
if ( blur > 1 && blur != g_blur )
{
g_blur = blur;
g_pGaussianDistribution = new float[blur * 2 + 1];
double sigma = 0.683f * blur;
for (int x = 0; x <= (blur * 2); x++)
{
int val = x - blur;
g_pGaussianDistribution[x] = (float)((1.0 / sqrt(2.0 * 3.14 * sigma * sigma)) * pow(2.7, -1.0 * (val * val) / (2.0 * sigma * sigma)));
// brightening factor
g_pGaussianDistribution[x] *= 1;
}
}
// alloc a new buffer
unsigned char *src = (unsigned char *)_alloca(rgbaWide * rgbaTall * 4);
memcpy(src, rgba, rgbaWide * rgbaTall * 4);
unsigned char *dest = rgba;
for (int y = 0; y < rgbaTall; y++)
{
for (int x = 0; x < rgbaWide; x++)
{
// scan the source pixel
GetBlurValueForPixel(src, blur, g_pGaussianDistribution, x, y, rgbaWide, rgbaTall, dest);
// move to the next
dest += 4;
}
}
}
//-----------------------------------------------------------------------------
// ApplyScanlineEffectToTexture
//-----------------------------------------------------------------------------
void ApplyScanlineEffectToTexture( int scanLines, int rgbaX, int rgbaY, int rgbaWide, int rgbaTall, unsigned char *rgba)
{
if (scanLines < 2)
return;
float scale;
scale = 0;
// darken all the areas except the scanlines
for (int y = 0; y < rgbaTall; y++)
{
// skip the scan lines
if (y % scanLines == 0)
continue;
DWORD *pBits = (DWORD*)&rgba[(rgbaX + ((y + rgbaY) * rgbaWide)) * 4];
// darken the other lines
for (int x = 0; x < rgbaWide; x++, pBits++)
{
FLOAT r = ( ( 0x00ff0000 & pBits[0] ) >> 16L ) / 255.0f;
FLOAT g = ( ( 0x0000ff00 & pBits[0] ) >> 8L ) / 255.0f;
FLOAT b = ( ( 0x000000ff & pBits[0] ) >> 0L ) / 255.0f;
r *= scale;
g *= scale;
b *= scale;
pBits[0] = (((int)(r * 255.0f))<<16) | (((int)(g * 255.0f))<<8) | ((int)(b * 255.0f));
pBits[0] |= 0xFF000000;
}
}
}
//-----------------------------------------------------------------------------
// Name: RenderTTFGlyphs()
// Desc: Draws the list of font glyphs in the scroll view
//-----------------------------------------------------------------------------
GLYPH_ATTR* CTextureFont::RenderTTFGlyphs( HFONT hFont, HBITMAP hBitmap,
DWORD dwTextureWidth, DWORD dwTextureHeight,
BOOL bOutlineEffect, BOOL bShadowEffect,
int nScanlineEffect, int nBlurEffect,
BOOL bAntialias,
BYTE* ValidGlyphs, DWORD dwNumGlyphs )
{
// Create a DC
HDC hDC = CreateCompatibleDC( NULL );
// Associate the drawing surface
SelectObject( hDC, hBitmap );
// Create a clip region
HRGN rgn = CreateRectRgn( 0, 0, dwTextureWidth, dwTextureHeight );
SelectClipRgn( hDC, rgn );
// Setup the DC for the font
SetTextColor( hDC, COLOR_WHITE );
SelectObject( hDC, hFont );
SetTextAlign( hDC, TA_LEFT|TA_TOP|TA_UPDATECP );
SetMapMode( hDC, MM_TEXT );
SetBkMode( hDC, TRANSPARENT );
if ( nScanlineEffect || nBlurEffect )
{
SetBkColor( hDC, COLOR_BLACK );
if ( nBlurEffect < 2 )
nBlurEffect = 2;
if ( nScanlineEffect < 2 )
nScanlineEffect = 2;
}
else
{
SetBkColor( hDC, COLOR_BLUE );
}
// Fill the background in blue
RECT rect;
SetRect( &rect, 0, 0, dwTextureWidth, dwTextureHeight );
ExtTextOut( hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL );
// Get the effective font height
WCHAR str[2] = L"A";
SIZE size;
GetTextExtentPoint32W( hDC, str, 1, &size );
DWORD dwLeftOrigin = 1;
DWORD dwTopOrigin = 1;
GLYPH_ATTR* pGlyphs = new GLYPH_ATTR[dwNumGlyphs];
memset( pGlyphs, 0, dwNumGlyphs*sizeof( GLYPH_ATTR ) );
// Loop through all printable character and output them to the bitmap..
// Meanwhile, keep track of the corresponding tex coords for each character.
DWORD x = dwLeftOrigin;
DWORD y = dwTopOrigin;
int sx;
int sy;
int numGlyphs = 0;
for( DWORD i=0; i<65536; i++ )
{
if ( 0 == ValidGlyphs[i])
continue;
str[0] = (WCHAR)i;
if ( i==0 && ValidGlyphs[i] == 1 )
{
// account for unprintable, but don't render it
numGlyphs++;
continue;
}
GetTextExtentPoint32W( hDC, str, 1, &size );
// Get char width a different way
int charwidth;
GetCharWidth32( hDC, str[0], str[0], &charwidth );
// Get the ABC widths for the letter
ABC abc;
if ( FALSE == GetCharABCWidthsW( hDC, str[0], str[0], &abc ) )
{
abc.abcA = 0;
abc.abcB = size.cx;
abc.abcC = 0;
}
int w = abc.abcB;
int h = size.cy;
// Determine padding for effects
int left_padding = 0;
int right_padding = 0;
int top_padding = 0;
int bottom_padding = 0;
if ( bOutlineEffect || bShadowEffect )
{
if ( bOutlineEffect )
left_padding = 1;
if ( bOutlineEffect )
{
if ( bShadowEffect )
right_padding = 2;
else
right_padding = 1;
}
else
{
if ( bShadowEffect )
right_padding = 2;
else
right_padding = 0;
}
if ( bOutlineEffect )
top_padding = 1;
if ( bOutlineEffect )
{
if ( bShadowEffect )
bottom_padding = 2;
else
bottom_padding = 1;
}
else
{
if ( bShadowEffect )
bottom_padding = 2;
else
bottom_padding = 0;
}
}
else if ( nBlurEffect )
{
left_padding = nBlurEffect;
right_padding = nBlurEffect;
}
if ( ValidGlyphs[i] == 2 )
{
// Handle special characters
// Advance to the next line, if necessary
if ( x + h + left_padding + right_padding >= (int)dwTextureWidth )
{
x = dwLeftOrigin;
y += h + top_padding + bottom_padding + 1;
}
sx = x;
sy = y;
// Draw a square box for a placeholder for custom glyph graphics
w = h + left_padding + right_padding;
h = h + top_padding + bottom_padding;
abc.abcA = 0;
abc.abcB = w;
abc.abcC = 0;
RECT rect;
SetRect( &rect, x, y, x+w, y+h );
SetBkColor( hDC, COLOR_BLACK );
ExtTextOut( hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL );
}
else
{
// Hack to adjust for Kanji
if ( str[0] > 0x1000 )
{
w = h;
}
// Advance to the next line, if necessary
if ( x + w + left_padding + right_padding + 1 >= (int)dwTextureWidth )
{
x = dwLeftOrigin;
y += h + top_padding + bottom_padding + 1;
}
sx = x;
sy = y;
// Adjust ccordinates to account for the leading edge
if ( abc.abcA >= 0 )
x += abc.abcA;
else
sx -= abc.abcA;
// Hack to adjust for Kanji
if ( str[0] > 0x1000 )
{
sx += abc.abcA;
}
// Add padding to the width and height
w += left_padding + right_padding;
h += top_padding + bottom_padding;
abc.abcA -= left_padding;
abc.abcB += left_padding + right_padding;
abc.abcC -= right_padding;
if ( bOutlineEffect || bShadowEffect )
{
if ( bOutlineEffect )
{
SetTextColor( hDC, COLOR_BLACK );
MoveToEx( hDC, sx+0, sy+0, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
MoveToEx( hDC, sx+1, sy+0, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
MoveToEx( hDC, sx+2, sy+0, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
MoveToEx( hDC, sx+0, sy+1, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
MoveToEx( hDC, sx+2, sy+1, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
MoveToEx( hDC, sx+0, sy+2, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
MoveToEx( hDC, sx+1, sy+2, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
MoveToEx( hDC, sx+2, sy+2, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
if ( bShadowEffect )
{
MoveToEx( hDC, sx+3, sy+3, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
}
// Output the letter
SetTextColor( hDC, COLOR_WHITE );
MoveToEx( hDC, sx+1, sy+1, NULL ); ExtTextOutW( hDC, sx, sy, ETO_OPAQUE, NULL, str, 1, NULL );
}
else
{
if ( bShadowEffect )
{
SetTextColor( hDC, COLOR_BLACK );
MoveToEx( hDC, sx+2, sy+2, NULL ); ExtTextOutW( hDC, 0, 0, ETO_OPAQUE, NULL, str, 1, NULL );
}
// Output the letter
SetTextColor( hDC, COLOR_WHITE );
MoveToEx( hDC, sx, sy, NULL ); ExtTextOutW( hDC, sx, sy, ETO_OPAQUE, NULL, str, 1, NULL );
}
}
else if ( nBlurEffect )
{
// blur effect
SetTextColor( hDC, COLOR_WHITE );
MoveToEx( hDC, sx + nBlurEffect, sy, NULL );
ExtTextOutW( hDC, sx, sy, ETO_OPAQUE, NULL, str, 1, NULL );
// apply blur effect
unsigned char *pBGRA = GetTextureBits( hBitmap, dwTextureWidth, dwTextureHeight, x, y, w, h );
if ( pBGRA )
{
ApplyGaussianBlurToTexture( nBlurEffect, 0, 0, w, h, pBGRA );
SetTextureBits( hBitmap, dwTextureWidth, dwTextureHeight, x, y, w, h, pBGRA );
delete [] pBGRA;
}
}
else
{
// normal, no effect
// Output the letter
SetTextColor( hDC, COLOR_WHITE );
MoveToEx( hDC, sx, sy, NULL );
ExtTextOutW( hDC, sx, sy, ETO_OPAQUE, NULL, str, 1, NULL );
}
// apply scanline effect
if ( nScanlineEffect )
{
unsigned char *pBGRA = GetTextureBits( hBitmap, dwTextureWidth, dwTextureHeight, x, y, w, h );
if ( pBGRA )
{
ApplyScanlineEffectToTexture( nScanlineEffect, 0, 0, w, h, pBGRA );
SetTextureBits( hBitmap, dwTextureWidth, dwTextureHeight, x, y, w, h, pBGRA );
delete [] pBGRA;
}
}
// Hack for extended characters (like Kanji) that don't seem to report
// correct ABC widths. In this case, use the width calculated from
// drawing the glyph.
if( str[0] > 0x1000 )
{
POINT pos;
GetCurrentPositionEx( hDC, &pos );
abc.abcB = pos.x - sx;
if( abc.abcC < 0 )
abc.abcB -= abc.abcC;
w = abc.abcB;
}
}
// Store the glyph attributes
pGlyphs[numGlyphs].x = x;
pGlyphs[numGlyphs].y = y;
pGlyphs[numGlyphs].w = w;
pGlyphs[numGlyphs].h = h;
pGlyphs[numGlyphs].a = abc.abcA;
pGlyphs[numGlyphs].b = abc.abcB;
pGlyphs[numGlyphs].c = abc.abcC;
pGlyphs[numGlyphs].fLeft = ((FLOAT)(x+0)) / dwTextureWidth;
pGlyphs[numGlyphs].fTop = ((FLOAT)(y+0)) / dwTextureHeight;
pGlyphs[numGlyphs].fRight = ((FLOAT)(x+w)) / dwTextureWidth;
pGlyphs[numGlyphs].fBottom = ((FLOAT)(y+h)) / dwTextureHeight;
numGlyphs++;
// Advance the cursor to the next position
x += w + 1;
}
SelectClipRgn( hDC, NULL );
DeleteObject( rgn );
DeleteDC( hDC );
return pGlyphs;
}
//-----------------------------------------------------------------------------
// Name: RenderCustomGlyphs()
// Desc: Draws the list of font glyphs in the scroll view
//-----------------------------------------------------------------------------
GLYPH_ATTR* CTextureFont::RenderCustomGlyphs( HBITMAP hBitmap )
{
int i;
int w;
int h;
byte_t *pTGAPixels;
m_maxCustomCharHeight = 0;
// Create a DC
HDC hDC = CreateCompatibleDC( NULL );
// Associate the drawing surface
SelectObject( hDC, hBitmap );
// Create a clip region
HRGN rgn = CreateRectRgn( 0, 0, m_dwTextureWidth, m_dwTextureHeight );
SelectClipRgn( hDC, rgn );
// clear the background
unsigned char *pBGRA = GetTextureBits( hBitmap, m_dwTextureWidth, m_dwTextureHeight, 0, 0, m_dwTextureWidth, m_dwTextureHeight );
for (i=0; i<(int)(m_dwTextureHeight*m_dwTextureWidth); i++)
{
pBGRA[i*4+0] = 0xFF;
pBGRA[i*4+1] = 0x00;
pBGRA[i*4+2] = 0x00;
pBGRA[i*4+3] = 0x00;
}
SetTextureBits( hBitmap, m_dwTextureWidth, m_dwTextureHeight, 0, 0, m_dwTextureWidth, m_dwTextureHeight, pBGRA );
// build the glyph table
GLYPH_ATTR* pGlyphs = new GLYPH_ATTR[m_dwNumGlyphs];
memset( pGlyphs, 0, m_dwNumGlyphs*sizeof( GLYPH_ATTR ) );
int x = 0;
int y = 0;
int maxHeight = 0;
for( DWORD i=0; i<m_dwNumGlyphs; i++ )
{
if ( !i )
{
// account for null
continue;
}
else if ( TL_Exists( m_pCustomGlyphFiles[i-1] ) )
{
TL_LoadTGA( m_pCustomGlyphFiles[i-1], &pTGAPixels, &w, &h );
// convert to expected order
for (int j=0; j<h*w; j++)
{
int r = pTGAPixels[j*4+0];
int g = pTGAPixels[j*4+1];
int b = pTGAPixels[j*4+2];
pTGAPixels[j*4+0] = b;
pTGAPixels[j*4+1] = g;
pTGAPixels[j*4+2] = r;
}
}
else
{
// build a "bad" symbol
pTGAPixels = (byte_t*)TL_Malloc( 32*32*4 );
w = 32;
h = 32;
for (int j=0; j<32*32; j++)
{
pTGAPixels[j*4+0] = 0x00;
pTGAPixels[j*4+1] = 0x00;
pTGAPixels[j*4+2] = 0xFF;
pTGAPixels[j*4+3] = 0xFF;
}
}
if ( m_maxCustomCharHeight < h )
{
m_maxCustomCharHeight = h;
}
if ( maxHeight < h )
{
maxHeight = h;
}
if ( x + w > (int)m_dwTextureWidth )
{
// skip to new row
y += maxHeight;
x = 0;
maxHeight = h;
}
SetTextureBits( hBitmap, m_dwTextureWidth, m_dwTextureHeight, x, y, w, h, pTGAPixels );
TL_Free( pTGAPixels );
// Store the glyph attributes
pGlyphs[i].x = x;
pGlyphs[i].y = y;
pGlyphs[i].w = w;
pGlyphs[i].h = h;
pGlyphs[i].a = 0;
pGlyphs[i].b = w;
pGlyphs[i].c = 0;
pGlyphs[i].fLeft = ((FLOAT)(x+0)) / m_dwTextureWidth;
pGlyphs[i].fTop = ((FLOAT)(y+0)) / m_dwTextureHeight;
pGlyphs[i].fRight = ((FLOAT)(x+w)) / m_dwTextureWidth;
pGlyphs[i].fBottom = ((FLOAT)(y+h)) / m_dwTextureHeight;
x += w;
}
SelectClipRgn( hDC, NULL );
DeleteObject( rgn );
DeleteDC( hDC );
delete [] pBGRA;
return pGlyphs;
}
//-----------------------------------------------------------------------------
// Name: CalculateAndRenderGlyphs()
// Desc: Draws the list of font glyphs
//-----------------------------------------------------------------------------
HRESULT CTextureFont::CalculateAndRenderGlyphs()
{
// Create a bitmap
HBITMAP hBitmap = CreateBitmap( m_dwTextureWidth, m_dwTextureHeight, 1, 32, NULL );
if ( m_pGlyphs )
delete[] m_pGlyphs;
if ( m_pCustomFilename )
{
m_pGlyphs = RenderCustomGlyphs( hBitmap );
}
else
{
// Create a font
if ( m_hFont )
{
DeleteObject( m_hFont );
}
if ( m_bAntialiasEffect )
{
m_LogFont.lfQuality = ANTIALIASED_QUALITY;
}
else
{
m_LogFont.lfQuality = NONANTIALIASED_QUALITY;
}
m_hFont = CreateFontIndirect( &m_LogFont );
m_pGlyphs = RenderTTFGlyphs(
m_hFont,
hBitmap,
m_dwTextureWidth,
m_dwTextureHeight,
m_bOutlineEffect,
m_bShadowEffect,
m_nScanlines,
m_nBlur,
m_bAntialiasEffect,
m_ValidGlyphs,
m_dwNumGlyphs );
}
// Store the resulting bits
if ( m_pBits )
delete[] m_pBits;
m_pBits = new DWORD[ m_dwTextureWidth * m_dwTextureHeight ];
GetBitmapBits2( hBitmap, m_dwTextureWidth, m_dwTextureHeight, m_pBits );
DeleteObject( hBitmap );
return S_OK;
}