diff options
Diffstat (limited to 'utils/xbox/FontMaker/glyphs.cpp')
| -rw-r--r-- | utils/xbox/FontMaker/glyphs.cpp | 1484 |
1 files changed, 1484 insertions, 0 deletions
diff --git a/utils/xbox/FontMaker/glyphs.cpp b/utils/xbox/FontMaker/glyphs.cpp new file mode 100644 index 0000000..c2328ac --- /dev/null +++ b/utils/xbox/FontMaker/glyphs.cpp @@ -0,0 +1,1484 @@ +//----------------------------------------------------------------------------- +// 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; +} |