From 61f14679d31176c9f3a709f3129d607fac3596cd Mon Sep 17 00:00:00 2001 From: Petr Shurgalin Date: Thu, 30 Jul 2020 12:26:23 +0300 Subject: [PATCH 1/4] FreeType: enable colored-glyphs --- .github/workflows/build.yml | 2 +- imgui_draw.cpp | 61 +++++++++++++++++++++++++---- misc/freetype/imgui_freetype.cpp | 67 +++++++++++++++++++++++++------- misc/freetype/imgui_freetype.h | 3 +- 4 files changed, 108 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b3b910f2d89f..274ca2a6841c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -180,7 +180,7 @@ jobs: run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx12/example_win32_directx12.vcxproj /p:Platform=x64 /p:Configuration=Release' Linux: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v1 with: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index ad44b671c6e8..b0f1cdc5d912 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2376,6 +2376,16 @@ void ImFontAtlasBuildRender1bppRectFromString(ImFontAtlas* atlas, int x, int y, 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) { ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdMouseCursors); @@ -2388,15 +2398,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); const int x_for_white = r->X; const int x_for_black = r->X + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1; - ImFontAtlasBuildRender1bppRectFromString(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); - ImFontAtlasBuildRender1bppRectFromString(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); + if (atlas->TexPixelsAlpha8 != NULL) + { + ImFontAtlasBuildRender1bppRectFromString(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); + ImFontAtlasBuildRender1bppRectFromString(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 { // Render 4 white pixels IM_ASSERT(r->Width == 2 && r->Height == 2); 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); } @@ -2419,10 +2444,30 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas) // 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 - unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)]; - memset(write_ptr, 0x00, pad_left); - memset(write_ptr + pad_left, 0xFF, line_width); - memset(write_ptr + pad_left + line_width, 0x00, pad_right); + if (atlas->TexPixelsAlpha8 != NULL) + { + unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)]; + 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 ImVec2 uv0 = ImVec2((float)(r->X + pad_left - 1), (float)(r->Y + y)) * atlas->TexUvScale; @@ -2457,7 +2502,7 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { // Render into our custom data blocks - IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); + IM_ASSERT(atlas->TexPixelsAlpha8 != NULL || atlas->TexPixelsRGBA32 != NULL); ImFontAtlasBuildRenderDefaultTexData(atlas); ImFontAtlasBuildRenderLinesTexData(atlas); diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index b76bc6e0844a..cb0a0af87bc4 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -104,7 +104,7 @@ namespace 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_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(); } // [Internals] @@ -151,6 +151,9 @@ namespace else RenderMode = FT_RENDER_MODE_NORMAL; + if (UserFlags & ImGuiFreeType::LoadColor) + LoadFlags |= FT_LOAD_COLOR; + return true; } @@ -231,7 +234,7 @@ namespace 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); const uint32_t w = ft_bitmap->width; @@ -246,13 +249,18 @@ namespace if (multiply_table == NULL) { 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 { for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) + { 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; } @@ -268,7 +276,35 @@ namespace { if ((x & 7) == 0) bits = *bits_ptr++; - dst[x] = (bits & 0x80) ? color1 : color0; + dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0); + } + } + break; + } + case FT_PIXEL_MODE_BGRA: + { + 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( + src[x * 4], + src[x * 4 + 1], + src[x * 4 + 2], + 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[src[x * 4]], + multiply_table[src[x * 4 + 1]], + multiply_table[src[x * 4 + 2]], + multiply_table[src[x * 4 + 3]]); } } break; @@ -296,7 +332,7 @@ struct ImFontBuildSrcGlyphFT { GlyphInfo Info; 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 @@ -476,7 +512,7 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns IM_ASSERT(ft_bitmap); // 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) { buf_bitmap_current_used_bytes = 0; @@ -484,9 +520,9 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns } // 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; - 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].h = (stbrp_coord)(src_glyph.Info.Height + padding); @@ -533,8 +569,8 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns // 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); - atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight); - 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); // 8. Copy rasterized font characters back into the main texture // 9. Setup ImFont and glyphs for runtime @@ -571,10 +607,11 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns // Blit from temporary buffer to final texture size_t blit_src_stride = (size_t)src_glyph.Info.Width; size_t blit_dst_stride = (size_t)atlas->TexWidth; - unsigned char* blit_src = src_glyph.BitmapData; - unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx; - 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 int* blit_src = src_glyph.BitmapData; + 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]; float char_advance_x_org = info.AdvanceX; float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX); diff --git a/misc/freetype/imgui_freetype.h b/misc/freetype/imgui_freetype.h index 619735c45cd6..3b91232d4a10 100644 --- a/misc/freetype/imgui_freetype.h +++ b/misc/freetype/imgui_freetype.h @@ -25,7 +25,8 @@ namespace ImGuiFreeType MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output. Bold = 1 << 5, // Styling: Should we artificially embolden the font? Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style? - Monochrome = 1 << 7 // Disable anti-aliasing. Combine this with MonoHinting for best results! + Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results! + LoadColor = 1 << 8 // Enable FreeType color-layered glyphs }; IMGUI_API bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags = 0); From 65ebfe43a5b01f3b6fdbda57da541fda382ff252 Mon Sep 17 00:00:00 2001 From: Petr Shurgalin Date: Thu, 30 Jul 2020 14:28:01 +0300 Subject: [PATCH 2/4] FreeType: allocate 32-bit buffer only if LoadColor enabled --- misc/freetype/imgui_freetype.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index cb0a0af87bc4..abee6a723196 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -569,8 +569,16 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns // 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); - atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight * 4); - memset(atlas->TexPixelsRGBA32, 0, atlas->TexWidth * atlas->TexHeight * 4); + if ((extra_flags & ImGuiFreeType::RasterizerFlags::LoadColor) == ImGuiFreeType::RasterizerFlags::LoadColor) + { + 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 // 9. Setup ImFont and glyphs for runtime @@ -608,10 +616,20 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns size_t blit_src_stride = (size_t)src_glyph.Info.Width; size_t blit_dst_stride = (size_t)atlas->TexWidth; unsigned int* blit_src = src_glyph.BitmapData; - 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]; + if (atlas->TexPixelsAlpha8 != NULL) + { + 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]; + } float char_advance_x_org = info.AdvanceX; float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX); From b1ce57175402253fc60fec1692d7ec3816243203 Mon Sep 17 00:00:00 2001 From: Petr Shurgalin Date: Sat, 8 Aug 2020 19:40:10 +0300 Subject: [PATCH 3/4] FreeType: fix color components order --- misc/freetype/imgui_freetype.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index abee6a723196..040cc365926b 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -289,9 +289,9 @@ namespace { for (uint32_t x = 0; x < w; x++) dst[x] = IM_COL32( - src[x * 4], - src[x * 4 + 1], src[x * 4 + 2], + src[x * 4 + 1], + src[x * 4], src[x * 4 + 3]); } } @@ -301,9 +301,9 @@ namespace { for (uint32_t x = 0; x < w; x++) dst[x] = IM_COL32( - multiply_table[src[x * 4]], - multiply_table[src[x * 4 + 1]], multiply_table[src[x * 4 + 2]], + multiply_table[src[x * 4 + 1]], + multiply_table[src[x * 4]], multiply_table[src[x * 4 + 3]]); } } From 1cc96e735da7367ed8979a288c72123e1ef90017 Mon Sep 17 00:00:00 2001 From: Petr Shurgalin Date: Fri, 2 Oct 2020 18:03:32 +0300 Subject: [PATCH 4/4] FreeType: convert premultiplied alpha to straight --- misc/freetype/imgui_freetype.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 040cc365926b..de35ee22bc1c 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -283,15 +283,17 @@ namespace } 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( - src[x * 4 + 2], - src[x * 4 + 1], - src[x * 4], + 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]); } } @@ -301,12 +303,14 @@ namespace { for (uint32_t x = 0; x < w; x++) dst[x] = IM_COL32( - multiply_table[src[x * 4 + 2]], - multiply_table[src[x * 4 + 1]], - multiply_table[src[x * 4]], + 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: