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);
}
Beispiel #2
0
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 );
}