|
|
//-----------------------------------------------------------------------------
// 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; }
|