diff --git a/imgui.cpp b/imgui.cpp index 5591b547..d639e83e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5938,25 +5938,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)), @@ -6350,7 +6335,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar continue; 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 25e45af1..cf1c45ac 100644 --- a/imgui.h +++ b/imgui.h @@ -3120,7 +3120,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 125b64be..e5f55f02 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -262,94 +262,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"); - static float radius = io.Fonts->RoundCornersMaxSize * 0.5f; - ImGui::SliderFloat("radius", &radius, 0.0f, (float)io.Fonts->RoundCornersMaxSize, "%.0f"); - ImGui::BeginGroup(); + 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; 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::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::BeginGroup(); - ImGui::SameLine(); + ImGui::PushItemWidth(120); + ImGui::SliderInt("segments", &segments, 3, 100); + ImGui::PopItemWidth(); - 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); + { + 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(); + } - 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::SameLine(); { - 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); - } - { - ImGui::Button("", ImVec2(200, 200)); - ImVec2 r_min = ImGui::GetItemRectMin(); - ImVec2 r_max = ImGui::GetItemRectMax(); + 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); - 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::Button("##3", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); - ImGui::EndGroup(); + 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::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 beff9331..4b7221ce 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1465,7 +1465,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) @@ -1585,7 +1585,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); @@ -1614,7 +1614,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); @@ -1763,7 +1763,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f) return; - if (ImGui::GetIO().KeyShift) + if (ImGui::GetIO().KeyShift) // FIXME-ROUNDCORNERS { AddRoundCornerCircle(this, center, radius, col, false); return; @@ -1831,7 +1831,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; @@ -3518,9 +3518,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; @@ -3539,32 +3538,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++) @@ -3574,10 +3569,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 50a52160..945022e0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -786,7 +786,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