imgui_freetype: Add support for colored glyphs. Font: add support for untinted glyphs (#3369)
Amend 9499afd with missing static inline.
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index de1d3cf..012874c 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -53,10 +53,13 @@
other than compiling misc/freetype/imgui_freetype.cpp and linking with FreeType.
- Use '#define IMGUI_ENABLE_STB_TRUETYPE' if you somehow need the stb_truetype rasterizer to be
compiled in along with the FreeType one, otherwise it is enabled by default.
+- imgui_freetype: Added support for colored glyphs as supported by Freetype 2.10+ (for .ttf using CPAL/COLR
+ tables only). Enable the ImGuiFreeTypeBuilderFlags_LoadColor on a given font. Atlas always output directly
+ as RGBA8 in this situation. Likely to make sense with IMGUI_USE_WCHAR32. (#3369) [@pshurgal]
- Fonts: Fixed CalcTextSize() width rounding so it behaves more like a ceil. This is in order for text wrapping
to have enough space when provided width precisely calculated with CalcTextSize().x. (#3776)
Note that the rounding of either positions and widths are technically undesirable (e.g. #3437, #791) but
- variety of code is currently on it so we are first fixing current behavior before we'll eventually chhnge it.
+ variety of code is currently on it so we are first fixing current behavior before we'll eventually change it.
- ImDrawList: Fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738)
Would lead to a buffer read overflow.
- Backends: Win32: Dynamically loading XInput DLL instead of linking with it, facilite compiling with
diff --git a/imgui.h b/imgui.h
index 6bfe753..61937da 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2504,8 +2504,9 @@
// (Note: some language parsers may fail to convert the 31+1 bitfield members, in this case maybe drop store a single u32 or we can rework this)
struct ImFontGlyph
{
- unsigned int Codepoint : 31; // 0x0000..0xFFFF
- unsigned int Visible : 1; // Flag to allow early out when rendering
+ unsigned int Colored : 1; // Flag to indicate glyph is colored and should generally ignore tinting (make it usable with no shift on little-endian as this is used in loops)
+ unsigned int Visible : 1; // Flag to indicate glyph has no visible pixels (e.g. space). Allow early out when rendering.
+ unsigned int Codepoint : 30; // 0x0000..0x10FFFF
float AdvanceX; // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in)
float X0, Y0, X1, Y1; // Glyph corners
float U0, V0, U1, V1; // Texture coordinates
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index b3e8de3..213ac44 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -3065,6 +3065,7 @@
ImFontGlyph& glyph = Glyphs.back();
glyph.Codepoint = (unsigned int)codepoint;
glyph.Visible = (x0 != x1) && (y0 != y1);
+ glyph.Colored = false;
glyph.X0 = x0;
glyph.Y0 = y0;
glyph.X1 = x1;
@@ -3315,6 +3316,8 @@
const ImFontGlyph* glyph = FindGlyph(c);
if (!glyph || !glyph->Visible)
return;
+ if (glyph->Colored)
+ col |= ~IM_COL32_A_MASK;
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
pos.x = IM_FLOOR(pos.x);
pos.y = IM_FLOOR(pos.y);
@@ -3377,6 +3380,8 @@
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;
+ const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
+
while (s < text_end)
{
if (word_wrap_enabled)
@@ -3482,14 +3487,17 @@
}
}
+ // Support for untinted glyphs
+ ImU32 glyph_col = glyph->Colored ? col_untinted : col;
+
// We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
{
idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2);
idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3);
- vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;
- vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;
- vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;
- vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;
+ vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;
+ vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;
+ vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;
+ vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;
vtx_write += 4;
vtx_current_idx += 4;
idx_write += 6;
diff --git a/imgui_internal.h b/imgui_internal.h
index 1c96161..38e3588 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2509,7 +2509,8 @@
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent);
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);
IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas);
-IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int atlas_x, int atlas_y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value);
+IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value);
+IMGUI_API 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);
IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);
IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);
diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp
index 97646a4..db1b68e 100644
--- a/misc/freetype/imgui_freetype.cpp
+++ b/misc/freetype/imgui_freetype.cpp
@@ -6,6 +6,7 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
+// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'.
// renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
@@ -96,7 +97,7 @@
// | |
// |------------- advanceX ----------->|
- /// A structure that describe a glyph.
+ // A structure that describe a glyph.
struct GlyphInfo
{
int Width; // Glyph's width in pixels.
@@ -104,6 +105,7 @@
FT_Int OffsetX; // The distance from the origin ("pen position") to the left of the glyph.
FT_Int OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0.
float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
+ bool IsColored;
};
// Font parameters and metrics.
@@ -252,6 +254,7 @@
out_glyph_info->OffsetX = Face->glyph->bitmap_left;
out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
+ out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
return ft_bitmap;
}
@@ -271,18 +274,14 @@
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(255, 255, 255, src[x]);
- }
}
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(255, 255, 255, multiply_table[src[x]]);
- }
}
break;
}
@@ -305,33 +304,28 @@
}
case FT_PIXEL_MODE_BGRA:
{
+ // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
#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]);
- }
+ {
+ uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
+ dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
+ }
}
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]]);
+ {
+ uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
+ dst[x] = IM_COL32(multiply_table[DE_MULTIPLY(r, a)], multiply_table[DE_MULTIPLY(g, a)], multiply_table[DE_MULTIPLY(b, a)], multiply_table[a]);
+ }
}
}
-
#undef DE_MULTIPLY
break;
}
@@ -359,6 +353,8 @@
GlyphInfo Info;
uint32_t Codepoint;
unsigned int* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
+
+ ImFontBuildSrcGlyphFT() { memset(this, 0, sizeof(*this)); }
};
struct ImFontBuildSrcDataFT
@@ -396,6 +392,7 @@
atlas->ClearTexData();
// Temporary storage for building
+ bool src_load_color = false;
ImVector<ImFontBuildSrcDataFT> src_tmp_array;
ImVector<ImFontBuildDstDataFT> dst_tmp_array;
src_tmp_array.resize(atlas->ConfigData.Size);
@@ -425,6 +422,7 @@
return false;
// Measure highest codepoints
+ src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0;
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
@@ -476,7 +474,6 @@
if (entries_32 & ((ImU32)1 << bit_n))
{
ImFontBuildSrcGlyphFT src_glyph;
- memset(&src_glyph, 0, sizeof(src_glyph));
src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
//src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
src_tmp.GlyphsList.push_back(src_glyph);
@@ -595,7 +592,7 @@
// 7. Allocate texture
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
- if (extra_flags & ImGuiFreeTypeBuilderFlags_LoadColor)
+ if (src_load_color)
{
atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight * 4);
memset(atlas->TexPixelsRGBA32, 0, atlas->TexWidth * atlas->TexHeight * 4);
@@ -670,6 +667,10 @@
float u1 = (tx + info.Width) / (float)atlas->TexWidth;
float v1 = (ty + info.Height) / (float)atlas->TexHeight;
dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX);
+
+ IM_ASSERT(dst_font->Glyphs.back().Codepoint == src_glyph.Codepoint);
+ if (src_glyph.Info.IsColored)
+ dst_font->Glyphs.back().Colored = true;
}
src_tmp.Rects = NULL;
diff --git a/misc/freetype/imgui_freetype.h b/misc/freetype/imgui_freetype.h
index eb31ed2..d570f83 100644
--- a/misc/freetype/imgui_freetype.h
+++ b/misc/freetype/imgui_freetype.h
@@ -44,6 +44,6 @@
// Obsolete names (will be removed soon)
// Prefer using '#define IMGUI_ENABLE_FREETYPE'
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
- bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); }
+ static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); }
#endif
}