diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index de1d3cf91..012874c88 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,10 +53,13 @@ Other Changes: 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 6bfe7532f..61937da69 100644 --- a/imgui.h +++ b/imgui.h @@ -2504,8 +2504,9 @@ struct ImFontConfig // (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 b3e8de317..213ac4478 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3065,6 +3065,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa 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 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col 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 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col 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 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col } } + // 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 1c961617a..38e3588de 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2509,7 +2509,8 @@ IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); 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 97646a414..db1b68e53 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 @@ namespace // | | // |------------- 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 @@ namespace 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 @@ namespace 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 @@ namespace 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 @@ namespace } 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 @@ struct ImFontBuildSrcGlyphFT 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 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u atlas->ClearTexData(); // Temporary storage for building + bool src_load_color = false; ImVector src_tmp_array; ImVector dst_tmp_array; src_tmp_array.resize(atlas->ConfigData.Size); @@ -425,6 +422,7 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u 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 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u 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 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u // 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 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u 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 eb31ed2d9..d570f83a9 100644 --- a/misc/freetype/imgui_freetype.h +++ b/misc/freetype/imgui_freetype.h @@ -44,6 +44,6 @@ namespace ImGuiFreeType // 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 }