Browse Source

Added first-pass of Image() based on #73 + demo

ocornut 10 years ago
parent
commit
cc79b85c28
2 changed files with 142 additions and 16 deletions
  1. 127 14
      imgui.cpp
  2. 15 2
      imgui.h

+ 127 - 14
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);
 

+ 15 - 2
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<ImDrawVert>    vtx_buffer;         // each command consume ImDrawCmd::vtx_count of those
 
     // [Internal to ImGui]
-    ImVector<ImVec4>        clip_rect_stack;    // [internal] clip rect stack while building the command-list (so text command can perform clipping early on)
+    ImVector<ImVec4>        clip_rect_stack;    // [internal]
+    ImVector<ImTextureID>   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;