From d81c5cbae3d61d148cc28f6d2f6000d46e9247c9 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 18 Nov 2019 14:41:02 +0100 Subject: [PATCH] Texture-based round corners: Make FIXME consistent so they can be grepped. Tidying and added notes of things to fix. --- imgui.cpp | 21 +------ imgui.h | 3 +- imgui_demo.cpp | 148 ++++++++++++++++++++++++++--------------------- imgui_draw.cpp | 35 +++++------ imgui_internal.h | 2 +- 5 files changed, 103 insertions(+), 106 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7201a0c5..5295ee43 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5547,25 +5547,10 @@ static const ImGuiResizeBorderDef resize_border_def[4] = static void AddResizeGrip(ImDrawList* dl, const ImVec2& corner, unsigned int rad, int corners_flags, ImU32 col) { ImTextureID tex = dl->_Data->Font->ContainerAtlas->TexID; - IM_ASSERT(tex == dl->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. - - switch (corners_flags) - { - case ImDrawFlags_RoundCornersTopLeft: - case ImDrawFlags_RoundCornersTopRight: - case ImDrawFlags_RoundCornersBottomLeft: - case ImDrawFlags_RoundCornersBottomRight: - break; - default: - { - IM_ASSERT("Invalid ImDrawCornerFlags for corner quad. {Top,Bot}{Left,Right} pick exactly one of each!"); - return; - } - } + IM_ASSERT(tex == dl->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + IM_ASSERT(ImIsPowerOfTwo(corners_flags)); // Allow a single corner to be specified here. const ImVec4& uvs = (*dl->_Data->TexUvRoundCornerFilled)[rad - 1]; - - // NOTE: test performance using locals instead of array const ImVec2 uv[] = { ImVec2(ImLerp(uvs.x, uvs.z, 0.5f), ImLerp(uvs.y, uvs.w, 0.5f)), @@ -5861,7 +5846,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); - if (g.IO.KeyAlt) + if (g.IO.KeyAlt) // FIXME-ROUNDCORNERS { ImVec2 grip_corner = corner; grip_corner.x += grip.InnerDir.x * window_border_size; diff --git a/imgui.h b/imgui.h index a1fa4e11..fe9b0e5b 100644 --- a/imgui.h +++ b/imgui.h @@ -2833,7 +2833,8 @@ struct ImFontAtlas int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines - // FIXME-ROUND-SHAPES: WIP + // FIXME-ROUNDCORNERS: WIP + // FIXME: avoid so many allocations, statically sized buffer removing an indirection may be beneficial here? int RoundCornersMaxSize; // Max pixel size of round corner textures to generate ImVector RoundCornersRectIds; // Ids of custom rects for round corners indexed by size [0] is 1px, [n] is (n+1)px (index up to RoundCornersMaxSize - 1). ImVector TexUvRoundCornerFilled; // Texture coordinates to filled round corner quads diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7eb9d2e3..9890c30f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -257,94 +257,108 @@ static void GetVtxIdxDelta(ImDrawList* dl, int* vtx, int *idx) idx_o = idx_n; } +// https://github.com/ocornut/imgui/issues/1962 +// FIXME-ROUNDCORNERS: Sizes aren't matching. +// FIXME-ROUNDCORNERS: Lift the RoundCornersMaxSize limitation, fallback on existing renderer. +// FIXME-ROUNDCORNERS: Work on reducing filtrate for stroked shapes (may need to trade some cpu/vtx to reduce fill-rate for e.g. simple stroked circle) > https://github.com/ocornut/imgui/issues/1962#issuecomment-411507917 +// FIXME-ROUNDCORNERS: Figure out how to support multiple thickness, might hard-code common steps (1.0, 1.5, 2.0, 3.0), not super satisfactory but may be best +// FIXME-ROUNDCORNERS: AddCircle* API relying on num_segments may need rework, might obsolete this parameter, or make it 0 default and rely on automatic subdivision similar to style.CurveTessellationTol for Bezier +// FIXME-ROUNDCORNERS: Intentional "low segment count" shapes (e.g. hexagon) currently achieved with AddCircle may need a new API (AddNgon?) static void TestTextureBasedRender() { ImGuiIO& io = ImGui::GetIO(); + ImGuiStyle& style = ImGui::GetStyle(); - ImGui::TextUnformatted("Press SHIFT to toggle quads (hold to see them)."); - ImGui::TextUnformatted(io.KeyShift ? "SHIFT ON -- Rasterized quad circle! w00t! OPTIMIZATION!" - : "SHIFT OFF -- Regular, boring circle with PathArcToFast."); + ImGui::Begin("tex_round_corners"); + + ImGui::Text("Hold SHIFT to toggle (%s)", io.KeyShift ? "SHIFT ON -- Using textures." : "SHIFT OFF -- Old method."); static float radius = io.Fonts->RoundCornersMaxSize * 0.5f; - ImGui::SliderFloat("radius", &radius, 0.0f, (float)io.Fonts->RoundCornersMaxSize, "%.0f"); - ImGui::BeginGroup(); - static int segments = 20; - ImGui::PushItemWidth(120); - ImGui::SliderInt("segments", &segments, 3, 100); - ImGui::PopItemWidth(); - int vtx = 0; - int idx = 0; - ImDrawList* dl = ImGui::GetWindowDrawList(); + ImGui::SliderFloat("radius", &radius, 0.0f, (float)io.Fonts->RoundCornersMaxSize, "%.0f"); + + int vtx_n = 0; + int idx_n = 0; + ImDrawList* draw_list = ImGui::GetWindowDrawList(); { - ImGui::Button("##1", ImVec2(200, 200)); - GetVtxIdxDelta(dl, &vtx, &idx); - ImVec2 min = ImGui::GetItemRectMin(); - ImVec2 size = ImGui::GetItemRectSize(); - dl->AddCircleFilled(ImVec2(min.x + size.x * 0.5f, min.y + size.y * 0.5f), radius, 0xFFFF00FF, segments); - GetVtxIdxDelta(dl, &vtx, &idx); - ImGui::Text("AddCircleFilled\n %d vtx, %d idx", vtx, idx); + ImGui::BeginGroup(); + + ImGui::PushItemWidth(120); + ImGui::SliderInt("segments", &segments, 3, 100); + ImGui::PopItemWidth(); + + { + ImGui::Button("##1", ImVec2(200, 200)); + GetVtxIdxDelta(draw_list, &vtx_n, &idx_n); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + draw_list->AddCircleFilled(ImVec2(min.x + size.x * 0.5f, min.y + size.y * 0.5f), radius, IM_COL32(255,0,255,255), segments); + GetVtxIdxDelta(draw_list, &vtx_n, &idx_n); + ImGui::Text("AddCircleFilled\n %d vtx, %d idx", vtx_n, idx_n); + } + { + ImGui::Button("##2", ImVec2(200, 200)); + GetVtxIdxDelta(draw_list, &vtx_n, &idx_n); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + draw_list->AddCircle(ImVec2(min.x + size.x * 0.5f, min.y + size.y * 0.5f), radius, IM_COL32(255,0,255,255), segments); + GetVtxIdxDelta(draw_list, &vtx_n, &idx_n); + ImGui::Text("AddCircle\n %d vtx, %d idx", vtx_n, idx_n); + } + ImGui::EndGroup(); } - { - ImGui::Button("##2", ImVec2(200, 200)); - GetVtxIdxDelta(dl, &vtx, &idx); - ImVec2 min = ImGui::GetItemRectMin(); - ImVec2 size = ImGui::GetItemRectSize(); - dl->AddCircle(ImVec2(min.x + size.x * 0.5f, min.y + size.y * 0.5f), radius, 0xFFFF00FF, segments); - GetVtxIdxDelta(dl, &vtx, &idx); - ImGui::Text("AddCircle\n %d vtx, %d idx", vtx, idx); - } - ImGui::EndGroup(); ImGui::SameLine(); - ImGui::BeginGroup(); - static bool tl = true, tr = true, bl = true, br = true; - ImGui::Checkbox("TL", &tl); - ImGui::SameLine(0, 12); - ImGui::Checkbox("TR", &tr); - ImGui::SameLine(0, 12); - ImGui::Checkbox("BL", &bl); - ImGui::SameLine(0, 12); - ImGui::Checkbox("BR", &br); - - ImDrawFlags flags = 0; - flags |= tl ? ImDrawFlags_RoundCornersTopLeft : 0; - flags |= tr ? ImDrawFlags_RoundCornersTopRight : 0; - flags |= bl ? ImDrawFlags_RoundCornersBottomLeft : 0; - flags |= br ? ImDrawFlags_RoundCornersBottomRight : 0; - if (flags == 0) - flags |= ImDrawFlags_RoundCornersNone; - { - ImGui::Button("", ImVec2(200, 200)); - ImVec2 r_min = ImGui::GetItemRectMin(); - ImVec2 r_max = ImGui::GetItemRectMax(); + ImGui::BeginGroup(); - GetVtxIdxDelta(dl, &vtx, &idx); - dl->AddRectFilled(r_min, r_max, 0xFFFF00FF, radius, flags); - GetVtxIdxDelta(dl, &vtx, &idx); - ImGui::Text("AddRectFilled\n %d vtx, %d idx", vtx, idx); + static ImDrawFlags corner_flags = ImDrawFlags_RoundCornersAll; + ImGui::CheckboxFlags("TL", (unsigned int*)&corner_flags, ImDrawFlags_RoundCornersTopLeft); + ImGui::SameLine(); + ImGui::CheckboxFlags("TR", (unsigned int*)&corner_flags, ImDrawFlags_RoundCornersTopRight); + ImGui::SameLine(); + ImGui::CheckboxFlags("BL", (unsigned int*)&corner_flags, ImDrawFlags_RoundCornersBottomLeft); + ImGui::SameLine(); + ImGui::CheckboxFlags("BR", (unsigned int*)&corner_flags, ImDrawFlags_RoundCornersBottomRight); + + { + ImGui::Button("##3", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(draw_list, &vtx_n, &idx_n); + draw_list->AddRectFilled(r_min, r_max, IM_COL32(255,0,255,255), radius, corner_flags ? corner_flags : ImDrawFlags_RoundCornersNone); + GetVtxIdxDelta(draw_list, &vtx_n, &idx_n); + ImGui::Text("AddRectFilled\n %d vtx, %d idx", vtx_n, idx_n); + } + { + ImGui::Button("##4", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(draw_list, &vtx_n, &idx_n); + draw_list->AddRect(r_min, r_max, IM_COL32(255,0,255,255), radius, corner_flags); + GetVtxIdxDelta(draw_list, &vtx_n, &idx_n); + ImGui::Text("AddRect\n %d vtx, %d idx", vtx_n, idx_n); + } + + ImGui::EndGroup(); } - { - ImGui::Button("", ImVec2(200, 200)); - ImVec2 r_min = ImGui::GetItemRectMin(); - ImVec2 r_max = ImGui::GetItemRectMax(); - - GetVtxIdxDelta(dl, &vtx, &idx); - dl->AddRect(r_min, r_max, 0xFFFF00FF, radius, flags); - GetVtxIdxDelta(dl, &vtx, &idx); - ImGui::Text("AddRect\n %d vtx, %d idx", vtx, idx); - } - - ImGui::EndGroup(); - ImGui::Separator(); + ImGui::Text("Style"); + ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 100.0f, "%.0f"); + ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 100.0f, "%.0f"); + + // Show atlas + ImGui::Text("Atlas"); ImFontAtlas* atlas = ImGui::GetIO().Fonts; 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::End(); } //----------------------------------------------------------------------------- diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 9a1d6402..a6870ee8 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1457,7 +1457,7 @@ inline void AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV int vyc = vcc, vxc = vcc, vic = vcc; int vyd = vcd, vxd = vcd, vid = vcd; - // FIXME-ROUND_SHAPES: TODO: find a way of saving vertices/triangles here? + // FIXME-ROUNDCORNERS: TODO: find a way of saving vertices/triangles here? // currently it's the same cost regardless of how many corners are rounded if (ba || 1) @@ -1577,7 +1577,7 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl rounding = ImMin(rounding, ImFabs(p_max.y - p_min.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f); #endif - // FIXME-ROUND-SHAPES: NOTE HACK TODO figure out why it's broken on small rounding + // FIXME-ROUNDCORNERS: NOTE HACK TODO figure out why it's broken on small rounding if (ImGui::GetIO().KeyShift && rounding > 3) return AddRoundCornerRect(this, p_min, p_max, col, rounding, flags, /* fill */ false); @@ -1606,7 +1606,7 @@ void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 c } else { - // FIXME-ROUND-SHAPES: NOTE HACK TODO figure out why it's broken on small rounding + // FIXME-ROUNDCORNERS: NOTE HACK TODO figure out why it's broken on small rounding if (ImGui::GetIO().KeyShift && rounding > 3) { AddRoundCornerRect(this, p_min, p_max, col, rounding, flags, /* fill */ true); @@ -1755,7 +1755,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f) return; - if (ImGui::GetIO().KeyShift) + if (ImGui::GetIO().KeyShift) // FIXME-ROUNDCORNERS { AddRoundCornerCircle(this, center, radius, col, false); return; @@ -1823,7 +1823,7 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; - if (ImGui::GetIO().KeyShift) + if (ImGui::GetIO().KeyShift) // FIXME-ROUNDCORNERS { AddRoundCornerCircle(this, center, radius, col, true); return; @@ -3073,9 +3073,8 @@ const int FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING = 2; static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas) { - if (atlas->RoundCornersRectIds.size() > 0) + if (atlas->RoundCornersRectIds.Size > 0) return; - if ((atlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)) return; @@ -3094,32 +3093,28 @@ static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas) static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas) { IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); - IM_ASSERT(atlas->TexUvRoundCornerFilled.size() == 0); - IM_ASSERT(atlas->TexUvRoundCornerStroked.size() == 0); - - if ((atlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)) + IM_ASSERT(atlas->TexUvRoundCornerFilled.Size == 0); + IM_ASSERT(atlas->TexUvRoundCornerStroked.Size == 0); + if (atlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners) return; + // Render the texture const int w = atlas->TexWidth; const unsigned int max = atlas->RoundCornersMaxSize; - - // Filled + const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING; for (unsigned int stage = 0; stage < 2; stage++) { - bool filled = stage == 0; + const bool filled = (stage == 0); for (unsigned int n = 0; n < max; n++) { const unsigned int id = (filled ? 0 : max) + n; - IM_ASSERT(atlas->RoundCornersRectIds.size() > (int) n); + IM_ASSERT(atlas->RoundCornersRectIds.Size > (int)n); ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->RoundCornersRectIds[id]]; IM_ASSERT(r.IsPacked()); - - const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING; - IM_ASSERT(r.Width == n + 1 + pad * 2 && r.Height == n + 1 + pad * 2); const int radius = (int)(r.Width - pad * 2); - const float stroke_width = 1.0f; + const float stroke_width = 1.0f; // FIXME-ROUNDCORNERS for (int y = -pad; y < (int) (radius); y++) for (int x = (filled ? -pad : y); x < (int)(filled ? y + pad : radius); x++) @@ -3129,10 +3124,12 @@ static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas) float alpha = 0.0f; if (filled) { + // Fill alpha = ImClamp(-dist, 0.0f, 1.0f); } else { + // Stroke const float alpha1 = ImClamp(dist + stroke_width, 0.0f, 1.0f); const float alpha2 = ImClamp(dist, 0.0f, 1.0f); alpha = alpha1 - alpha2; diff --git a/imgui_internal.h b/imgui_internal.h index b36d29c3..5a221b1b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -725,7 +725,7 @@ struct IMGUI_API ImDrawListSharedData ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead) const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas - // FIXME-ROUNDSHAPES: WIP + need to remove CircleVtx12 before PR + // FIXME-ROUNDCORNERS: WIP + need to remove CircleVtx12 before PR ImVector* TexUvRoundCornerFilled; // UV of filled round corner quad in the atlas ImVector* TexUvRoundCornerStroked; // UV of stroked round corner quad in the atlas