ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
61.41k stars 10.34k forks source link

A colored icon glyph loses its color in light mode but keeps intact in dark/classic mode. #8133

Closed IniKiwi closed 2 weeks ago

IniKiwi commented 3 weeks ago

Version/Branch of Dear ImGui:

Version latest, Branch: docking

Back-ends:

imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp

Compiler, OS:

Linux Debian 12

Full config/build information:

Dear ImGui 1.91.5 WIP (19141)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=201703
define: __linux__
define: __GNUC__=13
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: imgui_impl_glfw
io.BackendRendererName: imgui_impl_opengl3
io.ConfigFlags: 0x00000081
 NavEnableKeyboard
 DockingEnable
io.ConfigViewportsNoDecoration
io.ConfigNavCaptureKeyboard
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigWindowsMoveFromTitleBarOnly
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
 RendererHasViewports
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,64
io.DisplaySize: 1280.00,720.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

I'm trying to add icons to a treenode. But the icons are made black. image image

Screenshots/Video:

https://github.com/user-attachments/assets/64840460-4a67-4bec-98f8-160c497e6bb8

Minimal, Complete and Verifiable Example code:

#define ICON_WORLD "w"
void LoadImGuiFontIcon(std::string filename, int id){
    ImGuiIO& io = ImGui::GetIO();
    uint8_t* glyphPixels = nullptr;
    int glyphX, glyphY;
    io.Fonts->GetTexDataAsRGBA32(&glyphPixels, &glyphX, &glyphY);
    if (const ImFontAtlasCustomRect* rect = io.Fonts->GetCustomRectByIndex(id)){
        int imgX, imgY, channels;
        FILE* file = OpenContentFile(std::string("icons/")+filename, "r");
        if(file == NULL){return;}
        uint8_t* texturePixels = stbi_load_from_file(file, &imgX, &imgY, &channels, 4);

        int pixCounter = 0;
        // Fill the custom rectangle with red pixels (in reality you would draw/copy your bitmap data here!)
        fmt::println("texture {} {} {}", imgX, imgY, channels);
        for (int y = 0; y < rect->Height; y++){
            ImU32* p = (ImU32*)glyphPixels + (rect->Y + y) * glyphX + (rect->X);
            for (int x = rect->Width; x > 0; x--){
                *p++ = IM_COL32(texturePixels[pixCounter], texturePixels[pixCounter+1], texturePixels[pixCounter+2], texturePixels[pixCounter+3]);
                pixCounter += 4;
            }
        }
        stbi_image_free(texturePixels);
    }
}

void Render::LoadImGuiFontIcons(){
    ImGuiIO& io = ImGui::GetIO();
    static ImFontConfig cfg;
    cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_LoadColor;
    ImFont* font = io.Fonts->AddFontDefault();
    int rect_ids[10];
    rect_ids[0] = io.Fonts->AddCustomRectFontGlyph(font, *(const ImWchar*)ICON_WORLD, 16, 16, 16+1);
    rect_ids[1] = io.Fonts->AddCustomRectFontGlyph(font, *(const ImWchar*)ICON_BRICK, 16, 16, 16+1);

    // Build atlas
    io.Fonts->Build();

    LoadImGuiFontIcon("world.png", rect_ids[0]);
}
ocornut commented 3 weeks ago

The glyph needs to be marked as Colored to undo the vertex coloring applied by text color. It should happens to work because in the darker style the text color is white (1,1,1,1) and in the other it is black (0,0,0,1) which multiplies down the color.

There is 1 bit in ImFontGlyph for that:

 unsigned int    Colored : 1;  // Flag to indicate glyph is colored and should generally ignore tinting

Unfortunately this wasn't planned ahead in the AddCustomRectFontGlyph() function, there is no way to pass that parameter.

You would currently need to use this workaround:

    int rect_ids[10];
    rect_ids[0] = io.Fonts->AddCustomRectFontGlyph(font, *(const ImWchar*)ICON_WORLD, 16, 16, 16+1);
    rect_ids[1] = io.Fonts->AddCustomRectFontGlyph(font, *(const ImWchar*)ICON_BRICK, 16, 16, 16+1);

    // Build atlas
    io.Fonts->Build();

    for (int n = 0; n < 2; n++)
    {
         ImFontGlyph* glyph = (ImFontGlyph*)font->FindGlyph(rect_ids[n].GlyphID);
         glyph->Colored = 1;
    }

Normally I would fix it by expanding the AddCustomRectFontGlyph() API but as with #8107 I am currently working on a large refactor of the font system.

IniKiwi commented 3 weeks ago

Thanks you very much! image Do you have any idea how fit this icon in the treenode by changing the vertical alignment of the glyph and the size of the tree nodes?

ocornut commented 3 weeks ago

If you manually add glyphs to a font it should be added with same vertical size. Horizontal size you specify.

Consider using emoji svg fonts using freetype as an alternative.

IniKiwi commented 3 weeks ago

I'm using same icons as gmod and old roblox, these icons are just 16x16 bitmaps. I will leave it like this. Thanks you very much!

image These icons are so cute!

ocornut commented 2 weeks ago

As an undocumented small change I have pushed 17bd417

You can now do this:

rect_ids[0] = io.Fonts->AddCustomRectFontGlyph(font, *(const ImWchar*)ICON_WORLD, 16, 16, 16+1);
rect_ids[1] = io.Fonts->AddCustomRectFontGlyph(font, *(const ImWchar*)ICON_BRICK, 16, 16, 16+1);
for (int n = 0; n < 2; n++)
    io.Fonts->GetCustomRectByIndex(rects_id[n])->GlyphColored = 1;

It's not great and doesn't constitute a neat public API, but you can do this in the same call size without waiting for Build() to occur.

I'll keep this in mind when we introduce a new API.