我正在创建一个 UI 引擎并处理字体渲染。我刚刚开始并尝试实现 FreeType 来初始加载和渲染 .ttf 文件。 这是我用于加载 .ttf 并创建 Direct2D 位图的代码:
FT_Error Error = FT_Init_FreeType(&Library);
assert(!Error);
FT_Face Face;
Error = FT_New_Face(Library, "Roboto.ttf", 0, &Face);
assert(!Error);
Error = FT_Set_Char_Size(Face, 0, 16 * 64, 96, 96);
assert(!Error);
std::cout << "Loading Font " << Face->family_name << "\n\tNum Glyphs: " << Face->num_glyphs << std::endl;
uint32 Index = FT_Get_Char_Index(Face, TEXT('L'));
std::cout << "\tCharacter 'L' is at index " << Index << "\n";
Error = FT_Load_Glyph(Face, Index, 0);
assert(!Error);
Error = FT_Render_Glyph(Face->glyph, FT_RENDER_MODE_NORMAL);
assert(!Error);
for (int i = 0; i < Face->glyph->bitmap.pitch; i++)
{
for (int j = 0; j < Face->glyph->bitmap.rows; j++)
{
std::cout << (uint8)Face->glyph->bitmap.buffer[i + j];
}
}
FT_Bitmap FTBitmap;
FT_Bitmap_New(&FTBitmap);
FT_Bitmap Src = Face->glyph->bitmap;
Error = FT_Bitmap_Convert(Library, &Src, &FTBitmap, 8);
assert(!Error);
auto render = static_cast<Direct2DRenderer*>(static_cast<WindowsWindow*>(Window)->GetRenderer());
D2D1_PIXEL_FORMAT format;
format.format = DXGI_FORMAT_B8G8R8A8_UNORM; // I have tried using R8_UNORM but it is not supported
format.alphaMode = D2D1_ALPHA_MODE_IGNORE;
render->GetRenderTarget()->CreateBitmap(D2D1::SizeU(FTBitmap.width, FTBitmap.pitch), FTBitmap.buffer, FTBitmap.pitch, D2D1::BitmapProperties(format), &Bitmap);
FT_Bitmap_Done(Library, &FTBitmap);
我已经尝试过许多 D2D 像素格式,尝试过 FT_RENDER_MODE_LCD(3x8 位通道)和 FT_RENDER_MODE_NORMAL(1x8 位通道),但要么我不完全理解这一点,要么我从一开始就做错了。任何帮助表示赞赏。还尝试过修改 FT_Set_Char_Size 值、D2D 像素格式值和 FT_Render_Glyph 值,但没有任何改变,无论如何也不认为它应该改变。
(Direct2DRenderer 类只是 HwndRenderTarget 的包装器,并不断渲染帧。另外,我发布的代码只是我试图让它工作,而不是传送代码,我知道它不漂亮!)
freetype 字形位图本质上是一个不透明蒙版,它是每个像素 1 字节(0-255 级别),就像 Alpha 通道位图。
Direct2D 的 ID2D1HwndRenderTarget 仅支持有限的一组格式,最有用的是 DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED,因此每像素格式 4 字节 (BGRA)。
以下是将字形格式转换为 Direct2D ID2D1Bitmap 的方法(省略错误检查):
auto bmpSize = D2D1::SizeU(ftb.width, ftb.rows);
D2D1_BITMAP_PROPERTIES props{};
props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
// build a memory buffer (1 pixel => 4 bytes)
// from glyph bitmap (1 pixel => 1 byte)
auto bits = new unsigned char[ftb.width * 4 * ftb.rows];
for (unsigned int row = 0; row < ftb.rows; row++)
{
auto ptr = (unsigned int*)bits + row * ftb.pitch;
for (unsigned int i = 0; i < ftb.width; i++)
{
auto opacity = (unsigned int)ftb.buffer[row * ftb.pitch + i];
*ptr = opacity << 24; // ARGB format with A = opacity from glyph
ptr++;
}
}
ID2D1Bitmap* bmp;
m_pRenderTarget->CreateBitmap(bmpSize, (const void*)bits, ftb.width * 4, props, &bmp);
delete[] bits;
...
// do some work with bitmap ...
...
bmp->Release();