diff --git a/3rdparty/ocornut-imgui/imgui.cpp b/3rdparty/ocornut-imgui/imgui.cpp index a668f463..5c682e78 100644 --- a/3rdparty/ocornut-imgui/imgui.cpp +++ b/3rdparty/ocornut-imgui/imgui.cpp @@ -136,6 +136,27 @@ Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix. Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. + - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure. + you need to render your textured triangles with bilinear filtering to benefit from subpixel positioning of text. + - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost. + this necessary change will break your rendering function! the fix should be very easy. sorry for that :( + - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest. + - the signature of the io.RenderDrawListsFn handler has changed! + ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) + became: + ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). + argument 'cmd_lists' -> 'draw_data->CmdLists' + argument 'cmd_lists_count' -> 'draw_data->CmdListsCount' + ImDrawList 'commands' -> 'CmdBuffer' + ImDrawList 'vtx_buffer' -> 'VtxBuffer' + ImDrawList n/a -> 'IdxBuffer' (new) + ImDrawCmd 'vtx_count' -> 'ElemCount' + ImDrawCmd 'clip_rect' -> 'ClipRect' + ImDrawCmd 'user_callback' -> 'UserCallback' + ImDrawCmd 'texture_id' -> 'TextureId' + - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer. + - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering! + - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount. @@ -312,12 +333,26 @@ // the first loaded font gets used by default // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime + // Options + ImFontConfig config; + config.OversampleH = 3; + config.OversampleV = 3; + config.GlyphExtraSpacing.x = 1.0f; + io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config); + + // Merging input from different fonts into one + ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; + ImFontConfig config; + config.MergeMode = true; + io.Fonts->AddFontDefault(); + io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); + io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); Q: How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic? A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. ImGui will support UTF-8 encoding across the board. Character input depends on you passing the right character code to io.AddInputCharacter(). The example applications do that. - io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() io.ImeWindowHandle = MY_HWND; // To input using Microsoft IME, give ImGui the hwnd of your application @@ -333,7 +368,6 @@ Issue numbers (#) refer to github issues. - misc: merge or clarify ImVec4 vs ImRect? - - window: fix resize grip rendering scaling along with Rounding style setting - window: autofit feedback loop when user relies on any dynamic layout (window width multiplier, column). maybe just clearly drop manual autofit? - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. - window: allow resizing of child windows (possibly given min/max for each axis?) @@ -379,6 +413,7 @@ - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y) - gauge: various forms of gauge/loading bars widgets - color: better color editor. + - plot: plotlines should use the polygon-stroke facilities (currently issues with averaging normals) - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots) - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value) - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) @@ -413,6 +448,7 @@ - input: rework IO to be able to pass actual events to fix temporal aliasing issues. - input: support track pad style scrolling & slider edit. - portability: big-endian test/support (#81) + - drawlist: add support for anti-aliased lines with >1.0f thickness (#133) - memory: add a way to discard allocs of unused/transient windows. with the current architecture new windows (including popup, opened combos, listbox) perform at least 3 allocs. - misc: mark printf compiler attributes on relevant functions - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL) @@ -434,6 +470,11 @@ #include // sqrtf, fabsf, fmodf, powf, cosf, sinf, floorf, ceilf #include // vsnprintf, sscanf, printf #include // new (ptr) +#if defined(_MSC_VER) +#include // alloca +#else +#include // alloca +#endif #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier #include // intptr_t #else @@ -616,15 +657,18 @@ ImGuiStyle::ImGuiStyle() ScrollbarWidth = 16.0f; // Width of the vertical scrollbar ScrollbarRounding = 0.0f; // Radius of grab corners rounding for scrollbar GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar + GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. + AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.65f); - Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.60f); + Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f); Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f); @@ -765,6 +809,7 @@ static inline float ImLerp(float a, float b, float t) static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } +static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; } static bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c) { @@ -1042,10 +1087,10 @@ struct ImRect // 2D axis aligned bounding-box ImVec2 Min; ImVec2 Max; - ImRect() { Min = ImVec2(FLT_MAX,FLT_MAX); Max = ImVec2(-FLT_MAX,-FLT_MAX); } - ImRect(const ImVec2& min, const ImVec2& max) { Min = min; Max = max; } - ImRect(const ImVec4& v) { Min.x = v.x; Min.y = v.y; Max.x = v.z; Max.y = v.w; } - ImRect(float x1, float y1, float x2, float y2) { Min.x = x1; Min.y = y1; Max.x = x2; Max.y = y2; } + ImRect() : Min(FLT_MAX,FLT_MAX), Max(-FLT_MAX,-FLT_MAX) {} + ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {} + ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {} + ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {} ImVec2 GetCenter() const { return ImVec2((Min.x+Max.x)*0.5f, (Min.y+Max.y)*0.5f); } ImVec2 GetSize() const { return ImVec2(Max.x-Min.x,Max.y-Min.y); } @@ -1058,12 +1103,13 @@ struct ImRect // 2D axis aligned bounding-box bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x < Max.x && r.Max.y < Max.y; } bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } - void Add(const ImVec2& rhs) { Min.x = ImMin(Min.x, rhs.x); Min.y = ImMin(Min.y, rhs.y); Max.x = ImMax(Max.x, rhs.x); Max.y = ImMax(Max.x, rhs.x); } + void Add(const ImVec2& rhs) { Min.x = ImMin(Min.x, rhs.x); Min.y = ImMin(Min.y, rhs.y); Max.x = ImMax(Max.x, rhs.x); Max.y = ImMax(Max.y, rhs.y); } void Add(const ImRect& rhs) { Min.x = ImMin(Min.x, rhs.Min.x); Min.y = ImMin(Min.y, rhs.Min.y); Max.x = ImMax(Max.x, rhs.Max.x); Max.y = ImMax(Max.y, rhs.Max.y); } void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; } void Clip(const ImRect& clip) { Min.x = ImMax(Min.x, clip.Min.x); Min.y = ImMax(Min.y, clip.Min.y); Max.x = ImMin(Max.x, clip.Max.x); Max.y = ImMin(Max.y, clip.Max.y); } + void Round() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const { if (!on_edge && Contains(p)) @@ -1394,7 +1440,7 @@ struct ImGuiState PrivateClipboard = NULL; ModalWindowDarkeningRatio = 0.0f; - OverlayDrawList.owner_name = "##Overlay"; // Give it a name for debugging + OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging MouseCursor = ImGuiMouseCursor_Arrow; LogEnabled = false; @@ -1815,7 +1861,7 @@ ImGuiWindow::ImGuiWindow(const char* name) DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList)); new(DrawList) ImDrawList(); - DrawList->owner_name = Name; + DrawList->_OwnerName = Name; RootWindow = NULL; RootNonPopupWindow = NULL; @@ -1885,12 +1931,13 @@ void ImGuiWindow::FocusItemUnregister() static inline void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list) { - if (!draw_list->commands.empty() && !draw_list->vtx_buffer.empty()) + if (!draw_list->CmdBuffer.empty() && !draw_list->VtxBuffer.empty()) { - if (draw_list->commands.back().vtx_count == 0) - draw_list->commands.pop_back(); + if (draw_list->CmdBuffer.back().ElemCount == 0) + draw_list->CmdBuffer.pop_back(); out_render_list.push_back(draw_list); - GImGui->IO.MetricsRenderVertices += draw_list->vtx_buffer.Size; + GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size; + GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size; } } @@ -2107,6 +2154,9 @@ void ImGui::NewFrame() g.FrameCount += 1; g.Tooltip[0] = '\0'; g.OverlayDrawList.Clear(); + g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID); + g.OverlayDrawList.PushClipRectFullScreen(); + g.OverlayDrawList.AddDrawCmd(); // Update inputs state if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) @@ -2381,7 +2431,7 @@ static void PopClipRect() { ImGuiWindow* window = GetCurrentWindow(); window->DrawList->PopClipRect(); - window->ClipRect = window->DrawList->clip_rect_stack.back(); + window->ClipRect = window->DrawList->_ClipRectStack.back(); } void ImGui::Render() @@ -2441,7 +2491,7 @@ void ImGui::Render() } // Skip render altogether if alpha is 0.0 - // Note that vertex buffers have been created, so it is best practice that you don't create windows in the first place, or respond to Begin() returning false + // Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or respond to Begin() returning false. if (g.Style.Alpha > 0.0f) { // Render tooltip @@ -2453,8 +2503,7 @@ void ImGui::Render() } // Gather windows to render - g.IO.MetricsRenderVertices = 0; - g.IO.MetricsActiveWindows = 0; + g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) g.RenderDrawLists[i].resize(0); for (int i = 0; i != g.Windows.Size; i++) @@ -2462,7 +2511,7 @@ void ImGui::Render() ImGuiWindow* window = g.Windows[i]; if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0) { - // FIXME: Generalize this with a proper layering system so we can stack. + // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, .. g.IO.MetricsActiveWindows++; if (window->Flags & ImGuiWindowFlags_Popup) AddWindowToRenderList(g.RenderDrawLists[1], window); @@ -2502,12 +2551,19 @@ void ImGui::Render() g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], 0xFFFFFFFF); // White fill g.OverlayDrawList.PopTextureID(); } - if (!g.OverlayDrawList.vtx_buffer.empty()) + if (!g.OverlayDrawList.VtxBuffer.empty()) AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList); // Render if (!g.RenderDrawLists[0].empty()) - g.IO.RenderDrawListsFn(&g.RenderDrawLists[0][0], g.RenderDrawLists[0].Size); + { + ImDrawData data; + data.CmdLists = &g.RenderDrawLists[0][0]; + data.CmdListsCount = g.RenderDrawLists[0].Size; + data.TotalVtxCount = g.IO.MetricsRenderVertices; + data.TotalIdxCount = g.IO.MetricsRenderIndices; + g.IO.RenderDrawListsFn(&data); + } } } @@ -2713,9 +2769,8 @@ static void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); if (border && (window->Flags & ImGuiWindowFlags_ShowBorders)) { - // FIXME: This is the best I've found that works on multiple renderer/back ends. Bit dodgy. - window->DrawList->AddRect(p_min+ImVec2(1.5f,1.5f), p_max+ImVec2(1,1), window->Color(ImGuiCol_BorderShadow), rounding); - window->DrawList->AddRect(p_min+ImVec2(0.5f,0.5f), p_max+ImVec2(0,0), window->Color(ImGuiCol_Border), rounding); + window->DrawList->AddRect(p_min+ImVec2(1,1), p_max, window->Color(ImGuiCol_BorderShadow), rounding); + window->DrawList->AddRect(p_min, p_max-ImVec2(1,1), window->Color(ImGuiCol_Border), rounding); } } @@ -2757,15 +2812,17 @@ static void RenderCheckMark(ImVec2 pos, ImU32 col) ImVec2 a, b, c; float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f); float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f); - a.x = pos.x + start_x; + a.x = pos.x + 0.5f + start_x; b.x = a.x + rem_third; c.x = a.x + rem_third * 3.0f; - b.y = pos.y + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y); + b.y = pos.y + 0.5f + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y); a.y = b.y - rem_third; c.y = b.y - rem_third * 2.0f; - window->DrawList->AddLine(a, b, col); - window->DrawList->AddLine(b, c, col); + window->DrawList->PathLineTo(a); + window->DrawList->PathLineTo(b); + window->DrawList->PathLineTo(c); + window->DrawList->PathStroke(col, false); } // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. @@ -3173,7 +3230,7 @@ static void CloseInactivePopups() if (g.OpenedPopupStack.empty()) return; - // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it + // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. // Don't close our own child popup windows int n = 0; if (g.FocusedWindow) @@ -3185,21 +3242,17 @@ static void CloseInactivePopups() continue; IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) - { - if (g.FocusedWindow->RootWindow != popup.Window->RootWindow) - break; - } - else - { - bool has_focus = false; - for (int m = n; m < g.OpenedPopupStack.Size && !has_focus; m++) - has_focus = (g.OpenedPopupStack[m].Window && g.OpenedPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow); - if (!has_focus) - break; - } + continue; + + bool has_focus = false; + for (int m = n; m < g.OpenedPopupStack.Size && !has_focus; m++) + has_focus = (g.OpenedPopupStack[m].Window && g.OpenedPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow); + if (!has_focus) + break; } } - g.OpenedPopupStack.resize(n); + if (n < g.OpenedPopupStack.Size) // This test is not required but it allows to set a useful breakpoint on the line below + g.OpenedPopupStack.resize(n); } static ImGuiWindow* GetFrontMostModalRootWindow() @@ -3979,19 +4032,14 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ // (after the input handling so we don't have a frame of latency) if (!(flags & ImGuiWindowFlags_NoResize)) { - const float r = window_rounding; + const float base_size = g.FontSize * 1.35f; + const float min_size = window_rounding + 1.0f + g.FontSize * 0.2f; + const float corner_size = ImMax(base_size, min_size); const ImVec2 br = window->Rect().GetBR(); - if (r == 0.0f) - { - window->DrawList->AddTriangleFilled(br, br-ImVec2(0,14), br-ImVec2(14,0), resize_col); - } - else - { - // FIXME: We should draw 4 triangles and decide on a size that's not dependent on the rounding size (previously used 18) - window->DrawList->AddArcFast(br - ImVec2(r,r), r, resize_col, 6, 9, true); - window->DrawList->AddTriangleFilled(br+ImVec2(0,-2*r),br+ImVec2(0,-r),br+ImVec2(-r,-r), resize_col); - window->DrawList->AddTriangleFilled(br+ImVec2(-r,-r), br+ImVec2(-r,0),br+ImVec2(-2*r,0), resize_col); - } + window->DrawList->PathLineTo(br + ImVec2(-corner_size, 0.0f)); + window->DrawList->PathLineTo(br + ImVec2(0.0f, -corner_size)); + window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding, br.y - window_rounding), window_rounding, 6, 9); + window->DrawList->PathFill(resize_col); } } @@ -5520,8 +5568,8 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display ImRect bb = ImRect(pos_min, ImVec2(pos_max.x, pos_min.y + label_size.y)); if (display_frame) { - bb.Min.x -= window_padding.x*0.5f - 1; - bb.Max.x += window_padding.x*0.5f - 1; + bb.Min.x -= (float)(int)(window_padding.x*0.5f) - 1; + bb.Max.x += (float)(int)(window_padding.x*0.5f) - 1; bb.Max.y += style.FramePadding.y * 2; } @@ -5558,7 +5606,7 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display const char log_prefix[] = "\n##"; LogText(bb.Min + style.FramePadding, log_prefix, log_prefix+3); } - RenderText(bb.Min + style.FramePadding + ImVec2(g.FontSize + style.FramePadding.x*2,0), label); + RenderTextClipped(bb.Min + style.FramePadding + ImVec2(g.FontSize + style.FramePadding.x*2,0), bb.Max, label, NULL, &label_size); if (g.LogEnabled) { const char log_suffix[] = "##"; @@ -5996,7 +6044,7 @@ static bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + 2.0f), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - 2.0f)); else grab_bb = ImRect(ImVec2(frame_bb.Min.x + 2.0f, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - 2.0f, grab_pos + grab_sz*0.5f)); - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, window->Color(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab)); + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, window->Color(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); return value_changed; } @@ -6406,12 +6454,12 @@ bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power); } -bool ImGui::DragFloat3(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power) +bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power) { return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power); } -bool ImGui::DragFloat4(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power) +bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power) { return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power); } @@ -7386,7 +7434,7 @@ static bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Count lines + find lines numbers of cursor and select_start int matches_remaining = 0; int matches_line_no[2] = { -1, -999 }; - const ImWchar* matches_ptr[2]; + const ImWchar* matches_ptr[2] = {}; matches_ptr[0] = text_begin + edit_state.StbState.cursor; matches_remaining++; if (edit_state.StbState.select_start != edit_state.StbState.select_end) { @@ -7492,7 +7540,7 @@ static bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Draw blinking cursor ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; if (g.InputTextState.CursorIsVisible()) - draw_window->DrawList->AddLine(cursor_screen_pos + ImVec2(0,-g.FontSize+1), cursor_screen_pos + ImVec2(0,-1), window->Color(ImGuiCol_Text)); + draw_window->DrawList->AddLine(cursor_screen_pos + ImVec2(0.0f,-g.FontSize+0.5f), cursor_screen_pos + ImVec2(0.0f,-1.5f), window->Color(ImGuiCol_Text)); // Notify OS of text input position for advanced IME if (io.ImeSetInputScreenPosFn && ImLengthSqr(edit_state.InputCursorScreenPos - cursor_screen_pos) > 0.0001f) @@ -8662,12 +8710,14 @@ void ImGui::NextColumn() if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount) { window->DC.ColumnsOffsetX = ImGui::GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.ColumnsStartX + g.Style.ItemSpacing.x; + window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent); } else { window->DC.ColumnsCurrent = 0; window->DC.ColumnsOffsetX = 0.0f; window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY; + window->DrawList->ChannelsSetCurrent(0); } window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX); window->DC.CursorPos.y = window->DC.ColumnsCellMinY; @@ -8700,11 +8750,10 @@ static float GetDraggedColumnOffset(int column_index) IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets. IM_ASSERT(g.ActiveId == window->DC.ColumnsSetID + ImGuiID(column_index)); - float x = g.IO.MousePos.x + g.ActiveClickDeltaToCenter.x; - x -= window->Pos.x; + float x = g.IO.MousePos.x + g.ActiveClickDeltaToCenter.x - window->Pos.x; x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing); - return x; + return (float)(int)x; } float ImGui::GetColumnOffset(int column_index) @@ -8727,8 +8776,8 @@ float ImGui::GetColumnOffset(int column_index) const float min_x = window->DC.ColumnsStartX; const float max_x = window->Size.x - (g.Style.ScrollbarWidth);// - window->WindowPadding().x; - const float offset = min_x + t * (max_x - min_x); - return offset; + const float x = min_x + t * (max_x - min_x); + return (float)(int)x; } void ImGui::SetColumnOffset(int column_index, float offset) @@ -8780,6 +8829,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border) ItemSize(ImVec2(0,0)); // Advance to column 0 ImGui::PopItemWidth(); PopClipRect(); + window->DrawList->ChannelsMerge(window->DC.ColumnsCount); window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y); window->DC.CursorPos.y = window->DC.ColumnsCellMaxY; @@ -8793,10 +8843,8 @@ void ImGui::Columns(int columns_count, const char* id, bool border) for (int i = 1; i < window->DC.ColumnsCount; i++) { float x = window->Pos.x + GetColumnOffset(i); - const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(i); const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2)); - if (IsClippedEx(column_rect, &column_id, false)) continue; @@ -8808,7 +8856,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border) // Draw before resize so our items positioning are in sync with the line being drawn const ImU32 col = window->Color(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column); const float xi = (float)(int)x; - window->DrawList->AddLine(ImVec2(xi, y1), ImVec2(xi, y2), col); + window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col); if (held) { @@ -8845,7 +8893,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border) const float t = window->DC.StateStorage->GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store an union into the map?) window->DC.ColumnsOffsetsT[column_index] = t; } - + window->DrawList->ChannelsSplit(window->DC.ColumnsCount); PushColumnClipRect(); ImGui::PushItemWidth(ImGui::GetColumnWidth() * 0.65f); } @@ -8954,72 +9002,147 @@ static ImVec4 GNullClipRect(-9999.0f,-9999.0f, +9999.0f, +9999.0f); void ImDrawList::Clear() { - commands.resize(0); - vtx_buffer.resize(0); - vtx_write = NULL; - clip_rect_stack.resize(0); - texture_id_stack.resize(0); + CmdBuffer.resize(0); + IdxBuffer.resize(0); + VtxBuffer.resize(0); + _VtxCurrentIdx = 0; + _VtxWritePtr = NULL; + _IdxWritePtr = NULL; + _ClipRectStack.resize(0); + _TextureIdStack.resize(0); + _Path.resize(0); + _ChannelCurrent = 0; + // NB: Do not clear channels so our allocations are re-used after the first frame. } void ImDrawList::ClearFreeMemory() { - commands.clear(); - vtx_buffer.clear(); - vtx_write = NULL; - clip_rect_stack.clear(); - texture_id_stack.clear(); + CmdBuffer.clear(); + IdxBuffer.clear(); + VtxBuffer.clear(); + _VtxCurrentIdx = 0; + _VtxWritePtr = NULL; + _IdxWritePtr = NULL; + _ClipRectStack.clear(); + _TextureIdStack.clear(); + _Path.clear(); + _ChannelCurrent = 0; + for (int i = 0; i < _Channels.Size; i++) + { + if (i == 0) memset(&_Channels[0], 0, sizeof(_Channels[0])); // channel 0 is a copy of CmdBuffer/IdxBuffer, don't destruct again + _Channels[i].CmdBuffer.clear(); + _Channels[i].IdxBuffer.clear(); + } + _Channels.clear(); } void ImDrawList::AddDrawCmd() { ImDrawCmd draw_cmd; - draw_cmd.vtx_count = 0; - draw_cmd.clip_rect = clip_rect_stack.empty() ? GNullClipRect : clip_rect_stack.back(); - draw_cmd.texture_id = texture_id_stack.empty() ? NULL : texture_id_stack.back(); - draw_cmd.user_callback = NULL; - draw_cmd.user_callback_data = NULL; + draw_cmd.ElemCount = 0; + draw_cmd.ClipRect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect; + draw_cmd.TextureId = _TextureIdStack.Size ? _TextureIdStack.back() : NULL; + draw_cmd.UserCallback = NULL; + draw_cmd.UserCallbackData = NULL; - IM_ASSERT(draw_cmd.clip_rect.x <= draw_cmd.clip_rect.z && draw_cmd.clip_rect.y <= draw_cmd.clip_rect.w); - commands.push_back(draw_cmd); + IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); + CmdBuffer.push_back(draw_cmd); } void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) { - ImDrawCmd* current_cmd = commands.empty() ? NULL : &commands.back(); - if (!current_cmd || current_cmd->vtx_count != 0 || current_cmd->user_callback != NULL) + ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + if (!current_cmd || current_cmd->ElemCount != 0 || current_cmd->UserCallback != NULL) { AddDrawCmd(); - current_cmd = &commands.back(); + current_cmd = &CmdBuffer.back(); } - current_cmd->user_callback = callback; - current_cmd->user_callback_data = callback_data; + current_cmd->UserCallback = callback; + current_cmd->UserCallbackData = callback_data; - // Force a new command after us - // We function this way so that the most common calls (AddLine, AddRect..) always have a command to add to without doing any check. + // Force a new command after us (we function this way so that the most common calls AddLine, AddRect, etc. always have a command to add to without doing any check). AddDrawCmd(); } +void ImDrawList::ChannelsSplit(int channel_count) +{ + IM_ASSERT(_ChannelCurrent == 0); + int old_channels_count = _Channels.Size; + if (old_channels_count < channel_count) + _Channels.resize(channel_count); + for (int i = 0; i < channel_count; i++) + if (i >= old_channels_count) + new(&_Channels[i]) ImDrawChannel(); + else + _Channels[i].CmdBuffer.resize(0), _Channels[i].IdxBuffer.resize(0); +} + +void ImDrawList::ChannelsMerge(int channel_count) +{ + // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. + // This is why this function takes 'channel_count' as a parameter of how many channels to merge (the user knows) + if (channel_count < 2) + return; + + ChannelsSetCurrent(0); + if (CmdBuffer.Size && CmdBuffer.back().ElemCount == 0) + CmdBuffer.pop_back(); + + int new_cmd_buffer_count = 0, new_idx_buffer_count = 0; + for (int i = 1; i < channel_count; i++) + { + ImDrawChannel& ch = _Channels[i]; + if (ch.CmdBuffer.Size && ch.CmdBuffer.back().ElemCount == 0) + ch.CmdBuffer.pop_back(); + new_cmd_buffer_count += ch.CmdBuffer.Size; + new_idx_buffer_count += ch.IdxBuffer.Size; + } + CmdBuffer.resize(CmdBuffer.Size + new_cmd_buffer_count); + IdxBuffer.resize(IdxBuffer.Size + new_idx_buffer_count); + + ImDrawCmd* cmd_write = CmdBuffer.Data + CmdBuffer.Size - new_cmd_buffer_count; + _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size - new_idx_buffer_count; + for (int i = 1; i < channel_count; i++) + { + ImDrawChannel& ch = _Channels[i]; + if (int sz = ch.CmdBuffer.Size) { memcpy(cmd_write, ch.CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; } + if (int sz = ch.IdxBuffer.Size) { memcpy(_IdxWritePtr, ch.IdxBuffer.Data, sz * sizeof(ImDrawIdx)); _IdxWritePtr += sz; } + } + AddDrawCmd(); +} + +void ImDrawList::ChannelsSetCurrent(int idx) +{ + if (_ChannelCurrent == idx) return; + memcpy(&_Channels.Data[_ChannelCurrent].CmdBuffer, &CmdBuffer, sizeof(CmdBuffer)); + memcpy(&_Channels.Data[_ChannelCurrent].IdxBuffer, &IdxBuffer, sizeof(IdxBuffer)); + _ChannelCurrent = idx; + memcpy(&CmdBuffer, &_Channels.Data[_ChannelCurrent].CmdBuffer, sizeof(CmdBuffer)); + memcpy(&IdxBuffer, &_Channels.Data[_ChannelCurrent].IdxBuffer, sizeof(IdxBuffer)); + _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size; +} + void ImDrawList::UpdateClipRect() { - ImDrawCmd* current_cmd = commands.empty() ? NULL : &commands.back(); - if (!current_cmd || (current_cmd->vtx_count != 0) || current_cmd->user_callback != NULL) + ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + if (!current_cmd || (current_cmd->ElemCount != 0) || current_cmd->UserCallback != NULL) { AddDrawCmd(); } else { - ImVec4 current_clip_rect = clip_rect_stack.empty() ? GNullClipRect : clip_rect_stack.back(); - if (commands.Size > 2 && ImLengthSqr(commands[commands.Size-2].clip_rect - current_clip_rect) < 0.00001f) - commands.pop_back(); + ImVec4 current_clip_rect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect; + if (CmdBuffer.Size >= 2 && ImLengthSqr(CmdBuffer.Data[CmdBuffer.Size-2].ClipRect - current_clip_rect) < 0.00001f) + CmdBuffer.pop_back(); else - current_cmd->clip_rect = current_clip_rect; + current_cmd->ClipRect = current_clip_rect; } } // Scissoring. The values in clip_rect are x1, y1, x2, y2. void ImDrawList::PushClipRect(const ImVec4& clip_rect) { - clip_rect_stack.push_back(clip_rect); + _ClipRectStack.push_back(clip_rect); UpdateClipRect(); } @@ -9034,55 +9157,46 @@ void ImDrawList::PushClipRectFullScreen() void ImDrawList::PopClipRect() { - IM_ASSERT(clip_rect_stack.Size > 0); - clip_rect_stack.pop_back(); + IM_ASSERT(_ClipRectStack.Size > 0); + _ClipRectStack.pop_back(); UpdateClipRect(); } void ImDrawList::UpdateTextureID() { - ImDrawCmd* current_cmd = commands.empty() ? NULL : &commands.back(); - const ImTextureID texture_id = texture_id_stack.empty() ? NULL : texture_id_stack.back(); - if (!current_cmd || (current_cmd->vtx_count != 0 && current_cmd->texture_id != texture_id) || current_cmd->user_callback != NULL) - { + ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + const ImTextureID texture_id = _TextureIdStack.Size ? _TextureIdStack.back() : NULL; + if (!current_cmd || (current_cmd->ElemCount != 0 && current_cmd->TextureId != texture_id) || current_cmd->UserCallback != NULL) AddDrawCmd(); - } else - { - current_cmd->texture_id = texture_id; - } + current_cmd->TextureId = texture_id; } void ImDrawList::PushTextureID(const ImTextureID& texture_id) { - texture_id_stack.push_back(texture_id); + _TextureIdStack.push_back(texture_id); UpdateTextureID(); } void ImDrawList::PopTextureID() { - IM_ASSERT(texture_id_stack.Size > 0); - texture_id_stack.pop_back(); + IM_ASSERT(_TextureIdStack.Size > 0); + _TextureIdStack.pop_back(); UpdateTextureID(); } -void ImDrawList::PrimReserve(unsigned int vtx_count) +void ImDrawList::PrimReserve(int idx_count, int vtx_count) { - ImDrawCmd& draw_cmd = commands.back(); - draw_cmd.vtx_count += vtx_count; + ImDrawCmd& draw_cmd = CmdBuffer.Data[CmdBuffer.Size-1]; + draw_cmd.ElemCount += idx_count; + + int vtx_buffer_size = VtxBuffer.Size; + VtxBuffer.resize(vtx_buffer_size + vtx_count); + _VtxWritePtr = VtxBuffer.Data + vtx_buffer_size; - int vtx_buffer_size = vtx_buffer.Size; - vtx_buffer.resize(vtx_buffer_size + vtx_count); - vtx_write = vtx_buffer.Data + vtx_buffer_size; -} - -void ImDrawList::PrimTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col) -{ - const ImVec2 uv = GImGui->FontTexUvWhitePixel; - vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; - vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; - vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; - vtx_write += 3; + int idx_buffer_size = IdxBuffer.Size; + IdxBuffer.resize(idx_buffer_size + idx_count); + _IdxWritePtr = IdxBuffer.Data + idx_buffer_size; } void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) @@ -9090,196 +9204,361 @@ void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) const ImVec2 uv = GImGui->FontTexUvWhitePixel; const ImVec2 b(c.x, a.y); const ImVec2 d(a.x, c.y); - vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; - vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; - vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; - vtx_write[3].pos = a; vtx_write[3].uv = uv; vtx_write[3].col = col; - vtx_write[4].pos = c; vtx_write[4].uv = uv; vtx_write[4].col = col; - vtx_write[5].pos = d; vtx_write[5].uv = uv; vtx_write[5].col = col; - vtx_write += 6; + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+2); + _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx+2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx+3); + _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + _VtxCurrentIdx += 4; + _IdxWritePtr += 6; } void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col) { - const ImVec2 b(c.x, a.y); - const ImVec2 d(a.x, c.y); - const ImVec2 uv_b(uv_c.x, uv_a.y); - const ImVec2 uv_d(uv_a.x, uv_c.y); - vtx_write[0].pos = a; vtx_write[0].uv = uv_a; vtx_write[0].col = col; - vtx_write[1].pos = b; vtx_write[1].uv = uv_b; vtx_write[1].col = col; - vtx_write[2].pos = c; vtx_write[2].uv = uv_c; vtx_write[2].col = col; - vtx_write[3].pos = a; vtx_write[3].uv = uv_a; vtx_write[3].col = col; - vtx_write[4].pos = c; vtx_write[4].uv = uv_c; vtx_write[4].col = col; - vtx_write[5].pos = d; vtx_write[5].uv = uv_d; vtx_write[5].col = col; - vtx_write += 6; + const ImVec2 b(c.x, a.y); + const ImVec2 d(a.x, c.y); + const ImVec2 uv_b(uv_c.x, uv_a.y); + const ImVec2 uv_d(uv_a.x, uv_c.y); + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+2); + _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx+2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx+3); + _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + _VtxCurrentIdx += 4; + _IdxWritePtr += 6; } -void ImDrawList::PrimQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col) +void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness, bool anti_aliased) +{ + if (points_count < 2) + return; + + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + anti_aliased &= GImGui->Style.AntiAliasedLines; + //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; + + int count = points_count; + if (!closed) + count = points_count-1; + + if (anti_aliased && thickness == 1.0f) + { + // Anti-aliased stroke + const float AA_SIZE = 1.0f; + const ImU32 col_trans = col & 0x00ffffff; + const int idx_count = count*12; + const int vtx_count = points_count*3; + PrimReserve(idx_count, vtx_count); + unsigned int vtx_inner_idx = _VtxCurrentIdx+1; + unsigned int vtx_outer_idx = _VtxCurrentIdx+2; + + // Temporary buffer + ImVec2* temp_inner = (ImVec2*)alloca(points_count * 3 * sizeof(ImVec2)); + ImVec2* temp_outer = temp_inner + points_count; + ImVec2* temp_normals = temp_inner + points_count * 2; + + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + const ImVec2& p1 = points[i1]; + const ImVec2& p2 = points[i2]; + ImVec2 diff = p2 - p1; + diff *= ImInvLength(diff, 1.0f); + temp_normals[i1].x = diff.y; + temp_normals[i1].y = -diff.x; + } + + if (!closed) + { + temp_normals[points_count-1] = temp_normals[points_count-2]; + temp_outer[0] = points[0] + temp_normals[0] * AA_SIZE; + temp_inner[0] = points[0] - temp_normals[0] * AA_SIZE; + temp_outer[points_count-1] = points[points_count-1] + temp_normals[points_count-1] * AA_SIZE; + temp_inner[points_count-1] = points[points_count-1] - temp_normals[points_count-1] * AA_SIZE; + } + + // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + + // Average normals + const ImVec2& n1 = temp_normals[i1]; + const ImVec2& n2 = temp_normals[i2]; + ImVec2 dm = (n1 + n2) * 0.5f; + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 100.0f) scale = 100.0f; + dm *= scale; + } + dm *= AA_SIZE; + temp_outer[i2] = points[i2] + dm; + temp_inner[i2] = points[i2] - dm; + + // Add indexes + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + i2*3); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + i1*3); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + i1*3); + _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + i1*3); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + i2*3); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx + i2*3); + _IdxWritePtr[6] = (ImDrawIdx)(vtx_inner_idx + i2*3); _IdxWritePtr[7] = (ImDrawIdx)(vtx_inner_idx + i1*3); _IdxWritePtr[8] = (ImDrawIdx)(_VtxCurrentIdx + i1*3); + _IdxWritePtr[9] = (ImDrawIdx)(_VtxCurrentIdx + i1*3); _IdxWritePtr[10]= (ImDrawIdx)(_VtxCurrentIdx + i2*3); _IdxWritePtr[11]= (ImDrawIdx)(vtx_inner_idx + i2*3); + _IdxWritePtr += 12; + } + + // Add vertexes + for (int i = 0; i < points_count; i++) + { + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = temp_inner[i]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; + _VtxWritePtr[2].pos = temp_outer[i]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col_trans; + _VtxWritePtr += 3; + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } + else + { + // Non Anti-aliased Stroke + const int idx_count = count*6; + const int vtx_count = count*4; // FIXME-OPT: Not sharing edges + PrimReserve(idx_count, vtx_count); + + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + const ImVec2& p1 = points[i1]; + const ImVec2& p2 = points[i2]; + ImVec2 diff = p2 - p1; + diff *= ImInvLength(diff, 1.0f); + + const float dx = diff.x * (thickness * 0.5f); + const float dy = diff.y * (thickness * 0.5f); + _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+2); + _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx+2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx+3); + _IdxWritePtr += 6; + _VtxCurrentIdx += 4; + } + } +} + +void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col, bool anti_aliased) { const ImVec2 uv = GImGui->FontTexUvWhitePixel; - vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; - vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; - vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; - vtx_write[3].pos = a; vtx_write[3].uv = uv; vtx_write[3].col = col; - vtx_write[4].pos = c; vtx_write[4].uv = uv; vtx_write[4].col = col; - vtx_write[5].pos = d; vtx_write[5].uv = uv; vtx_write[5].col = col; - vtx_write += 6; + anti_aliased &= GImGui->Style.AntiAliasedShapes; + //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; + + if (anti_aliased) + { + // Anti-aliased Fill + const float AA_SIZE = 1.0f; + const ImU32 col_trans = col & 0x00ffffff; + const int idx_count = (points_count-2)*3 + points_count*6; + const int vtx_count = (points_count*2); + PrimReserve(idx_count, vtx_count); + + // Add indexes for fill + unsigned int vtx_inner_idx = _VtxCurrentIdx; + unsigned int vtx_outer_idx = _VtxCurrentIdx+1; + for (int i = 2; i < points_count; i++) + { + _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+((i-1)<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx+(i<<1)); + _IdxWritePtr += 3; + } + + // Compute normals + ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); + for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) + { + const ImVec2& p0 = points[i0]; + const ImVec2& p1 = points[i1]; + ImVec2 diff = p1 - p0; + diff *= ImInvLength(diff, 1.0f); + temp_normals[i0].x = diff.y; + temp_normals[i0].y = -diff.x; + } + + for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) + { + // Average normals + const ImVec2& n0 = temp_normals[i0]; + const ImVec2& n1 = temp_normals[i1]; + ImVec2 dm = (n0 + n1) * 0.5f; + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 100.0f) scale = 100.0f; + dm *= scale; + } + dm *= AA_SIZE * 0.5f; + + // Add vertices + _VtxWritePtr[0].pos = (points[i1] - dm); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner + _VtxWritePtr[1].pos = (points[i1] + dm); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer + _VtxWritePtr += 2; + + // Add indexes for fringes + _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+(i0<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); + _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx+(i1<<1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); + _IdxWritePtr += 6; + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } + else + { + // Non Anti-aliased Fill + const int idx_count = (points_count-2)*3; + const int vtx_count = points_count; + PrimReserve(idx_count, vtx_count); + for (int i = 0; i < vtx_count; i++) + { + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr++; + } + for (int i = 2; i < points_count; i++) + { + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+i-1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+i); + _IdxWritePtr += 3; + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } } -// FIXME-OPT: In many instances the caller could provide a normal. -void ImDrawList::PrimLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness) +void ImDrawList::PathArcToFast(const ImVec2& centre, float radius, int amin, int amax) { - const float inv_length = 1.0f / sqrtf(ImLengthSqr(b - a)); - const float dx = (b.x - a.x) * (thickness * 0.5f * inv_length); // line direction, halved - const float dy = (b.y - a.y) * (thickness * 0.5f * inv_length); // line direction, halved + static ImVec2 circle_vtx[12]; + static bool circle_vtx_builds = false; + const int circle_vtx_count = IM_ARRAYSIZE(circle_vtx); + if (!circle_vtx_builds) + { + for (int i = 0; i < circle_vtx_count; i++) + { + const float a = ((float)i / (float)circle_vtx_count) * 2*IM_PI; + circle_vtx[i].x = cosf(a + IM_PI); + circle_vtx[i].y = sinf(a + IM_PI); + } + circle_vtx_builds = true; + } - const ImVec2 pa(a.x + dy, a.y - dx); - const ImVec2 pb(b.x + dy, b.y - dx); - const ImVec2 pc(b.x - dy, b.y + dx); - const ImVec2 pd(a.x - dy, a.y + dx); - PrimQuad(pa, pb, pc, pd, col); + if (amin > amax) return; + if (radius == 0.0f) + { + _Path.push_back(centre); + } + else + { + _Path.reserve(_Path.Size + (amax - amin + 1)); + for (int a = amin; a <= amax; a++) + { + const ImVec2& c = circle_vtx[a % circle_vtx_count]; + _Path.push_back(ImVec2(centre.x + c.x * radius, centre.y + c.y * radius)); + } + } +} + +void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float amin, float amax, int num_segments) +{ + if (radius == 0.0f) + _Path.push_back(centre); + _Path.reserve(_Path.Size + (num_segments + 1)); + for (int i = 0; i <= num_segments; i++) + { + const float a = amin + ((float)i / (float)num_segments) * (amax - amin); + _Path.push_back(ImVec2(centre.x + cosf(a + IM_PI) * radius, centre.y + sinf(a + IM_PI) * radius)); + } +} + +void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners) +{ + float r = rounding; + r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners&(1|2))==(1|2)) || ((rounding_corners&(4|8))==(4|8)) ? 0.5f : 1.0f ) - 1.0f); + r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners&(1|8))==(1|8)) || ((rounding_corners&(2|4))==(2|4)) ? 0.5f : 1.0f ) - 1.0f); + + if (r == 0.0f || rounding_corners == 0) + { + PathLineTo(a); + PathLineTo(ImVec2(b.x,a.y)); + PathLineTo(b); + PathLineTo(ImVec2(a.x,b.y)); + } + else + { + const float r0 = (rounding_corners & 1) ? r : 0.0f; + const float r1 = (rounding_corners & 2) ? r : 0.0f; + const float r2 = (rounding_corners & 4) ? r : 0.0f; + const float r3 = (rounding_corners & 8) ? r : 0.0f; + PathArcToFast(ImVec2(a.x+r0,a.y+r0), r0, 0, 3); + PathArcToFast(ImVec2(b.x-r1,a.y+r1), r1, 3, 6); + PathArcToFast(ImVec2(b.x-r2,b.y-r2), r2, 6, 9); + PathArcToFast(ImVec2(a.x+r3,b.y-r3), r3, 9, 12); + } } void ImDrawList::AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness) { if ((col >> 24) == 0) return; - - PrimReserve(6); - PrimLine(a, b, col, thickness); -} - -void ImDrawList::AddArcFast(const ImVec2& center, float radius, ImU32 col, int a_min, int a_max, bool filled, const ImVec2& third_point_offset) -{ - if ((col >> 24) == 0) - return; - - const int SAMPLES = 12; - static ImVec2 circle_vtx[SAMPLES]; - static bool circle_vtx_builds = false; - if (!circle_vtx_builds) - { - for (int i = 0; i < SAMPLES; i++) - { - const float a = ((float)i / (float)SAMPLES) * 2*IM_PI; - circle_vtx[i].x = cosf(a + IM_PI); - circle_vtx[i].y = sinf(a + IM_PI); - } - circle_vtx_builds = true; - } - - const ImVec2 uv = GImGui->FontTexUvWhitePixel; - if (filled) - { - PrimReserve((unsigned int)(a_max-a_min) * 3); - for (int a0 = a_min; a0 < a_max; a0++) - { - int a1 = (a0 + 1 == SAMPLES) ? 0 : a0 + 1; - PrimVtx(center + circle_vtx[a0] * radius, uv, col); - PrimVtx(center + circle_vtx[a1] * radius, uv, col); - PrimVtx(center + third_point_offset, uv, col); - } - } - else - { - PrimReserve((unsigned int)(a_max-a_min) * 6); - for (int a0 = a_min; a0 < a_max; a0++) - { - int a1 = (a0 + 1 == SAMPLES) ? 0 : a0 + 1; - PrimLine(center + circle_vtx[a0] * radius, center + circle_vtx[a1] * radius, col); - } - } + PathLineTo(a + ImVec2(0.5f,0.5f)); + PathLineTo(b + ImVec2(0.5f,0.5f)); + PathStroke(col, false, thickness); } void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners) { if ((col >> 24) == 0) return; - - float r = rounding; - r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners&(1|2))==(1|2)) || ((rounding_corners&(4|8))==(4|8)) ? 0.5f : 1.0f )); - r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners&(1|8))==(1|8)) || ((rounding_corners&(2|4))==(2|4)) ? 0.5f : 1.0f )); - - if (r == 0.0f || rounding_corners == 0) - { - PrimReserve(4*6); - PrimLine(a, ImVec2(b.x,a.y), col); - PrimLine(ImVec2(b.x,a.y), b, col); - PrimLine(b, ImVec2(a.x,b.y), col); - PrimLine(ImVec2(a.x,b.y), a, col); - } - else - { - PrimReserve(4*6); - PrimLine(ImVec2(a.x + ((rounding_corners & 1)?r:0), a.y), ImVec2(b.x - ((rounding_corners & 2)?r:0), a.y), col); - PrimLine(ImVec2(b.x, a.y + ((rounding_corners & 2)?r:0)), ImVec2(b.x, b.y - ((rounding_corners & 4)?r:0)), col); - PrimLine(ImVec2(b.x - ((rounding_corners & 4)?r:0), b.y), ImVec2(a.x + ((rounding_corners & 8)?r:0), b.y), col); - PrimLine(ImVec2(a.x, b.y - ((rounding_corners & 8)?r:0)), ImVec2(a.x, a.y + ((rounding_corners & 1)?r:0)), col); - - if (rounding_corners & 1) AddArcFast(ImVec2(a.x+r,a.y+r), r, col, 0, 3); - if (rounding_corners & 2) AddArcFast(ImVec2(b.x-r,a.y+r), r, col, 3, 6); - if (rounding_corners & 4) AddArcFast(ImVec2(b.x-r,b.y-r), r, col, 6, 9); - if (rounding_corners & 8) AddArcFast(ImVec2(a.x+r,b.y-r), r, col, 9, 12); - } + PathRect(a + ImVec2(0.5f,0.5f), b + ImVec2(0.5f,0.5f), rounding, rounding_corners); + PathStroke(col, true); } void ImDrawList::AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners) { if ((col >> 24) == 0) return; - - float r = rounding; - r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners&(1|2))==(1|2)) || ((rounding_corners&(4|8))==(4|8)) ? 0.5f : 1.0f )); - r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners&(1|8))==(1|8)) || ((rounding_corners&(2|4))==(2|4)) ? 0.5f : 1.0f )); - - if (r == 0.0f || rounding_corners == 0) + if (rounding > 0.0f) { - // Use triangle so we can merge more draw calls together (at the cost of extra vertices) - PrimReserve(6); - PrimRect(a, b, col); + PathRect(a, b, rounding, rounding_corners); + PathFill(col); } else { - PrimReserve(6+6*2); - PrimRect(ImVec2(a.x+r,a.y), ImVec2(b.x-r,b.y), col); - - float top_y = (rounding_corners & 1) ? a.y+r : a.y; - float bot_y = (rounding_corners & 8) ? b.y-r : b.y; - PrimRect(ImVec2(a.x,top_y), ImVec2(a.x+r,bot_y), col); - - top_y = (rounding_corners & 2) ? a.y+r : a.y; - bot_y = (rounding_corners & 4) ? b.y-r : b.y; - PrimRect(ImVec2(b.x-r,top_y), ImVec2(b.x,bot_y), col); - - if (rounding_corners & 1) AddArcFast(ImVec2(a.x+r,a.y+r), r, col, 0, 3, true); - if (rounding_corners & 2) AddArcFast(ImVec2(b.x-r,a.y+r), r, col, 3, 6, true); - if (rounding_corners & 4) AddArcFast(ImVec2(b.x-r,b.y-r), r, col, 6, 9, true); - if (rounding_corners & 8) AddArcFast(ImVec2(a.x+r,b.y-r), r, col, 9, 12, true); + PrimReserve(6, 4); + PrimRect(a, b, col); } } -void ImDrawList::AddRectFilledMultiColor(const ImVec2& a, const ImVec2& b, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left) +void ImDrawList::AddRectFilledMultiColor(const ImVec2& a, const ImVec2& c, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left) { if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) >> 24) == 0) return; const ImVec2 uv = GImGui->FontTexUvWhitePixel; - PrimReserve(6); - PrimVtx(a, uv, col_upr_left); - PrimVtx(ImVec2(b.x,a.y), uv, col_upr_right); - PrimVtx(ImVec2(b.x,b.y), uv, col_bot_right); - PrimVtx(a, uv, col_upr_left); - PrimVtx(b, uv, col_bot_right); - PrimVtx(ImVec2(a.x,b.y), uv, col_bot_left); + PrimReserve(6, 4); + PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); + PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+3)); + PrimWriteVtx(a, uv, col_upr_left); + PrimWriteVtx(ImVec2(c.x, a.y), uv, col_upr_right); + PrimWriteVtx(c, uv, col_bot_right); + PrimWriteVtx(ImVec2(a.x, c.y), uv, col_bot_left); } void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col) { if ((col >> 24) == 0) return; - - PrimReserve(3); - PrimTriangle(a, b, c, col); + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathFill(col); } void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments) @@ -9287,15 +9566,9 @@ void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int nu if ((col >> 24) == 0) return; - PrimReserve((unsigned int)num_segments*6); - const float a_step = 2*IM_PI/(float)num_segments; - float a0 = 0.0f; - for (int i = 0; i < num_segments; i++) - { - const float a1 = (i + 1) == num_segments ? 0.0f : a0 + a_step; - PrimLine(centre + ImVec2(cosf(a0), sinf(a0))*radius, centre + ImVec2(cosf(a1), sinf(a1))*radius, col); - a0 = a1; - } + const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(centre, radius, 0.0f, a_max, num_segments); + PathStroke(col, true); } void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments) @@ -9303,18 +9576,9 @@ void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, if ((col >> 24) == 0) return; - const ImVec2 uv = GImGui->FontTexUvWhitePixel; - PrimReserve((unsigned int)num_segments*3); - const float a_step = 2*IM_PI/(float)num_segments; - float a0 = 0.0f; - for (int i = 0; i < num_segments; i++) - { - const float a1 = (i + 1) == num_segments ? 0.0f : a0 + a_step; - PrimVtx(centre + ImVec2(cosf(a0), sinf(a0))*radius, uv, col); - PrimVtx(centre + ImVec2(cosf(a1), sinf(a1))*radius, uv, col); - PrimVtx(centre, uv, col); - a0 = a1; - } + const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(centre, radius, 0.0f, a_max, num_segments); + PathFill(col); } void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) @@ -9327,15 +9591,17 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, if (text_begin == text_end) return; - IM_ASSERT(font->ContainerAtlas->TexID == texture_id_stack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + IM_ASSERT(font->ContainerAtlas->TexID == _TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. // reserve vertices for worse case - const unsigned int char_count = (unsigned int)(text_end - text_begin); - const unsigned int vtx_count_max = char_count * 6; - const int vtx_begin = vtx_buffer.Size; - PrimReserve(vtx_count_max); + const int char_count = (int)(text_end - text_begin); + const int vtx_count_max = char_count * 4; + const int idx_count_max = char_count * 6; + const int vtx_begin = VtxBuffer.Size; + const int idx_begin = IdxBuffer.Size; + PrimReserve(idx_count_max, vtx_count_max); - ImVec4 clip_rect = clip_rect_stack.back(); + ImVec4 clip_rect = _ClipRectStack.back(); if (cpu_fine_clip_rect) { clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x); @@ -9346,10 +9612,15 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, font->RenderText(font_size, pos, col, clip_rect, text_begin, text_end, this, wrap_width, cpu_fine_clip_rect != NULL); // give back unused vertices - vtx_buffer.resize((int)(vtx_write - &vtx_buffer.front())); - const int vtx_count = vtx_buffer.Size - vtx_begin; - commands.back().vtx_count -= (unsigned int)(vtx_count_max - vtx_count); - vtx_write -= (vtx_count_max - vtx_count); + // FIXME-OPT + VtxBuffer.resize((int)(_VtxWritePtr - VtxBuffer.Data)); + IdxBuffer.resize((int)(_IdxWritePtr - IdxBuffer.Data)); + int vtx_unused = vtx_count_max - (VtxBuffer.Size - vtx_begin); + int idx_unused = idx_count_max - (IdxBuffer.Size - idx_begin); + CmdBuffer.back().ElemCount -= idx_unused; + _VtxWritePtr -= vtx_unused; + _IdxWritePtr -= idx_unused; + _VtxCurrentIdx = (ImDrawIdx)VtxBuffer.Size; } void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col) @@ -9358,42 +9629,60 @@ void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const Im return; // FIXME-OPT: This is wasting draw calls. - const bool push_texture_id = texture_id_stack.empty() || user_texture_id != texture_id_stack.back(); + const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); if (push_texture_id) PushTextureID(user_texture_id); - PrimReserve(6); + PrimReserve(6, 4); PrimRectUV(a, b, uv0, uv1, col); if (push_texture_id) PopTextureID(); } +//----------------------------------------------------------------------------- +// ImDrawData +//----------------------------------------------------------------------------- + +// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! +void ImDrawData::DeIndexAllBuffers() +{ + ImVector new_vtx_buffer; + TotalVtxCount = TotalIdxCount = 0; + for (int i = 0; i < CmdListsCount; i++) + { + ImDrawList* cmd_list = CmdLists[i]; + if (cmd_list->IdxBuffer.empty()) + continue; + new_vtx_buffer.resize(cmd_list->IdxBuffer.Size); + for (int j = 0; j < cmd_list->IdxBuffer.Size; j++) + new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]]; + cmd_list->VtxBuffer.swap(new_vtx_buffer); + cmd_list->IdxBuffer.resize(0); + TotalVtxCount += cmd_list->VtxBuffer.Size; + } +} + //----------------------------------------------------------------------------- // ImFontAtlias //----------------------------------------------------------------------------- -struct ImFontAtlas::ImFontAtlasData +ImFontConfig::ImFontConfig() { - // Input - ImFont* OutFont; // Load into this font - void* TTFData; // TTF data, we own the memory - int TTFDataSize; // TTF data size, in bytes - float SizePixels; // Desired output size, in pixels - const ImWchar* GlyphRanges; // List of Unicode range (2 value per range, values are inclusive, zero-terminated list) - int FontNo; // Index of font within .TTF file (0) - - // Temporary Build Data - stbtt_fontinfo FontInfo; - stbrp_rect* Rects; - stbtt_pack_range* Ranges; - int RangesCount; -}; - -const stbtt_fontinfo& getFontInfo(int index) -{ - const ImGuiIO& io = ImGui::GetIO(); - return io.Fonts[index].InputData[0]->FontInfo; + FontData = NULL; + FontDataSize = 0; + FontDataOwnedByAtlas = true; + FontNo = 0; + SizePixels = 0.0f; + OversampleH = 3; + OversampleV = 1; + PixelSnapH = false; + GlyphExtraSpacing = ImVec2(0.0f, 0.0f); + GlyphRanges = NULL; + MergeMode = false; + MergeGlyphCenterV = false; + DstFont = NULL; + memset(Name, 0, sizeof(Name)); } ImFontAtlas::ImFontAtlas() @@ -9412,13 +9701,12 @@ ImFontAtlas::~ImFontAtlas() void ImFontAtlas::ClearInputData() { - for (int i = 0; i < InputData.Size; i++) - { - if (InputData[i]->TTFData) - ImGui::MemFree(InputData[i]->TTFData); - ImGui::MemFree(InputData[i]); - } - InputData.clear(); + for (int i = 0; i < ConfigData.Size; i++) + if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas) + { + ImGui::MemFree(ConfigData[i].FontData); + ConfigData[i].FontData = NULL; + } } void ImFontAtlas::ClearTexData() @@ -9453,7 +9741,7 @@ void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_wid // Lazily build if (TexPixelsAlpha8 == NULL) { - if (InputData.empty()) + if (ConfigData.empty()) AddFontDefault(); Build(); } @@ -9485,22 +9773,57 @@ void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_wid if (out_bytes_per_pixel) *out_bytes_per_pixel = 4; } +ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) +{ + IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0); + IM_ASSERT(font_cfg->SizePixels > 0.0f); + + // Create new font + if (!font_cfg->MergeMode) + { + ImFont* font = (ImFont*)ImGui::MemAlloc(sizeof(ImFont)); + new (font) ImFont(); + Fonts.push_back(font); + } + + ConfigData.push_back(*font_cfg); + ImFontConfig& new_font_cfg = ConfigData.back(); + new_font_cfg.DstFont = Fonts.back(); + if (!new_font_cfg.FontDataOwnedByAtlas) + { + new_font_cfg.FontData = ImGui::MemAlloc(new_font_cfg.FontDataSize); + new_font_cfg.FontDataOwnedByAtlas = true; + memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize); + } + + // Invalidate texture + ClearTexData(); + return Fonts.back(); +} + static void GetDefaultCompressedFontDataTTF(const void** ttf_compressed_data, unsigned int* ttf_compressed_size); static unsigned int stb_decompress_length(unsigned char *input); static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length); // Load embedded ProggyClean.ttf at size 13 -ImFont* ImFontAtlas::AddFontDefault() +ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) { unsigned int ttf_compressed_size; const void* ttf_compressed; GetDefaultCompressedFontDataTTF(&ttf_compressed, &ttf_compressed_size); - ImFont* font = AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, 13.0f, GetGlyphRangesDefault(), 0); - font->DisplayOffset.y += 1; - return font; + ImFontConfig font_cfg; + if (font_cfg_template) + font_cfg = *font_cfg_template; + else + { + font_cfg.OversampleH = font_cfg.OversampleV = 1; + font_cfg.PixelSnapH = true; + } + if (font_cfg.Name[0] == '\0') strcpy(font_cfg.Name, ""); + return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, 13.0f, &font_cfg, GetGlyphRangesDefault()); } -ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImWchar* glyph_ranges, int font_no) +ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { void* data = NULL; int data_size = 0; @@ -9509,72 +9832,76 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, IM_ASSERT(0); // Could not load file. return NULL; } - - ImFont* font = AddFontFromMemoryTTF(data, data_size, size_pixels, glyph_ranges, font_no); - return font; + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + if (font_cfg.Name[0] == '\0') + { + const char* p; + for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} + ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p); + } + return AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg, glyph_ranges); } // Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build() -ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImWchar* glyph_ranges, int font_no) +ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { - // Create new font - ImFont* font = (ImFont*)ImGui::MemAlloc(sizeof(ImFont)); - new (font) ImFont(); - Fonts.push_back(font); - - // Add to build list - ImFontAtlasData* data = (ImFontAtlasData*)ImGui::MemAlloc(sizeof(ImFontAtlasData)); - memset(data, 0, sizeof(ImFontAtlasData)); - data->OutFont = font; - data->TTFData = ttf_data; - data->TTFDataSize = ttf_size; - data->SizePixels = size_pixels; - data->GlyphRanges = glyph_ranges; - data->FontNo = font_no; - InputData.push_back(data); - - // Invalidate texture - ClearTexData(); - - return font; + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + IM_ASSERT(font_cfg.FontData == NULL); + font_cfg.FontData = ttf_data; + font_cfg.FontDataSize = ttf_size; + font_cfg.SizePixels = size_pixels; + if (glyph_ranges) + font_cfg.GlyphRanges = glyph_ranges; + return AddFont(&font_cfg); } -ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImWchar* glyph_ranges, int font_no) +ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { - // Decompress const unsigned int buf_decompressed_size = stb_decompress_length((unsigned char*)compressed_ttf_data); unsigned char* buf_decompressed_data = (unsigned char *)ImGui::MemAlloc(buf_decompressed_size); stb_decompress(buf_decompressed_data, (unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size); - // Add - ImFont* font = AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, glyph_ranges, font_no); - return font; + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + IM_ASSERT(font_cfg.FontData == NULL); + font_cfg.FontDataOwnedByAtlas = true; + return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, font_cfg_template, glyph_ranges); } bool ImFontAtlas::Build() { - IM_ASSERT(InputData.Size > 0); + IM_ASSERT(ConfigData.Size > 0); TexID = NULL; TexWidth = TexHeight = 0; TexUvWhitePixel = ImVec2(0, 0); ClearTexData(); + struct ImFontTempBuildData + { + stbtt_fontinfo FontInfo; + stbrp_rect* Rects; + stbtt_pack_range* Ranges; + int RangesCount; + }; + ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)ConfigData.Size * sizeof(ImFontTempBuildData)); + // Initialize font information early (so we can error without any cleanup) + count glyphs int total_glyph_count = 0; int total_glyph_range_count = 0; - for (int input_i = 0; input_i < InputData.Size; input_i++) + for (int input_i = 0; input_i < ConfigData.Size; input_i++) { - ImFontAtlasData& data = *InputData[input_i]; - IM_ASSERT(data.OutFont && (!data.OutFont->IsLoaded() || data.OutFont->ContainerAtlas == this)); - const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)data.TTFData, data.FontNo); + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + + IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == this)); + const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); IM_ASSERT(font_offset >= 0); - if (!stbtt_InitFont(&data.FontInfo, (unsigned char*)data.TTFData, font_offset)) + if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) return false; - if (!data.GlyphRanges) - data.GlyphRanges = GetGlyphRangesDefault(); - for (const ImWchar* in_range = data.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) + if (!cfg.GlyphRanges) + cfg.GlyphRanges = GetGlyphRangesDefault(); + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) { total_glyph_count += (in_range[1] - in_range[0]) + 1; total_glyph_range_count++; @@ -9586,13 +9913,12 @@ bool ImFontAtlas::Build() TexHeight = 0; const int max_tex_height = 1024*32; stbtt_pack_context spc; - int ret = stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL); - IM_ASSERT(ret); - stbtt_PackSetOversampling(&spc, 1, 1); + stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL); // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). ImVector extra_rects; RenderCustomTexData(0, &extra_rects); + stbtt_PackSetOversampling(&spc, 1, 1); stbrp_pack_rects((stbrp_context*)spc.pack_info, &extra_rects[0], extra_rects.Size); for (int i = 0; i < extra_rects.Size; i++) if (extra_rects[i].was_packed) @@ -9608,26 +9934,27 @@ bool ImFontAtlas::Build() memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range)); // First font pass: pack all glyphs (no rendering at this point, we are working with glyph sizes only) - for (int input_i = 0; input_i < InputData.Size; input_i++) + for (int input_i = 0; input_i < ConfigData.Size; input_i++) { - ImFontAtlasData& data = *InputData[input_i]; + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; // Setup ranges int glyph_count = 0; int glyph_ranges_count = 0; - for (const ImWchar* in_range = data.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) { glyph_count += (in_range[1] - in_range[0]) + 1; glyph_ranges_count++; } - data.Ranges = buf_ranges + buf_ranges_n; - data.RangesCount = glyph_ranges_count; + tmp.Ranges = buf_ranges + buf_ranges_n; + tmp.RangesCount = glyph_ranges_count; buf_ranges_n += glyph_ranges_count; for (int i = 0; i < glyph_ranges_count; i++) { - const ImWchar* in_range = &data.GlyphRanges[i * 2]; - stbtt_pack_range& range = data.Ranges[i]; - range.font_size = data.SizePixels; + const ImWchar* in_range = &cfg.GlyphRanges[i * 2]; + stbtt_pack_range& range = tmp.Ranges[i]; + range.font_size = cfg.SizePixels; range.first_unicode_char_in_range = in_range[0]; range.num_chars_in_range = (in_range[1] - in_range[0]) + 1; range.chardata_for_range = buf_packedchars + buf_packedchars_n; @@ -9635,15 +9962,16 @@ bool ImFontAtlas::Build() } // Pack - data.Rects = buf_rects + buf_rects_n; + tmp.Rects = buf_rects + buf_rects_n; buf_rects_n += glyph_count; - const int n = stbtt_PackFontRangesGatherRects(&spc, &data.FontInfo, data.Ranges, data.RangesCount, data.Rects); - stbrp_pack_rects((stbrp_context*)spc.pack_info, data.Rects, n); + stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); + int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); + stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n); // Extend texture height for (int i = 0; i < n; i++) - if (data.Rects[i].was_packed) - TexHeight = ImMax(TexHeight, data.Rects[i].y + data.Rects[i].h); + if (tmp.Rects[i].was_packed) + TexHeight = ImMax(TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); } IM_ASSERT(buf_rects_n == total_glyph_count); IM_ASSERT(buf_packedchars_n == total_glyph_count); @@ -9657,11 +9985,13 @@ bool ImFontAtlas::Build() spc.height = TexHeight; // Second pass: render characters - for (int input_i = 0; input_i < InputData.Size; input_i++) + for (int input_i = 0; input_i < ConfigData.Size; input_i++) { - ImFontAtlasData& data = *InputData[input_i]; - ret = stbtt_PackFontRangesRenderIntoRects(&spc, &data.FontInfo, data.Ranges, data.RangesCount, data.Rects); - data.Rects = NULL; + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); + stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); + tmp.Rects = NULL; } // End packing @@ -9669,58 +9999,72 @@ bool ImFontAtlas::Build() ImGui::MemFree(buf_rects); buf_rects = NULL; - ImGuiIO& io = ImGui::GetIO(); - const float PixelCenterOffset = io.PixelCenterOffset; +// ImGuiIO& io = ImGui::GetIO(); +// const float PixelCenterOffset = io.PixelCenterOffset; // Third pass: setup ImFont and glyphs for runtime - for (int input_i = 0; input_i < InputData.Size; input_i++) + for (int input_i = 0; input_i < ConfigData.Size; input_i++) { - ImFontAtlasData& data = *InputData[input_i]; - data.OutFont->ContainerAtlas = this; - data.OutFont->FontSize = data.SizePixels; + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + ImFont* dst_font = cfg.DstFont; - const float font_scale = stbtt_ScaleForPixelHeight(&data.FontInfo, data.SizePixels); - int font_ascent, font_descent, font_line_gap; - stbtt_GetFontVMetrics(&data.FontInfo, &font_ascent, &font_descent, &font_line_gap); - data.OutFont->Ascent = (font_ascent * font_scale); - data.OutFont->Descent = (font_descent * font_scale); - data.OutFont->Glyphs.resize(0); + float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels); + int unscaled_ascent, unscaled_descent, unscaled_line_gap; + stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); - const float uv_scale_x = 1.0f / TexWidth; - const float uv_scale_y = 1.0f / TexHeight; - const int character_spacing_x = 1; - for (int i = 0; i < data.RangesCount; i++) + float ascent = unscaled_ascent * font_scale; + float descent = unscaled_descent * font_scale; + if (!cfg.MergeMode) { - stbtt_pack_range& range = data.Ranges[i]; + dst_font->ContainerAtlas = this; + dst_font->ConfigData = &cfg; + dst_font->ConfigDataCount = 0; + dst_font->FontSize = cfg.SizePixels; + dst_font->Ascent = ascent; + dst_font->Descent = descent; + dst_font->Glyphs.resize(0); + } + dst_font->ConfigDataCount++; + float off_y = (cfg.MergeMode && cfg.MergeGlyphCenterV) ? (ascent - dst_font->Ascent) * 0.5f : 0.0f; + + dst_font->FallbackGlyph = NULL; // Always clear fallback so FindGlyph can return NULL. It will be set again in BuildLookupTable() + for (int i = 0; i < tmp.RangesCount; i++) + { + stbtt_pack_range& range = tmp.Ranges[i]; for (int char_idx = 0; char_idx < range.num_chars_in_range; char_idx += 1) { const stbtt_packedchar& pc = range.chardata_for_range[char_idx]; if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1) continue; - data.OutFont->Glyphs.resize(data.OutFont->Glyphs.Size + 1); - ImFont::Glyph& glyph = data.OutFont->Glyphs.back(); - glyph.Codepoint = (ImWchar)(range.first_unicode_char_in_range + char_idx); - glyph.Width = (signed short)pc.x1 - pc.x0 + 1; - glyph.Height = (signed short)pc.y1 - pc.y0 + 1; - glyph.XOffset = (signed short)(pc.xoff); - glyph.YOffset = (signed short)(pc.yoff + (int)(font_ascent * font_scale)); - glyph.XAdvance = (signed short)(pc.xadvance + character_spacing_x); // Bake spacing into XAdvance - glyph.U0 = ((float)pc.x0 - PixelCenterOffset) * uv_scale_x; - glyph.V0 = ((float)pc.y0 - PixelCenterOffset) * uv_scale_y; - glyph.U1 = ((float)pc.x0 - PixelCenterOffset + glyph.Width) * uv_scale_x; - glyph.V1 = ((float)pc.y0 - PixelCenterOffset + glyph.Height) * uv_scale_y; + const int codepoint = range.first_unicode_char_in_range + char_idx; + if (cfg.MergeMode && dst_font->FindGlyph((unsigned short)codepoint)) + continue; + + stbtt_aligned_quad q; + float dummy_x = 0.0f, dummy_y = 0.0f; + stbtt_GetPackedQuad(range.chardata_for_range, TexWidth, TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); + + dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1); + ImFont::Glyph& glyph = dst_font->Glyphs.back(); + glyph.Codepoint = (ImWchar)codepoint; + glyph.X0 = q.x0; glyph.Y0 = q.y0; glyph.X1 = q.x1; glyph.Y1 = q.y1; + glyph.U0 = q.s0; glyph.V0 = q.t0; glyph.U1 = q.s1; glyph.V1 = q.t1; + glyph.Y0 += (float)(int)(dst_font->Ascent + off_y + 0.5f); + glyph.Y1 += (float)(int)(dst_font->Ascent + off_y + 0.5f); + glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance + if (cfg.PixelSnapH) + glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f); } } - - data.OutFont->BuildLookupTable(); + cfg.DstFont->BuildLookupTable(); } // Cleanup temporaries ImGui::MemFree(buf_packedchars); ImGui::MemFree(buf_ranges); - buf_packedchars = NULL; - buf_ranges = NULL; + ImGui::MemFree(tmp_array); // Render into our custom data block RenderCustomTexData(1, &extra_rects); @@ -9939,7 +10283,9 @@ ImFont::~ImFont() void ImFont::Clear() { FontSize = 0.0f; - DisplayOffset = ImVec2(-0.5f, 0.5f); + DisplayOffset = ImVec2(0.0f, 1.0f); + ConfigData = NULL; + ConfigDataCount = 0; Ascent = Descent = 0.0f; ContainerAtlas = NULL; Glyphs.clear(); @@ -10391,7 +10737,9 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re const bool word_wrap_enabled = (wrap_width > 0.0f); const char* word_wrap_eol = NULL; - ImDrawVert* out_vertices = draw_list->vtx_write; + ImDrawVert* vtx_write = draw_list->_VtxWritePtr; + ImDrawIdx* idx_write = draw_list->_IdxWritePtr; + unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; const char* s = text_begin; if (!word_wrap_enabled && y + line_height < clip_rect.y) @@ -10466,11 +10814,11 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re if (c != ' ' && c != '\t') { // We don't do a second finer clipping test on the Y axis (todo: do some measurement see if it is worth it, probably not) - float y1 = (float)(y + glyph->YOffset * scale); - float y2 = (float)(y1 + glyph->Height * scale); + float y1 = (float)(y + glyph->Y0 * scale); + float y2 = (float)(y + glyph->Y1 * scale); - float x1 = (float)(x + glyph->XOffset * scale); - float x2 = (float)(x1 + glyph->Width * scale); + float x1 = (float)(x + glyph->X0 * scale); + float x2 = (float)(x + glyph->X1 * scale); if (x1 <= clip_rect.z && x2 >= clip_rect.x) { // Render a character @@ -10505,26 +10853,18 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re } // NB: we are not calling PrimRectUV() here because non-inlined causes too much overhead in a debug build. - out_vertices[0].pos = ImVec2(x1, y1); - out_vertices[0].uv = ImVec2(u1, v1); - out_vertices[0].col = col; - - out_vertices[1].pos = ImVec2(x2, y1); - out_vertices[1].uv = ImVec2(u2, v1); - out_vertices[1].col = col; - - out_vertices[2].pos = ImVec2(x2, y2); - out_vertices[2].uv = ImVec2(u2, v2); - out_vertices[2].col = col; - - out_vertices[3] = out_vertices[0]; - out_vertices[4] = out_vertices[2]; - - out_vertices[5].pos = ImVec2(x1, y2); - out_vertices[5].uv = ImVec2(u1, v2); - out_vertices[5].col = col; - - out_vertices += 6; + // inlined: + { + 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 += 4; + vtx_current_idx += 4; + idx_write += 6; + } } } } @@ -10532,7 +10872,9 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re x += char_width; } - draw_list->vtx_write = out_vertices; + draw_list->_VtxWritePtr = vtx_write; + draw_list->_VtxCurrentIdx = vtx_current_idx; + draw_list->_IdxWritePtr = idx_write; } //----------------------------------------------------------------------------- @@ -10697,6 +11039,13 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.55f); + if (ImGui::TreeNode("Rendering")) + { + ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); + ImGui::Checkbox("Anti-aliased shapes", &style.AntiAliasedShapes); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Sizes")) { ImGui::SliderFloat("Alpha", &style.Alpha, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI. But application code could have a toggle to switch between zero and non-zero. @@ -10712,6 +11061,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("ScrollbarWidth", &style.ScrollbarWidth, 1.0f, 20.0f, "%.0f"); ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 16.0f, "%.0f"); ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 16.0f, "%.0f"); ImGui::TreePop(); } @@ -10897,26 +11247,39 @@ void ImGui::ShowTestWindow(bool* opened) if (ImGui::TreeNode("Fonts", "Fonts (%d)", ImGui::GetIO().Fonts->Fonts.Size)) { - ImGui::TextWrapped("Tip: Load fonts with GetIO().Fonts->AddFontFromFileTTF()."); - for (int i = 0; i < ImGui::GetIO().Fonts->Fonts.Size; i++) + ImGui::TextWrapped("Tip: Load fonts with io.Fonts->AddFontFromFileTTF()."); + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + if (ImGui::TreeNode("Atlas texture")) { - ImFont* font = ImGui::GetIO().Fonts->Fonts[i]; - ImGui::BulletText("Font %d: %.2f pixels, %d glyphs", i, font->FontSize, font->Glyphs.Size); + ImGui::Text("%dx%d pixels", atlas->TexWidth, atlas->TexHeight); + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + ImGui::TreePop(); + } + ImGui::PushItemWidth(100); + for (int i = 0; i < atlas->Fonts.Size; i++) + { + ImFont* font = atlas->Fonts[i]; + ImGui::BulletText("Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData[0].Name, font->FontSize, font->Glyphs.Size); ImGui::TreePush((void*)i); + if (i > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { atlas->Fonts[i] = atlas->Fonts[0]; atlas->Fonts[0] = font; } } ImGui::PushFont(font); ImGui::Text("The quick brown fox jumps over the lazy dog"); ImGui::PopFont(); - if (i > 0 && ImGui::Button("Set as default")) + if (ImGui::TreeNode("Details")) { - ImGui::GetIO().Fonts->Fonts[i] = ImGui::GetIO().Fonts->Fonts[0]; - ImGui::GetIO().Fonts->Fonts[0] = font; + ImGui::DragFloat("font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this font + ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); + ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar); + for (int j = 0; j < font->ConfigDataCount; j++) + ImGui::BulletText("Input %d: \'%s\'", j, font->ConfigData[j].Name); + ImGui::TreePop(); } - ImGui::SliderFloat("font scale", &font->Scale, 0.3f, 2.0f, "%.1f"); // scale only this font ImGui::TreePop(); } static float window_scale = 1.0f; - ImGui::SliderFloat("this window scale", &window_scale, 0.3f, 2.0f, "%.1f"); // scale only this window - ImGui::SliderFloat("global scale", &ImGui::GetIO().FontGlobalScale, 0.3f, 2.0f, "%.1f"); // scale everything + ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this window + ImGui::DragFloat("global scale", &ImGui::GetIO().FontGlobalScale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale everything + ImGui::PopItemWidth(); ImGui::SetWindowFontScale(window_scale); ImGui::TreePop(); } @@ -11380,7 +11743,7 @@ void ImGui::ShowTestWindow(bool* opened) ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); static bool pause; - static ImVector values; if (values.empty()) { values.resize(90); memset(&values.front(), 0, values.Size*sizeof(float)); } + static ImVector values; if (values.empty()) { values.resize(90); memset(values.Data, 0, values.Size*sizeof(float)); } static int values_offset = 0; if (!pause) { @@ -11393,7 +11756,7 @@ void ImGui::ShowTestWindow(bool* opened) phase += 0.10f*values_offset; } } - ImGui::PlotLines("##Graph", &values.front(), (int)values.Size, values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); + ImGui::PlotLines("##Graph", values.Data, values.Size, values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::BeginGroup(); ImGui::Text("Graph"); @@ -11715,6 +12078,17 @@ void ImGui::ShowTestWindow(bool* opened) ShowExampleMenuFile(); ImGui::EndPopup(); } + + ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); + ImGui::Separator(); + ImGui::MenuItem("Menu item", "CTRL+M"); + if (ImGui::BeginMenu("Menu")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + ImGui::Separator(); + ImGui::TreePop(); } @@ -12046,9 +12420,9 @@ void ImGui::ShowMetricsWindow(bool* opened) { ImGui::Text("ImGui %s", ImGui::GetVersion()); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); - ImGui::Text("%d vertices", ImGui::GetIO().MetricsRenderVertices); + ImGui::Text("%d vertices, %d triangles", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices / 3); ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs); - static bool show_clip_rects = false; + static bool show_clip_rects = true; ImGui::Checkbox("Show clipping rectangles when hovering ImDrawList", &show_clip_rects); ImGui::Separator(); @@ -12056,7 +12430,7 @@ void ImGui::ShowMetricsWindow(bool* opened) { static void NodeDrawList(ImDrawList* draw_list, const char* label) { - bool node_opened = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d cmds", label, draw_list->owner_name ? draw_list->owner_name : "", draw_list->vtx_buffer.Size, draw_list->commands.Size); + bool node_opened = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); if (draw_list == ImGui::GetWindowDrawList()) { ImGui::SameLine(); @@ -12064,16 +12438,23 @@ void ImGui::ShowMetricsWindow(bool* opened) } if (!node_opened) return; - for (const ImDrawCmd* pcmd = draw_list->commands.begin(); pcmd < draw_list->commands.end(); pcmd++) - if (pcmd->user_callback) - ImGui::BulletText("Callback %p, user_data %p", pcmd->user_callback, pcmd->user_callback_data); + + int elem_offset = 0; + for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) + if (pcmd->UserCallback) + ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); else { - ImGui::BulletText("Draw %d vtx, tex = %p, clip_rect = (%d,%d)..(%d,%d)", pcmd->vtx_count, pcmd->texture_id, (int)pcmd->clip_rect.x, (int)pcmd->clip_rect.y, (int)pcmd->clip_rect.z, (int)pcmd->clip_rect.w); + ImGui::BulletText("Draw %d indexed vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); if (show_clip_rects && ImGui::IsItemHovered()) { + ImRect clip_rect = pcmd->ClipRect; + ImRect vtxs_rect; + for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) + vtxs_rect.Add(draw_list->VtxBuffer[draw_list->IdxBuffer[i]].pos); GImGui->OverlayDrawList.PushClipRectFullScreen(); - GImGui->OverlayDrawList.AddRect(ImVec2(pcmd->clip_rect.x, pcmd->clip_rect.y), ImVec2(pcmd->clip_rect.z, pcmd->clip_rect.w), ImColor(255,255,0)); + clip_rect.Round(); GImGui->OverlayDrawList.AddRect(clip_rect.Min, clip_rect.Max, ImColor(255,255,0)); + vtxs_rect.Round(); GImGui->OverlayDrawList.AddRect(vtxs_rect.Min, vtxs_rect.Max, ImColor(255,0,255)); GImGui->OverlayDrawList.PopClipRect(); } } @@ -12110,10 +12491,13 @@ void ImGui::ShowMetricsWindow(bool* opened) Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList"); ImGui::TreePop(); } - if (ImGui::TreeNode("Popups", "Opened Popups (%d)", g.OpenedPopupStack.Size)) + if (ImGui::TreeNode("Popups", "Opened Popups Stack (%d)", g.OpenedPopupStack.Size)) { for (int i = 0; i < g.OpenedPopupStack.Size; i++) - ImGui::BulletText("PopupID: %08x, Window: '%s'", g.OpenedPopupStack[i].PopupID, g.OpenedPopupStack[i].Window ? g.OpenedPopupStack[i].Window->Name : "NULL"); + { + ImGuiWindow* window = g.OpenedPopupStack[i].Window; + ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenedPopupStack[i].PopupID, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); + } ImGui::TreePop(); } g.DisableHideTextAfterDoubleHash--; @@ -12179,8 +12563,10 @@ static void ShowExampleMenuFile() ImGui::Text("Scrolling Text %d", i); ImGui::EndChild(); static float f = 0.5f; + static int n = 0; ImGui::SliderFloat("Value", &f, 0.0f, 1.0f); ImGui::InputFloat("Input", &f, 0.1f); + ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0"); ImGui::EndMenu(); } if (ImGui::BeginMenu("Colors")) diff --git a/3rdparty/ocornut-imgui/imgui.h b/3rdparty/ocornut-imgui/imgui.h index 27347fa8..5128ed8a 100644 --- a/3rdparty/ocornut-imgui/imgui.h +++ b/3rdparty/ocornut-imgui/imgui.h @@ -31,6 +31,7 @@ // Forward declarations struct ImDrawCmd; struct ImDrawList; +struct ImDrawData; struct ImFont; struct ImFontAtlas; struct ImGuiIO; @@ -628,8 +629,11 @@ struct ImGuiStyle float ScrollbarWidth; // Width of the vertical scrollbar float ScrollbarRounding; // Radius of grab corners for scrollbar float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar + float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. + bool AntiAliasedShapes; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) ImVec4 Colors[ImGuiCol_COUNT]; IMGUI_API ImGuiStyle(); @@ -669,7 +673,7 @@ struct ImGuiIO // REQUIRED: rendering function. // See example code if you are unsure of how to implement this. - void (*RenderDrawListsFn)(ImDrawList** const draw_lists, int count); + void (*RenderDrawListsFn)(ImDrawData* data); // Optional: access OS clipboard // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) @@ -712,6 +716,7 @@ struct ImGuiIO float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames int MetricsAllocs; // Number of active memory allocations int MetricsRenderVertices; // Vertices processed during last call to Render() + int MetricsRenderIndices; // int MetricsActiveWindows; // Number of visible windows (exclude child windows) //------------------------------------------------------------------ @@ -849,7 +854,7 @@ struct ImGuiTextBuffer ImGuiTextBuffer() { Buf.push_back(0); } const char* begin() const { return &Buf.front(); } const char* end() const { return &Buf.back(); } // Buf is zero-terminated, so end() will point on the zero-terminator - int size() const { return Buf.size()-1; } + int size() const { return Buf.Size-1; } bool empty() { return size() >= 1; } void clear() { Buf.clear(); Buf.push_back(0); } IMGUI_API void append(const char* fmt, ...); @@ -974,15 +979,15 @@ struct ImGuiListClipper //----------------------------------------------------------------------------- // Draw List -// Hold a series of drawing commands. The user provides a renderer for ImDrawList. +// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. //----------------------------------------------------------------------------- // Draw callbacks for advanced uses. -// NB- You most likely DO NOT need to care about draw callbacks just to create your own widget or customized UI rendering (you can poke into the draw list for that) +// NB- You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering (you can poke into the draw list for that) // Draw callback are useful for example if you want to render a complex 3D scene inside a UI element. // The expected behavior from your rendering loop is: -// if (cmd.user_callback != NULL) -// cmd.user_callback(parent_list, cmd); +// if (cmd.UserCallback != NULL) +// cmd.UserCallback(parent_list, cmd); // else // RenderTriangles() // It is up to you to decide if your rendering loop or the callback should be responsible for backup/restoring rendering state. @@ -991,13 +996,16 @@ typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* c // Typically, 1 command = 1 gpu draw call (unless command is a callback) struct ImDrawCmd { - unsigned int vtx_count; // Number of vertices (multiple of 3) to be drawn as triangles. The vertices are stored in the callee ImDrawList's vtx_buffer[] array. - ImVec4 clip_rect; // Clipping rectangle (x1, y1, x2, y2) - ImTextureID texture_id; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. - ImDrawCallback user_callback; // If != NULL, call the function instead of rendering the vertices. vtx_count will be 0. clip_rect and texture_id will be set normally. - void* user_callback_data; // The draw callback code can access this. + unsigned int ElemCount; // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. + ImVec4 ClipRect; // Clipping rectangle (x1, y1, x2, y2) + ImTextureID TextureId; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. + ImDrawCallback UserCallback; // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. + void* UserCallbackData; // The draw callback code can access this. }; +// Vertex index +typedef unsigned short ImDrawIdx; + // Vertex layout #ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert @@ -1013,6 +1021,13 @@ struct ImDrawVert IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; #endif +// Draw channels are used by the Columns API to "split" the render list into different channels while building, so items of each column can be batched together. +struct ImDrawChannel +{ + ImVector CmdBuffer; + ImVector IdxBuffer; +}; + // Draw command list // This is the low-level list of polygons that ImGui functions are filling. At the end of the frame, all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. // At the moment, each ImGui window contains its own ImDrawList but they could potentially be merged in the future. @@ -1023,16 +1038,23 @@ IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; struct ImDrawList { // This is what you have to render - ImVector commands; // Commands. Typically 1 command = 1 gpu draw call. - ImVector vtx_buffer; // Vertex buffer. Each command consume ImDrawCmd::vtx_count of those + ImVector CmdBuffer; // Commands. Typically 1 command = 1 gpu draw call. + ImVector IdxBuffer; // Index buffer. Each command consume ImDrawCmd::ElemCount of those + ImVector VtxBuffer; // Vertex buffer. - // [Internal to ImGui] - const char* owner_name; // Pointer to owner window's name, if any - 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) + // [Internal, used while building lists] + const char* _OwnerName; // Pointer to owner window's name (if any) for debugging + unsigned int _VtxCurrentIdx; // [Internal] == VtxBuffer.Size + ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) + ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) + ImVector _ClipRectStack; // [Internal] + ImVector _TextureIdStack; // [Internal] + ImVector _Path; // [Internal] current path building + int _ChannelCurrent; // [Internal] current channel number (0) + ImVector _Channels; // [Internal] draw channels for columns API - ImDrawList() { owner_name = NULL; Clear(); } + ImDrawList() { _OwnerName = NULL; Clear(); } + ~ImDrawList() { ClearFreeMemory(); } IMGUI_API void Clear(); IMGUI_API void ClearFreeMemory(); IMGUI_API void PushClipRect(const ImVec4& clip_rect); // Scissoring. The values are x1, y1, x2, y2. @@ -1049,24 +1071,70 @@ struct ImDrawList IMGUI_API void AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col); IMGUI_API void AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12); IMGUI_API void AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12); - IMGUI_API void AddArcFast(const ImVec2& center, float radius, ImU32 col, int a_min_12, int a_max_12, bool filled = false, const ImVec2& third_point_offset = ImVec2(0,0)); // Angles in 0..12 range IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col = 0xFFFFFFFF); + IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness, bool anti_aliased); + IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col, bool anti_aliased); + + // Stateful path API, add points then finish with PathFill() or PathStroke() + inline void PathClear() { _Path.resize(0); } + inline void PathLineTo(const ImVec2& p) { _Path.push_back(p); } + IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min, int a_max); + IMGUI_API void PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments = 12); + IMGUI_API void PathRect(const ImVec2& a, const ImVec2& b, float rounding = 0.0f, int rounding_corners = 0x0F); + inline void PathFill(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col, true); PathClear(); } + inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness, true); PathClear(); } // Advanced - IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'user_callback' in ImDrawCmd and call the function instead of rendering triangles. + IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible + IMGUI_API void ChannelsSplit(int channel_count); + IMGUI_API void ChannelsMerge(int channel_count); + IMGUI_API void ChannelsSetCurrent(int idx); // Internal helpers - IMGUI_API void PrimReserve(unsigned int vtx_count); - IMGUI_API void PrimTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col); + // NB: all primitives needs to be reserved via PrimReserve() beforehand! + IMGUI_API void PrimReserve(int idx_count, int vtx_count); IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col); - IMGUI_API void PrimQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col); - IMGUI_API void PrimLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness = 1.0f); + inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } + inline void PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col){ _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; } + inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; } IMGUI_API void UpdateClipRect(); IMGUI_API void UpdateTextureID(); - IMGUI_API void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { vtx_write->pos = pos; vtx_write->uv = uv; vtx_write->col = col; vtx_write++; } +}; + +// All draw data to render an ImGui frame +struct ImDrawData +{ + ImDrawList** CmdLists; + int CmdListsCount; + int TotalVtxCount; // For convenience, sum of all cmd_lists vtx_buffer.Size + int TotalIdxCount; // For convenience, sum of all cmd_lists idx_buffer.Size + + // Functions + void DeIndexAllBuffers(); // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! +}; + +struct ImFontConfig +{ + void* FontData; // // TTF data + int FontDataSize; // // TTF data size + bool FontDataOwnedByAtlas; // true // TTF data ownership taken by the container ImFontAtlas (will delete memory itself). Set to true + int FontNo; // 0 // Index of font within TTF file + float SizePixels; // // Size in pixels for rasterizer + int OversampleH, OversampleV; // 3, 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. + bool PixelSnapH; // false // Align every character to pixel boundary (if enabled, set OversampleH/V to 1) + ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs + const ImWchar* GlyphRanges; // // List of Unicode range (2 value per range, values are inclusive, zero-terminated list) + bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). + bool MergeGlyphCenterV; // false // When merging (multiple ImFontInput for one ImFont), vertically center new glyphs instead of aligning their baseline + + // [Internal] + char Name[32]; // Name (strictly for debugging) + ImFont* DstFont; + + IMGUI_API ImFontConfig(); }; // Load and rasterize multiple TTF fonts into a same texture. @@ -1081,10 +1149,11 @@ struct ImFontAtlas { IMGUI_API ImFontAtlas(); IMGUI_API ~ImFontAtlas(); - IMGUI_API ImFont* AddFontDefault(); - IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0); - IMGUI_API ImFont* AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0); // Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build() - IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0); // 'compressed_ttf_data' untouched and still owned by caller. Compress with binary_to_compressed_c.cpp + IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); + IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); + IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); + IMGUI_API ImFont* AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build() + IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_ttf_data' untouched and still owned by caller. Compress with binary_to_compressed_c.cpp IMGUI_API void ClearTexData(); // Clear the CPU-side texture data. Saves RAM once the texture has been copied to graphics memory. IMGUI_API void ClearInputData(); // Clear the input TTF data (inc sizes, glyph ranges) IMGUI_API void ClearFonts(); // Clear the ImGui-side font data (glyphs storage, UV coordinates) @@ -1117,47 +1186,45 @@ struct ImFontAtlas ImVector Fonts; // Private - struct ImFontAtlasData; - ImVector InputData; // Internal data + ImVector ConfigData; // Internal data IMGUI_API bool Build(); // Build pixels data. This is automatically for you by the GetTexData*** functions. IMGUI_API void RenderCustomTexData(int pass, void* rects); }; -// TTF font loading and rendering +// Font runtime data and rendering // ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). -// Kerning isn't supported. At the moment some ImGui code does per-character CalcTextSize calls, need something more state-ful. struct ImFont { // Members: Settings - float FontSize; // // Height of characters, set during loading (don't change after loading) - 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. Only set via SetFallbackChar() + float FontSize; // // Height of characters, set during loading (don't change after loading) + 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. Only set via SetFallbackChar() + ImFontConfig* ConfigData; // // Pointer within ImFontAtlas->ConfigData + int ConfigDataCount; // // Members: Runtime data struct Glyph { - ImWchar Codepoint; - signed short XAdvance; - signed short Width, Height; - signed short XOffset, YOffset; - float U0, V0, U1, V1; // Texture coordinates + ImWchar Codepoint; + float XAdvance; + float X0, Y0, X1, Y1; + float U0, V0, U1, V1; // Texture coordinates }; - float Ascent; // Distance from top to bottom of e.g. 'A' [0..FontSize] - float Descent; // - ImFontAtlas* ContainerAtlas; // What we has been loaded into - ImVector Glyphs; - const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) - float FallbackXAdvance; // - ImVector IndexXAdvance; // Glyphs->XAdvance directly indexable (for CalcTextSize functions which are often bottleneck in large UI) - ImVector IndexLookup; // Index glyphs by Unicode code-point + float Ascent, Descent; // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] + ImFontAtlas* ContainerAtlas; // What we has been loaded into + ImVector Glyphs; + const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) + float FallbackXAdvance; // + ImVector IndexXAdvance; // Sparse. Glyphs->XAdvance directly indexable (for CalcTextSize functions which are often bottleneck in large UI) + ImVector IndexLookup; // Sparse. Index glyphs by Unicode code-point // Methods IMGUI_API ImFont(); IMGUI_API ~ImFont(); IMGUI_API void Clear(); IMGUI_API void BuildLookupTable(); - IMGUI_API float GetCharAdvance(unsigned short c) const { return ((int)c < IndexXAdvance.size()) ? IndexXAdvance[(int)c] : FallbackXAdvance; } + IMGUI_API float GetCharAdvance(unsigned short c) const { return ((int)c < IndexXAdvance.Size) ? IndexXAdvance[(int)c] : FallbackXAdvance; } IMGUI_API const Glyph* FindGlyph(unsigned short c) const; IMGUI_API void SetFallbackChar(ImWchar c); IMGUI_API bool IsLoaded() const { return ContainerAtlas != NULL; }