From cc79b85c2841de499d638b1e2518f2a453c896a3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 11 Jan 2015 16:30:39 +0000 Subject: [PATCH] Added first-pass of Image() based on #73 + demo --- imgui.cpp | 141 ++++++++++++++++++++++++++++++++++++++++++++++++------ imgui.h | 17 ++++++- 2 files changed, 142 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 410cb15b..c99da17f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2332,6 +2332,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph if (first_begin_of_the_frame) { window->DrawList->Clear(); + window->DrawList->PushTextureID(g.IO.Font->TexID); window->Visible = true; // New windows appears in front @@ -2652,6 +2653,9 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph } else { + // Short path when we do multiple Begin in the same frame. + window->DrawList->PushTextureID(g.IO.Font->TexID); + // Outer clipping rectangle if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ComboBox)) { @@ -2705,6 +2709,7 @@ void ImGui::End() ImGui::Columns(1, "#CloseColumns"); PopClipRect(); // inner window clip rectangle PopClipRect(); // outer window clip rectangle + window->DrawList->PopTextureID(); // Select window for move/focus when we're done with all our widgets (we only consider non-childs windows here) const ImGuiAabb bb(window->Pos, window->Pos+window->Size); @@ -3402,7 +3407,7 @@ static bool ButtonBehaviour(const ImGuiAabb& bb, const ImGuiID& id, bool* out_ho return pressed; } -bool ImGui::Button(const char* label, ImVec2 size, bool repeat_when_held) +bool ImGui::Button(const char* label, const ImVec2& size_arg, bool repeat_when_held) { ImGuiState& g = GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -3413,10 +3418,7 @@ bool ImGui::Button(const char* label, ImVec2 size, bool repeat_when_held) const ImGuiID id = window->GetID(label); const ImVec2 text_size = CalcTextSize(label, NULL, true); - if (size.x == 0.0f) - size.x = text_size.x; - if (size.y == 0.0f) - size.y = text_size.y; + const ImVec2 size(size_arg.x != 0.0f ? size_arg.x : text_size.x, size_arg.y != 0.0f ? size_arg.y : text_size.y); const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + size + style.FramePadding*2.0f); ItemSize(bb); @@ -3500,6 +3502,31 @@ static bool CloseWindowButton(bool* p_opened) return pressed; } +void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, ImU32 tint_col, ImU32 border_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + size); + if (border_col != 0) + bb.Max += ImVec2(2,2); + ItemSize(bb.GetSize(), &bb.Min); + + if (ClipAdvance(bb)) + return; + + if (border_col != 0) + { + window->DrawList->AddRect(bb.Min, bb.Max, border_col, 0.0f); + window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, tint_col); + } + else + { + window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, tint_col); + } +} + // Start logging ImGui output to TTY void ImGui::LogToTTY(int max_depth) { @@ -5810,19 +5837,24 @@ void ImGui::Color(const char* prefix, unsigned int v) // ImDrawList //----------------------------------------------------------------------------- +static ImVec4 GNullClipRect(-9999.0f,-9999.0f, +9999.0f, +9999.0f); +static ImTextureID GNullTextureID = NULL; + void ImDrawList::Clear() { commands.resize(0); vtx_buffer.resize(0); vtx_write = NULL; clip_rect_stack.resize(0); + texture_id_stack.resize(0); } -void ImDrawList::PushClipRect(const ImVec4& clip_rect) +void ImDrawList::SetClipRect(const ImVec4& clip_rect) { if (!commands.empty() && commands.back().vtx_count == 0) { - // Reuse empty command because high-level clipping may have discarded the other vertices already + // Reuse existing command (high-level clipping may have discarded vertices submitted earlier) + // FIXME-OPT: Possibly even reuse previous command. commands.back().clip_rect = clip_rect; } else @@ -5830,29 +5862,55 @@ void ImDrawList::PushClipRect(const ImVec4& clip_rect) ImDrawCmd draw_cmd; draw_cmd.vtx_count = 0; draw_cmd.clip_rect = clip_rect; + draw_cmd.texture_id = texture_id_stack.back(); commands.push_back(draw_cmd); } +} + +void ImDrawList::PushClipRect(const ImVec4& clip_rect) +{ + SetClipRect(clip_rect); clip_rect_stack.push_back(clip_rect); } void ImDrawList::PopClipRect() { clip_rect_stack.pop_back(); - const ImVec4 clip_rect = clip_rect_stack.empty() ? ImVec4(-9999.0f,-9999.0f, +9999.0f, +9999.0f) : clip_rect_stack.back(); + const ImVec4 clip_rect = clip_rect_stack.empty() ? GNullClipRect : clip_rect_stack.back(); + SetClipRect(clip_rect); +} + +void ImDrawList::SetTextureID(const ImTextureID& texture_id) +{ if (!commands.empty() && commands.back().vtx_count == 0) { - // Reuse empty command because high-level clipping may have discarded the other vertices already - commands.back().clip_rect = clip_rect; + // Reuse existing command (high-level clipping may have discarded vertices submitted earlier) + // FIXME-OPT: Possibly even reuse previous command. + commands.back().texture_id = texture_id; } else { ImDrawCmd draw_cmd; draw_cmd.vtx_count = 0; - draw_cmd.clip_rect = clip_rect; + draw_cmd.clip_rect = clip_rect_stack.empty() ? GNullClipRect: clip_rect_stack.back(); + draw_cmd.texture_id = texture_id; commands.push_back(draw_cmd); } } +void ImDrawList::PushTextureID(const ImTextureID& texture_id) +{ + SetTextureID(texture_id); + texture_id_stack.push_back(texture_id); +} + +void ImDrawList::PopTextureID() +{ + texture_id_stack.pop_back(); + const ImTextureID texture_id = texture_id_stack.empty() ? NULL : texture_id_stack.back(); + SetTextureID(texture_id); +} + void ImDrawList::ReserveVertices(unsigned int vtx_count) { if (vtx_count > 0) @@ -5872,6 +5930,14 @@ void ImDrawList::AddVtx(const ImVec2& pos, ImU32 col) vtx_write++; } +void ImDrawList::AddVtxUV(const ImVec2& pos, ImU32 col, const ImVec2& uv) +{ + vtx_write->pos = pos; + vtx_write->col = col; + vtx_write->uv = uv; + vtx_write++; +} + void ImDrawList::AddVtxLine(const ImVec2& a, const ImVec2& b, ImU32 col) { const float offset = GImGui.IO.PixelCenterOffset; @@ -6095,6 +6161,27 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 vtx_write -= (vtx_count_max - vtx_count); } +void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col) +{ + if ((col >> 24) == 0) + return; + + const bool push_texture_id = user_texture_id != texture_id_stack.back(); + if (push_texture_id) + PushTextureID(user_texture_id); + + ReserveVertices(6); + AddVtxUV(ImVec2(a.x,a.y), col, uv0); + AddVtxUV(ImVec2(b.x,a.y), col, ImVec2(uv1.x,uv0.y)); + AddVtxUV(ImVec2(b.x,b.y), col, uv1); + AddVtxUV(ImVec2(a.x,a.y), col, ImVec2(uv0.x,uv0.y)); + AddVtxUV(ImVec2(b.x,b.y), col, uv1); + AddVtxUV(ImVec2(a.x,b.y), col, ImVec2(uv0.x,uv1.y)); + + if (push_texture_id) + PopTextureID(); +} + //----------------------------------------------------------------------------- // ImBitmapFont //----------------------------------------------------------------------------- @@ -6120,13 +6207,16 @@ void ImFont::Clear() ImGui::MemFree(TexPixels); DisplayOffset = ImVec2(0.5f, 0.5f); + + TexID = NULL; + TexPixels = NULL; + TexWidth = TexHeight = 0; + TexExtraDataPos = TexUvWhitePixel = ImVec2(0, 0); + FontSize = 0.0f; Glyphs.clear(); IndexLookup.clear(); FallbackGlyph = NULL; - TexPixels = NULL; - TexWidth = TexHeight = 0; - TexExtraDataPos = ImVec2(0, 0); } // Retrieve list of range (2 int per range, values are inclusive) @@ -7259,6 +7349,29 @@ void ImGui::ShowTestWindow(bool* opened) ImGui::TreePop(); } + if (ImGui::TreeNode("Images")) + { + ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data.\nHover the texture for a zoomed view."); + ImVec2 tex_screen_pos = ImGui::GetCursorScreenPos(); + float tex_w = (float)ImGui::GetIO().Font->TexWidth; + float tex_h = (float)ImGui::GetIO().Font->TexHeight; + ImGui::Image(ImGui::GetIO().Font->TexID, ImVec2(tex_w, tex_h), ImVec2(0,0), ImVec2(1,1), 0xFFFFFFFF, 0x999999FF); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + float focus_sz = 32.0f; + float focus_x = ImClamp(ImGui::GetMousePos().x - tex_screen_pos.x - focus_sz * 0.5f, 0.0f, tex_w - focus_sz); + float focus_y = ImClamp(ImGui::GetMousePos().y - tex_screen_pos.y - focus_sz * 0.5f, 0.0f, tex_h - focus_sz); + ImGui::Text("Min: (%.2f, %.2f)", focus_x, focus_y); + ImGui::Text("Max: (%.2f, %.2f)", focus_x + focus_sz, focus_y + focus_sz); + ImVec2 uv0 = ImVec2((focus_x) / tex_w, (focus_y) / tex_h); + ImVec2 uv1 = ImVec2((focus_x + focus_sz) / tex_w, (focus_y + focus_sz) / tex_h); + ImGui::Image(ImGui::GetIO().Font->TexID, ImVec2(128,128), uv0, uv1, 0xFFFFFFFF, 0x999999FF); + ImGui::EndTooltip(); + } + ImGui::TreePop(); + } + static bool check = true; ImGui::Checkbox("checkbox", &check); diff --git a/imgui.h b/imgui.h index 46b6afae..e325a299 100644 --- a/imgui.h +++ b/imgui.h @@ -32,6 +32,7 @@ struct ImGuiWindow; typedef unsigned int ImU32; typedef unsigned short ImWchar; // character for display +typedef void* ImTextureID; // user data to refer to a texture (e.g. store your texture handle/id) typedef ImU32 ImGuiID; // unique ID used by widgets (typically hashed from a stack of string) typedef int ImGuiCol; // enum ImGuiCol_ typedef int ImGuiStyleVar; // enum ImGuiStyleVar_ @@ -233,8 +234,9 @@ namespace ImGui IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args); IMGUI_API void BulletText(const char* fmt, ...); IMGUI_API void BulletTextV(const char* fmt, va_list args); - IMGUI_API bool Button(const char* label, ImVec2 size = ImVec2(0,0), bool repeat_when_held = false); + IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0), bool repeat_when_held = false); IMGUI_API bool SmallButton(const char* label); + IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), ImU32 tint_col = 0xFFFFFFFF, ImU32 border_col = 0x00000000); IMGUI_API bool CollapsingHeader(const char* label, const char* str_id = NULL, const bool display_frame = true, const bool default_open = false); IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix. Use power!=1.0 for logarithmic sliders. IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); @@ -675,6 +677,7 @@ struct ImDrawCmd { unsigned int vtx_count; ImVec4 clip_rect; + ImTextureID texture_id; // Copy of user-provided 'TexID' from ImFont or passed to Image*() functions. Ignore if not using images or multiple fonts. }; #ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT @@ -705,16 +708,22 @@ struct ImDrawList ImVector vtx_buffer; // each command consume ImDrawCmd::vtx_count of those // [Internal to ImGui] - ImVector clip_rect_stack; // [internal] clip rect stack while building the command-list (so text command can perform clipping early on) + ImVector clip_rect_stack; // [internal] + ImVector texture_id_stack; // [internal] ImDrawVert* vtx_write; // [internal] point within vtx_buffer after each add command (to avoid using the ImVector<> operators too much) ImDrawList() { Clear(); } IMGUI_API void Clear(); + IMGUI_API void SetClipRect(const ImVec4& clip_rect); IMGUI_API void PushClipRect(const ImVec4& clip_rect); IMGUI_API void PopClipRect(); + IMGUI_API void SetTextureID(const ImTextureID& texture_id); + IMGUI_API void PushTextureID(const ImTextureID& texture_id); + IMGUI_API void PopTextureID(); IMGUI_API void ReserveVertices(unsigned int vtx_count); IMGUI_API void AddVtx(const ImVec2& pos, ImU32 col); + IMGUI_API void AddVtxUV(const ImVec2& pos, ImU32 col, const ImVec2& uv); IMGUI_API void AddVtxLine(const ImVec2& a, const ImVec2& b, ImU32 col); // Primitives @@ -726,6 +735,7 @@ struct ImDrawList IMGUI_API void AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12); IMGUI_API void AddArc(const ImVec2& center, float rad, ImU32 col, int a_min, int a_max, bool tris = false, const ImVec2& third_point_offset = ImVec2(0,0)); IMGUI_API void AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f); + IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col); }; // TTF font loading and rendering @@ -736,8 +746,11 @@ struct ImFont float Scale; // = 1.0f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() ImVec2 DisplayOffset; // = (0.0f,0.0f) // Offset font rendering by xx pixels ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. + ImTextureID TexID; // = NULL // User reference to texture used by the font (ignore if you aren't using multiple fonts/textures) // Texture data + // User is in charge of copying the pixels into a GPU texture. + // You can set 'TexID' to uniquely identify your texture. TexId is copied to the ImDrawCmd structure which you receive during rendering. unsigned char* TexPixels; // 1 byte, 1 component per pixel. Total byte size of TexWidth * TexHeight int TexWidth; int TexHeight;