imgui_freetype: add support for colored glyphs with ImGuiFreeTypeBuilderFlags_LoadColor (#3369)

(squashed 4 commits)
This commit is contained in:
Petr Shurgalin 2021-01-28 16:57:37 +01:00 committed by ocornut
parent 4622fa4b66
commit 24aa6654df
3 changed files with 129 additions and 24 deletions

View File

@ -2457,6 +2457,16 @@ void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y,
out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : 0x00; out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : 0x00;
} }
void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value)
{
IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth);
IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight);
unsigned int* out_pixel = atlas->TexPixelsRGBA32 + x + (y * atlas->TexWidth);
for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexWidth, in_str += w)
for (int off_x = 0; off_x < w; off_x++)
out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : IM_COL32_BLACK_TRANS;
}
static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)
{ {
ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdMouseCursors); ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdMouseCursors);
@ -2469,15 +2479,30 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)
IM_ASSERT(r->Width == FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1 && r->Height == FONT_ATLAS_DEFAULT_TEX_DATA_H); IM_ASSERT(r->Width == FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1 && r->Height == FONT_ATLAS_DEFAULT_TEX_DATA_H);
const int x_for_white = r->X; const int x_for_white = r->X;
const int x_for_black = r->X + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1; const int x_for_black = r->X + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;
ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', 0xFF); if (atlas->TexPixelsAlpha8 != NULL)
ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', 0xFF); {
ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', 0xFF);
ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', 0xFF);
}
else
{
ImFontAtlasBuildRender32bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', IM_COL32_WHITE);
ImFontAtlasBuildRender32bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', IM_COL32_WHITE);
}
} }
else else
{ {
// Render 4 white pixels // Render 4 white pixels
IM_ASSERT(r->Width == 2 && r->Height == 2); IM_ASSERT(r->Width == 2 && r->Height == 2);
const int offset = (int)r->X + (int)r->Y * w; const int offset = (int)r->X + (int)r->Y * w;
atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF; if (atlas->TexPixelsAlpha8 != NULL)
{
atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF;
}
else
{
atlas->TexPixelsRGBA32[offset] = atlas->TexPixelsRGBA32[offset + 1] = atlas->TexPixelsRGBA32[offset + w] = atlas->TexPixelsRGBA32[offset + w + 1] = IM_COL32_WHITE;
}
} }
atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y);
} }
@ -2500,10 +2525,30 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
// Write each slice // Write each slice
IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels
unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)]; if (atlas->TexPixelsAlpha8 != NULL)
memset(write_ptr, 0x00, pad_left); {
memset(write_ptr + pad_left, 0xFF, line_width); unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)];
memset(write_ptr + pad_left + line_width, 0x00, pad_right); for (unsigned int i = 0; i < pad_left; i++)
*(write_ptr + i) = 0x00;
for (unsigned int i = 0; i < line_width; i++)
*(write_ptr + pad_left + i) = 0xFF;
for (unsigned int i = 0; i < pad_right; i++)
*(write_ptr + pad_left + line_width + i) = 0x00;
}
else
{
unsigned int* write_ptr = &atlas->TexPixelsRGBA32[r->X + ((r->Y + y) * atlas->TexWidth)];
for (unsigned int i = 0; i < pad_left; i++)
*(write_ptr + i) = IM_COL32_BLACK_TRANS;
for (unsigned int i = 0; i < line_width; i++)
*(write_ptr + pad_left + i) = IM_COL32_WHITE;
for (unsigned int i = 0; i < pad_right; i++)
*(write_ptr + pad_left + line_width + i) = IM_COL32_BLACK_TRANS;
}
// Calculate UVs for this line // Calculate UVs for this line
ImVec2 uv0 = ImVec2((float)(r->X + pad_left - 1), (float)(r->Y + y)) * atlas->TexUvScale; ImVec2 uv0 = ImVec2((float)(r->X + pad_left - 1), (float)(r->Y + y)) * atlas->TexUvScale;
@ -2538,7 +2583,7 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas)
void ImFontAtlasBuildFinish(ImFontAtlas* atlas) void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
{ {
// Render into our custom data blocks // Render into our custom data blocks
IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); IM_ASSERT(atlas->TexPixelsAlpha8 != NULL || atlas->TexPixelsRGBA32 != NULL);
ImFontAtlasBuildRenderDefaultTexData(atlas); ImFontAtlasBuildRenderDefaultTexData(atlas);
ImFontAtlasBuildRenderLinesTexData(atlas); ImFontAtlasBuildRenderLinesTexData(atlas);

View File

@ -126,7 +126,7 @@ namespace
void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint); const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info); const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
void BlitGlyph(const FT_Bitmap* ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL); void BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL);
~FreeTypeFont() { CloseFont(); } ~FreeTypeFont() { CloseFont(); }
// [Internals] // [Internals]
@ -173,6 +173,9 @@ namespace
else else
RenderMode = FT_RENDER_MODE_NORMAL; RenderMode = FT_RENDER_MODE_NORMAL;
if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor)
LoadFlags |= FT_LOAD_COLOR;
return true; return true;
} }
@ -253,7 +256,7 @@ namespace
return ft_bitmap; return ft_bitmap;
} }
void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table) void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
{ {
IM_ASSERT(ft_bitmap != NULL); IM_ASSERT(ft_bitmap != NULL);
const uint32_t w = ft_bitmap->width; const uint32_t w = ft_bitmap->width;
@ -268,13 +271,18 @@ namespace
if (multiply_table == NULL) if (multiply_table == NULL)
{ {
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
memcpy(dst, src, w); {
for (uint32_t x = 0; x < w; x++)
dst[x] = IM_COL32(255, 255, 255, src[x]);
}
} }
else else
{ {
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
{
for (uint32_t x = 0; x < w; x++) for (uint32_t x = 0; x < w; x++)
dst[x] = multiply_table[src[x]]; dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]);
}
} }
break; break;
} }
@ -290,11 +298,43 @@ namespace
{ {
if ((x & 7) == 0) if ((x & 7) == 0)
bits = *bits_ptr++; bits = *bits_ptr++;
dst[x] = (bits & 0x80) ? color1 : color0; dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0);
} }
} }
break; break;
} }
case FT_PIXEL_MODE_BGRA:
{
#define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f)
if (multiply_table == NULL)
{
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
{
for (uint32_t x = 0; x < w; x++)
dst[x] = IM_COL32(
DE_MULTIPLY(src[x * 4 + 2], src[x * 4 + 3]),
DE_MULTIPLY(src[x * 4 + 1], src[x * 4 + 3]),
DE_MULTIPLY(src[x * 4], src[x * 4 + 3]),
src[x * 4 + 3]);
}
}
else
{
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
{
for (uint32_t x = 0; x < w; x++)
dst[x] = IM_COL32(
multiply_table[DE_MULTIPLY(src[x * 4 + 2], src[x * 4 + 3])],
multiply_table[DE_MULTIPLY(src[x * 4 + 1], src[x * 4 + 3])],
multiply_table[DE_MULTIPLY(src[x * 4], src[x * 4 + 3])],
multiply_table[src[x * 4 + 3]]);
}
}
#undef DE_MULTIPLY
break;
}
default: default:
IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!"); IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
} }
@ -318,7 +358,7 @@ struct ImFontBuildSrcGlyphFT
{ {
GlyphInfo Info; GlyphInfo Info;
uint32_t Codepoint; uint32_t Codepoint;
unsigned char* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array unsigned int* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
}; };
struct ImFontBuildSrcDataFT struct ImFontBuildSrcDataFT
@ -498,7 +538,7 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
IM_ASSERT(ft_bitmap); IM_ASSERT(ft_bitmap);
// Allocate new temporary chunk if needed // Allocate new temporary chunk if needed
const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height; const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4;
if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE) if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
{ {
buf_bitmap_current_used_bytes = 0; buf_bitmap_current_used_bytes = 0;
@ -506,9 +546,9 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
} }
// Blit rasterized pixels to our temporary buffer and keep a pointer to it. // Blit rasterized pixels to our temporary buffer and keep a pointer to it.
src_glyph.BitmapData = buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes; src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes);
buf_bitmap_current_used_bytes += bitmap_size_in_bytes; buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width * 1, multiply_enabled ? multiply_table : NULL); src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : NULL);
src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding); src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding); src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
@ -555,8 +595,16 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
// 7. Allocate texture // 7. Allocate texture
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight); if (extra_flags & ImGuiFreeTypeBuilderFlags_LoadColor)
memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); {
atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight * 4);
memset(atlas->TexPixelsRGBA32, 0, atlas->TexWidth * atlas->TexHeight * 4);
}
else
{
atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight);
memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
}
// 8. Copy rasterized font characters back into the main texture // 8. Copy rasterized font characters back into the main texture
// 9. Setup ImFont and glyphs for runtime // 9. Setup ImFont and glyphs for runtime
@ -596,10 +644,21 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
// Blit from temporary buffer to final texture // Blit from temporary buffer to final texture
size_t blit_src_stride = (size_t)src_glyph.Info.Width; size_t blit_src_stride = (size_t)src_glyph.Info.Width;
size_t blit_dst_stride = (size_t)atlas->TexWidth; size_t blit_dst_stride = (size_t)atlas->TexWidth;
unsigned char* blit_src = src_glyph.BitmapData; unsigned int* blit_src = src_glyph.BitmapData;
unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx; if (atlas->TexPixelsAlpha8 != NULL)
for (int y = info.Height; y > 0; y--, blit_dst += blit_dst_stride, blit_src += blit_src_stride) {
memcpy(blit_dst, blit_src, blit_src_stride); unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
for (int x = 0; x < info.Width; x++)
blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF);
}
else
{
unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx;
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
for (int x = 0; x < info.Width; x++)
blit_dst[x] = blit_src[x];
}
// Register glyph // Register glyph
float x0 = info.OffsetX + font_off_x; float x0 = info.OffsetX + font_off_x;

View File

@ -25,7 +25,8 @@ enum ImGuiFreeTypeBuilderFlags
ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output. ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font? ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style? ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7 // Disable anti-aliasing. Combine this with MonoHinting for best results! ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8 // Enable FreeType color-layered glyphs
}; };
namespace ImGuiFreeType namespace ImGuiFreeType