//----------------------------------------------------------------------------- // 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_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= 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 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> 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= bitmapHeight ) { // past end of bitmap break; } unsigned char *pDst = pBitmapBits + (yy*bitmapWidth + x)*4; for (int xx=0; xx= bitmapHeight ) { // past last row of bitmap break; } unsigned char *pSrc = pBitmapBits + (yy*bitmapWidth + x)*4; for (int xx=0; xx 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 (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; }