void CWin32Font::GetCharRGBA(wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba) { unsigned int glyphIndex = FT_Get_Char_Index(m_FTFace, ch); if (!glyphIndex || FT_Load_Glyph(m_FTFace, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_RENDER)) return; FT_GlyphSlot glyph = m_FTFace->glyph; FT_Bitmap &bitmap = glyph->bitmap; int srcY = 0; int dstY = m_iBaseline - glyph->bitmap_top; if (dstY < 0) { srcY = -dstY; dstY = 0; } dstY += m_iOutlineSize; int dstX = m_iOutlineSize; int wide = bitmap.width; if ((dstX + wide + m_iOutlineSize) > rgbaWide) wide = rgbaWide - dstX - m_iOutlineSize; int tall = bitmap.rows - srcY; if ((dstY + tall + m_iOutlineSize) > rgbaTall) tall = rgbaTall - dstY - m_iOutlineSize; dstX <<= 2; rgbaWide <<= 2; unsigned char *srcRow = bitmap.buffer + srcY * bitmap.pitch, *src; unsigned char *dstRow = rgba + dstY * rgbaWide, *dst; int x; for (; tall-- > 0; srcRow += bitmap.pitch, dstRow += rgbaWide) { for (x = wide, src = srcRow, dst = dstRow + dstX; x-- > 0; ++src, dst += 4) { if (!(*src)) continue; dst[0] = dst[1] = dst[2] = 255; dst[3] = *src; } } rgbaWide >>= 2; ApplyDropShadowToTexture(rgbaWide, rgbaTall, rgba, m_iDropShadowOffset); ApplyOutlineToTexture(rgbaWide, rgbaTall, rgba, m_iOutlineSize); ApplyGaussianBlurToTexture(rgbaWide, rgbaTall, rgba, m_iBlur); ApplyScanlineEffectToTexture(rgbaWide, rgbaTall, rgba, m_iScanLines); ApplyRotaryEffectToTexture(rgbaWide, rgbaTall, rgba, m_bRotary); }
void CWin32Font::GetCharRGBA(int ch, int rgbaX, int rgbaY, int rgbaWide, int rgbaTall, unsigned char *rgba) { int a, b, c; GetCharABCWidths(ch, a, b, c); ::SelectObject(m_hDC, m_hFont); int wide = b; if (m_bUnderlined) wide += (a + c); int tall = m_iHeight; GLYPHMETRICS glyphMetrics; MAT2 mat2 = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; int bytesNeeded = 0; bool bShouldAntialias = m_bAntiAliased; if (ch > 0x00FF && !(m_iFlags & vgui::ISurface::FONTFLAG_CUSTOM)) bShouldAntialias = false; if (!s_bSupportsUnicode) { if (ch == 'I' || ch == '1') bShouldAntialias = false; if (m_iHeight >= 13) bShouldAntialias = false; } if (bShouldAntialias) { ::SelectObject(m_hDC, m_hFont); bytesNeeded = ::GetGlyphOutlineW(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, 0, NULL, &mat2); } if (bytesNeeded > 0) { unsigned char *lpbuf = (unsigned char *)_alloca(bytesNeeded); ::GetGlyphOutlineW(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, bytesNeeded, lpbuf, &mat2); wide = glyphMetrics.gmBlackBoxX; while (wide % 4 != 0) wide++; int pushDown = m_iAscent - glyphMetrics.gmptGlyphOrigin.y; int xstart = 0; if ((int)glyphMetrics.gmBlackBoxX >= b + 2) xstart = (glyphMetrics.gmBlackBoxX - b) / 2; for (unsigned int j = 0; j < glyphMetrics.gmBlackBoxY; j++) { for (unsigned int i = xstart; i < glyphMetrics.gmBlackBoxX; i++) { int x = i - xstart + m_iBlur + m_iOutlineSize; int y = j + pushDown; if ((x < rgbaWide) && (y < rgbaTall)) { unsigned char grayscale = lpbuf[(j * wide + i)]; float r, g, b, a; if (grayscale) { r = g = b = 1.0f; a = (grayscale + 0) / 64.0f; if (a > 1.0f) a = 1.0f; } else r = g = b = a = 0.0f; if (ch == '\t') r = g = b = 0; unsigned char *dst = &rgba[(y*rgbaWide+x)*4]; dst[0] = (unsigned char)(r * 255.0f); dst[1] = (unsigned char)(g * 255.0f); dst[2] = (unsigned char)(b * 255.0f); dst[3] = (unsigned char)(a * 255.0f); } } } } else { ::SetBkColor(m_hDC, RGB(0, 0, 0)); ::SetTextColor(m_hDC, RGB(255, 255, 255)); ::SetBkMode(m_hDC, OPAQUE); if (m_bUnderlined) ::MoveToEx(m_hDC, 0, 0, NULL); else ::MoveToEx(m_hDC, -a, 0, NULL); wchar_t wch = (wchar_t)ch; if (s_bSupportsUnicode) { ::ExtTextOutW(m_hDC, 0, 0, 0, NULL, &wch, 1, NULL); } else { RECT rect = { 0, 0, wide, tall}; ::ExtTextOut(m_hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); char mbcs[6] = { 0 }; ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL); ::ExtTextOutA(m_hDC, 0, 0, 0, NULL, mbcs, strlen(mbcs), NULL); } ::SetBkMode(m_hDC, TRANSPARENT); if (wide > m_rgiBitmapSize[0]) wide = m_rgiBitmapSize[0]; if (tall > m_rgiBitmapSize[1]) tall = m_rgiBitmapSize[1]; for (int j = (int)m_iOutlineSize; j < tall - (int)m_iOutlineSize; j++) { for (int i = (int)m_iOutlineSize; i < wide - (int)m_iDropShadowOffset - (int)m_iOutlineSize; i++) { if ((i < rgbaWide) && (j < rgbaTall)) { unsigned char *src = &m_pBuf[(i + j*m_rgiBitmapSize[0])*4]; unsigned char *dst = &rgba[(i + j*rgbaWide)*4]; unsigned char r, g, b; if (ch == '\t') { r = g = b = 0; } else { r = src[0]; g = src[1]; b = src[2]; } dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = (unsigned char)((float)r * 0.34f + (float)g * 0.55f + (float)b * 0.11f); } } } if (m_iDropShadowOffset) { unsigned char *dst = &rgba[((m_iHeight - 1) * rgbaWide) * 4]; for (int i = 0; i < wide; i++) { dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst += 4; } } } if (m_iDropShadowOffset) ApplyDropShadowToTexture(rgbaX, rgbaY, rgbaWide, rgbaTall, wide, tall, rgba); if (m_iOutlineSize) ApplyOutlineToTexture(rgbaX, rgbaY, rgbaWide, rgbaTall, wide, tall, rgba); ApplyGaussianBlurToTexture(rgbaX, rgbaY, rgbaWide, rgbaTall, rgba); ApplyScanlineEffectToTexture(rgbaX, rgbaY, rgbaWide, rgbaTall, rgba); ApplyRotaryEffectToTexture(rgbaX, rgbaY, rgbaWide, rgbaTall, rgba); }
//----------------------------------------------------------------------------- // Purpose: writes the char into the specified 32bpp texture //----------------------------------------------------------------------------- void CWin32Font::GetCharRGBA(wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba) { int a, b, c; GetCharABCWidths(ch, a, b, c); // set us up to render into our dib ::SelectObject(m_hDC, m_hFont); int wide = b; if ( m_bUnderlined ) { wide += ( a + c ); } int tall = m_iHeight; GLYPHMETRICS glyphMetrics; MAT2 mat2 = { { 0, 1}, { 0, 0}, { 0, 0}, { 0, 1}}; int bytesNeeded = 0; bool bShouldAntialias = m_bAntiAliased; // filter out if ( ch > 0x00FF && !(m_iFlags & vgui::ISurface::FONTFLAG_CUSTOM) ) { bShouldAntialias = false; } if ( !s_bSupportsUnicode ) { // win98 hack, don't antialias some characters that ::GetGlyphOutline() produces bad results for if (ch == 'I' || ch == '1') { bShouldAntialias = false; } // don't antialias big fonts at all (since win98 often produces bad results) if (m_iHeight >= 13) { bShouldAntialias = false; } } // only antialias latin characters, since it essentially always fails for asian characters if (bShouldAntialias) { // try and get the glyph directly ::SelectObject(m_hDC, m_hFont); bytesNeeded = ::GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, 0, NULL, &mat2); } if (bytesNeeded > 0) { // take it unsigned char *lpbuf = (unsigned char *)_alloca(bytesNeeded); ::GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, bytesNeeded, lpbuf, &mat2); // rows are on DWORD boundaries wide = glyphMetrics.gmBlackBoxX; while (wide % 4 != 0) { wide++; } // see where we should start rendering int pushDown = m_iAscent - glyphMetrics.gmptGlyphOrigin.y; // set where we start copying from int xstart = 0; // don't copy the first set of pixels if the antialiased bmp is bigger than the char width if ((int)glyphMetrics.gmBlackBoxX >= b + 2) { xstart = (glyphMetrics.gmBlackBoxX - b) / 2; } // iterate through copying the generated dib into the texture for (unsigned int j = 0; j < glyphMetrics.gmBlackBoxY; j++) { for (unsigned int i = xstart; i < glyphMetrics.gmBlackBoxX; i++) { int x = i - xstart + m_iBlur + m_iOutlineSize; int y = j + pushDown; if ((x < rgbaWide) && (y < rgbaTall)) { unsigned char grayscale = lpbuf[(j*wide+i)]; float r, g, b, a; if (grayscale) { r = g = b = 1.0f; a = (grayscale + 0) / 64.0f; if (a > 1.0f) a = 1.0f; } else { r = g = b = a = 0.0f; } // Don't want anything drawn for tab characters. if (ch == '\t') { r = g = b = 0; } unsigned char *dst = &rgba[(y*rgbaWide+x)*4]; dst[0] = (unsigned char)(r * 255.0f); dst[1] = (unsigned char)(g * 255.0f); dst[2] = (unsigned char)(b * 255.0f); dst[3] = (unsigned char)(a * 255.0f); } } } } else { // use render-to-bitmap to get our font texture ::SetBkColor(m_hDC, RGB(0, 0, 0)); ::SetTextColor(m_hDC, RGB(255, 255, 255)); ::SetBkMode(m_hDC, OPAQUE); if ( m_bUnderlined ) { ::MoveToEx(m_hDC, 0, 0, NULL); } else { ::MoveToEx(m_hDC, -a, 0, NULL); } // render the character wchar_t wch = (wchar_t)ch; if (s_bSupportsUnicode) { // just use the unicode renderer ::ExtTextOutW(m_hDC, 0, 0, 0, NULL, &wch, 1, NULL); } else { // clear the background first (it may not get done automatically in win98/ME RECT rect = { 0, 0, wide, tall}; ::ExtTextOut(m_hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); // convert the character using the current codepage char mbcs[6] = { 0 }; ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL); ::ExtTextOutA(m_hDC, 0, 0, 0, NULL, mbcs, strlen(mbcs), NULL); } ::SetBkMode(m_hDC, TRANSPARENT); if (wide > m_rgiBitmapSize[0]) { wide = m_rgiBitmapSize[0]; } if (tall > m_rgiBitmapSize[1]) { tall = m_rgiBitmapSize[1]; } // iterate through copying the generated dib into the texture for (int j = (int)m_iOutlineSize; j < tall - (int)m_iOutlineSize; j++ ) { // only copy from within the dib, ignore the outline border we are artificially adding for (int i = (int)m_iOutlineSize; i < wide - (int)m_iDropShadowOffset - (int)m_iOutlineSize; i++) { if ((i < rgbaWide) && (j < rgbaTall)) { unsigned char *src = &m_pBuf[(i + j*m_rgiBitmapSize[0])*4]; unsigned char *dst = &rgba[(i + j*rgbaWide)*4]; // Don't want anything drawn for tab characters. unsigned char r, g, b; if ( ch == '\t' ) { r = g = b = 0; } else { r = src[0]; g = src[1]; b = src[2]; } // generate alpha based on luminance conversion dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = (unsigned char)((float)r * 0.34f + (float)g * 0.55f + (float)b * 0.11f); } } } // if we have a dropshadow, we need to clean off the bottom row of pixels // this is because of a bug in winME that writes noise to them, only on the first time the game is run after a reboot // the bottom row should guaranteed to be empty to fit the dropshadow if ( m_iDropShadowOffset ) { unsigned char *dst = &rgba[((m_iHeight - 1) * rgbaWide) * 4]; for (int i = 0; i < wide; i++) { dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst += 4; } } } // apply requested effects in specified order ApplyDropShadowToTexture( rgbaWide, rgbaTall, rgba, m_iDropShadowOffset ); ApplyOutlineToTexture( rgbaWide, rgbaTall, rgba, m_iOutlineSize ); ApplyGaussianBlurToTexture( rgbaWide, rgbaTall, rgba, m_iBlur ); ApplyScanlineEffectToTexture( rgbaWide, rgbaTall, rgba, m_iScanLines ); ApplyRotaryEffectToTexture( rgbaWide, rgbaTall, rgba, m_bRotary ); }