diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 07bade2b0..e2f5ff6e1 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -818,6 +818,20 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c _IdxWritePtr += 6; } + +static float CalculateCenterPixelAlignedOffset(float thickness, float _FringeScale) +{ + // Old jumpy one. + // return IM_TRUNC(thickness * 0.5f / _FringeScale) * _FringeScale; + + // Calculate outside offset for stroke position CenterPixelAligned + // so that one side the of the line is always at pixel boundary. + // On integer thickness both sides of the line are on pixel boundary. + const float screen_thickness = thickness / _FringeScale; + const int s_thickness = (int)screen_thickness; + return ((float)(s_thickness / 2) + (s_thickness & 1) * (screen_thickness - s_thickness)) * _FringeScale; +} + // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. // - Those macros expects l-values and need to be used as their own statement. // - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers. @@ -857,18 +871,14 @@ void ImDrawList::_AddPolyline(const ImVec2* points, ImVec2* normals, float* sqr_ const bool miters_only = (flags & ImDrawFlags_MiterOnly) != 0; const float miter_distance_limit_sqr = IM_POLYLINE_MITER_LIMIT * IM_POLYLINE_MITER_LIMIT; - float thickness0 = (thickness + fringe) * 0.5f; - float thickness1 = thickness0; + float thickness0 = (thickness + fringe) * 0.5f; // Center (or legacy) if (stroke_pos == ImDrawFlags_StrokeOutside) - { thickness0 = thickness + fringe * 0.5f; - thickness1 = fringe * 0.5f; - } + else if (stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) + thickness0 = CalculateCenterPixelAlignedOffset(thickness, _FringeScale) + fringe * 0.5f; else if (stroke_pos == ImDrawFlags_StrokeInside) - { thickness0 = fringe * 0.5f; - thickness1 = thickness + fringe * 0.5f; - } + float thickness1 = (thickness + fringe) - thickness0; int idx_count = 0; int vtx_count = 0; @@ -2032,19 +2042,6 @@ void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDr } } -static float CalculateCenterPixelAlignedOffset(float thickness, float _FringeScale) -{ - // Old jumpy one. - // return IM_TRUNC(thickness * 0.5f / _FringeScale) * _FringeScale; - - // Calculate outside offset for stroke position CenterPixelAligned - // so that one side the of the line is always at pixel boundary. - // On integer thickness both sides of the line are on pixel boundary. - const float screen_thickness = thickness / _FringeScale; - const int s_thickness = (int)screen_thickness; - return ((float)(s_thickness / 2) + (s_thickness & 1) * (screen_thickness - s_thickness)) * _FringeScale; -} - extern bool g_LEGACY_STROKES; // We intently don't turn g_LEGACY_STROKES into ImDrawFlags_StrokeLegacy here. @@ -2061,7 +2058,8 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th if ((col & IM_COL32_A_MASK) == 0) return; - if (g_LEGACY_STROKES) + ImDrawFlags stroke_pos = _GetStrokePos(flags, ImDrawFlags_StrokeCenter); + if (g_LEGACY_STROKES || stroke_pos == ImDrawFlags_StrokeLegacy) { const ImVec2 points[2] = { ImVec2(p1.x + 0.5f, p1.y + 0.5f), ImVec2(p2.x + 0.5f, p2.y + 0.5f) }; AddPolyline(points, 2, col, thickness, ImDrawFlags_StrokeLegacy); @@ -2081,26 +2079,33 @@ void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float th if (g_LEGACY_STROKES || stroke_pos == ImDrawFlags_StrokeLegacy) { const ImVec2 points[2] = { ImVec2(min_x + 0.5f, y + 0.5f), ImVec2(max_x + 0.5f, y + 0.5f) }; // Same as AddLine() above. - AddPolyline(points, 2, col, thickness, ImDrawFlags_StrokeLegacy); + AddPolyline(points, 2, col, thickness, stroke_pos); return; } + float top_y = y; // Inside if (stroke_pos == ImDrawFlags_StrokeCenter) - y -= thickness * 0.5f; + top_y -= thickness * 0.5f; else if (stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) - y -= CalculateCenterPixelAlignedOffset(thickness, _FringeScale); + top_y -= CalculateCenterPixelAlignedOffset(thickness, _FringeScale); else if (stroke_pos == ImDrawFlags_StrokeOutside) - y -= thickness; + top_y -= thickness; - if (_FringeScaleIsInteger && ImIsTruncated4(min_x, max_x, y, thickness)) + if (_FringeScaleIsInteger && ImIsTruncated4(min_x, max_x, top_y, thickness) && (flags & ImDrawFlags_SquareCap) == 0) { + // For pixel aligned case, use simple rectangle. PrimReserve(6, 4); - PrimRect(ImVec2(min_x, y), ImVec2(max_x, y + thickness), col); + PrimRect(ImVec2(min_x, top_y), ImVec2(max_x, top_y + thickness), col); } else { - // FIXME-POLYLINE: Implement square caps - AddRectFilled(ImVec2(min_x, y), ImVec2(max_x, y + thickness), col); + // Replace stroke pos with the already calculated one. + flags = (flags & ~ImDrawFlags_StrokeMask_) | stroke_pos; + + // For generic case use line, since it needs less triangles than AA rectangle. + PathLineTo(ImVec2(min_x, y)); + PathLineTo(ImVec2(max_x, y)); + PathStroke(col, thickness, flags); } } @@ -2113,26 +2118,34 @@ void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float th if (g_LEGACY_STROKES || stroke_pos == ImDrawFlags_StrokeLegacy) { const ImVec2 points[2] = { ImVec2(x + 0.5f, max_y + 0.5f), ImVec2(x + 0.5f, min_y + 0.5f) }; // Same as AddLine() above. - AddPolyline(points, 2, col, thickness, ImDrawFlags_StrokeLegacy); + AddPolyline(points, 2, col, thickness, stroke_pos); return; } + float left_x = x; // Inside if (stroke_pos == ImDrawFlags_StrokeCenter) - x -= thickness * 0.5f; + left_x -= thickness * 0.5f; else if (stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) - x -= CalculateCenterPixelAlignedOffset(thickness, _FringeScale); + left_x -= CalculateCenterPixelAlignedOffset(thickness, _FringeScale); else if (stroke_pos == ImDrawFlags_StrokeOutside) - x -= thickness; + left_x -= thickness; - if (_FringeScaleIsInteger && ImIsTruncated4(x, min_y, max_y, thickness)) + if (_FringeScaleIsInteger && ImIsTruncated4(left_x, min_y, max_y, thickness) && (flags & ImDrawFlags_SquareCap) == 0) { + // For pixel aligned case, use simple rectangle. PrimReserve(6, 4); - PrimRect(ImVec2(x, min_y), ImVec2(x + thickness, max_y), col); + PrimRect(ImVec2(left_x, min_y), ImVec2(left_x + thickness, max_y), col); } else { - // FIXME-POLYLINE: Implement square caps - AddRectFilled(ImVec2(x, min_y), ImVec2(x + thickness, max_y), col); + // Replace stroke pos with the already calculated one. + flags = (flags & ~ImDrawFlags_StrokeMask_) | stroke_pos; + + // For generic case use line, since it needs less triangles than AA rectangle. + // Drawing for bottom to top, so that the inside stroke pos expands to right. + PathLineTo(ImVec2(x, max_y)); + PathLineTo(ImVec2(x, min_y)); + PathStroke(col, thickness, flags); } } @@ -2374,49 +2387,37 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl if ((col & IM_COL32_A_MASK) == 0) return; - if (g_LEGACY_STROKES) + ImDrawFlags stroke_pos = _GetStrokePos(flags, ImDrawFlags_StrokeInside); + + if (g_LEGACY_STROKES || stroke_pos == ImDrawFlags_StrokeLegacy) { if (Flags & ImDrawListFlags_AntiAliasedLines) PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags); else PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes. - PathStroke(col, thickness, ImDrawFlags_Closed); + PathStroke(col, thickness, ImDrawFlags_Closed | ImDrawFlags_MiterOnly | ImDrawFlags_StrokeCenter); return; } - ImDrawFlags stroke_pos = _GetStrokePos(flags, ImDrawFlags_StrokeInside); -#if 0 // shiny_polyline - // FIXME-POLYLINE - const bool has_rounding = (rounding >= _FringeScale); + const bool has_rounding = (rounding >= _FringeScale * 0.5f); + // Calculate the outer boundary of the rectangle based on the stroke position. ImVec2 outer_min = p_min; ImVec2 outer_max = p_max; float outer_rounding = rounding; - if (stroke_pos == ImDrawFlags_StrokeLegacy) - { - outer_min += ImVec2(0.50f, 0.50f); - if (Flags & ImDrawListFlags_AntiAliasedLines) - outer_max -= ImVec2(0.50f, 0.50f); - else - outer_max -= ImVec2(0.49f, 0.49f); // Better looking lower-right corner and rounded non-AA shapes. - stroke_pos = ImDrawFlags_StrokeCenter; - } - + float offset = 0.0f; // Inside if (stroke_pos == ImDrawFlags_StrokeCenter) - { - const ImVec2 offset(thickness * 0.5f, thickness * 0.5f); - outer_min -= offset; - outer_max += offset; - outer_rounding += has_rounding ? thickness * 0.5f : 0.0f; - } + offset = thickness * 0.5f; + else if (stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) + offset = CalculateCenterPixelAlignedOffset(thickness, _FringeScale); else if (stroke_pos == ImDrawFlags_StrokeOutside) - { - const ImVec2 offset(thickness, thickness); - outer_min -= offset; - outer_max += offset; - outer_rounding += has_rounding ? thickness : 0.0f; - } + offset = thickness; + outer_min.x -= offset; + outer_min.y -= offset; + outer_max.x += offset; + outer_max.y += offset; + outer_rounding += has_rounding ? offset : 0.0f; const float width = outer_max.x - outer_min.x; const float height = outer_max.y - outer_min.y; @@ -2429,6 +2430,61 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl if ((flags & ImDrawFlags_RoundCornersMask_) == 0) flags |= ImDrawFlags_RoundCornersAll; + if (has_rounding) + { + // Constrain rounding to rect dimensions + outer_rounding = ImMin(outer_rounding, ImFabs(width) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f); + outer_rounding = ImMin(outer_rounding, ImFabs(height) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f); + } + + if (_FringeScaleIsInteger && ImIsTruncated4(outer_min.x, outer_min.y, outer_max.x, outer_max.y) && ImIsTruncated4(outer_rounding, thickness, 0.0f, 0.0f)) + { + int s_rounding = (int)(outer_rounding / _FringeScale); + int s_thickness = (int)(thickness / _FringeScale); + + if (s_rounding <= 0 || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) + { + // Pixel aligned non-rounded rect. + const ImVec2 opaque_uv = _Data->TexUvWhitePixel; + const float t = (float)s_thickness * _FringeScale; + + PrimReserve(8*3, 8); + const ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; + IM_APPEND_VTX(outer_min.x, outer_min.y, opaque_uv, col); + IM_APPEND_VTX(outer_min.x + t, outer_min.y + t, opaque_uv, col); + IM_APPEND_VTX(outer_max.x, outer_min.y, opaque_uv, col); + IM_APPEND_VTX(outer_max.x - t, outer_min.y + t, opaque_uv, col); + IM_APPEND_VTX(outer_max.x, outer_max.y, opaque_uv, col); + IM_APPEND_VTX(outer_max.x - t, outer_max.y - t, opaque_uv, col); + IM_APPEND_VTX(outer_min.x, outer_max.y, opaque_uv, col); + IM_APPEND_VTX(outer_min.x + t, outer_max.y - t, opaque_uv, col); + + IM_APPEND_TRI(idx + 0, idx + 3, idx + 1); + IM_APPEND_TRI(idx + 0, idx + 2, idx + 3); + + IM_APPEND_TRI(idx + 2, idx + 5, idx + 3); + IM_APPEND_TRI(idx + 2, idx + 4, idx + 5); + + IM_APPEND_TRI(idx + 4, idx + 7, idx + 5); + IM_APPEND_TRI(idx + 4, idx + 6, idx + 7); + + IM_APPEND_TRI(idx + 6, idx + 1, idx + 7); + IM_APPEND_TRI(idx + 6, idx + 0, idx + 1); + + return; + } + if ((Flags & ImDrawListFlags_RoundCornersUseTex) && s_thickness <= IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX && s_rounding <= IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX) + { + // Pixel aligned rect with round corners rendered using baked textures. + IM_ASSERT_PARANOID(!(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)); + const int size = ImMax(s_rounding, s_thickness); // This is matching the baking calculations. + const int idx = (s_thickness - 1) * IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX + s_rounding - 1; + IM_ASSERT_PARANOID(idx >= 0 && idx < IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX * IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX); + const ImVec4 tex_uvs = _Data->TexUvCornerStrokes[idx]; + _AddRectBaked(outer_min, outer_max, col, (float)size * _FringeScale, (float)thickness, tex_uvs, flags); + return; + } + } if (!has_rounding || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { @@ -2439,10 +2495,6 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl } else { - // Constrain rounding to rect dimensions - outer_rounding = ImMin(outer_rounding, ImFabs(width) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f); - outer_rounding = ImMin(outer_rounding, ImFabs(height) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f); - if (thickness > outer_rounding) { // Special case rendering to avoid rendering artifacts at the corners. @@ -2461,111 +2513,6 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl } PathStroke(col, thickness, ImDrawFlags_Closed | ImDrawFlags_MiterOnly | ImDrawFlags_StrokeInside); -#else - // textured_round_corners - const bool is_truncated = _FringeScaleIsInteger && ImIsTruncated4(p_min.x, p_min.y, p_max.x, p_max.y) && ImIsTruncated4(rounding, thickness, 0.0f, 0.0f); - if ((stroke_pos == ImDrawFlags_StrokeInside || stroke_pos == ImDrawFlags_StrokeOutside || stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) && is_truncated) - { - if ((flags & ImDrawFlags_RoundCornersMask_) == 0) - flags |= ImDrawFlags_RoundCornersAll; - - rounding = ImMin(rounding, ImFabs(p_max.x - p_min.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f); - 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); - - int s_rounding = (int)(rounding / _FringeScale); - int s_thickness = (int)(thickness / _FringeScale); - - // AddRectBaked() renders the stroke inside the rectangle, so we need to adjust the outside case - // This branch does not handle the center, since odd thickness strokes are not pixel aligned, even if the rectangle is. - // Pixel aligned center works, since the stroke is aligned to pixels. - ImVec2 s_min = p_min; - ImVec2 s_max = p_max; - if (stroke_pos == ImDrawFlags_StrokeOutside) - { - s_min.x -= thickness; - s_min.y -= thickness; - s_max.x += thickness; - s_max.y += thickness; - if (s_rounding != 0) - s_rounding += s_thickness; - } - else if (stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) - { - const float offset = (float)(s_thickness / 2) * _FringeScale; // Using integer div, since we want IM_TRUNC(thickness/2). - s_min.x -= offset; - s_min.y -= offset; - s_max.x += offset; - s_max.y += offset; - if (s_rounding != 0) - s_rounding += s_thickness / 2; - } - - if (s_rounding <= 0 || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) - { - const ImVec2 opaque_uv = _Data->TexUvWhitePixel; - const float t = (float)s_thickness * _FringeScale; - - PrimReserve(8*3, 8); - const ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; - IM_APPEND_VTX(s_min.x, s_min.y, opaque_uv, col); - IM_APPEND_VTX(s_min.x + t, s_min.y + t, opaque_uv, col); - IM_APPEND_VTX(s_max.x, s_min.y, opaque_uv, col); - IM_APPEND_VTX(s_max.x - t, s_min.y + t, opaque_uv, col); - IM_APPEND_VTX(s_max.x, s_max.y, opaque_uv, col); - IM_APPEND_VTX(s_max.x - t, s_max.y - t, opaque_uv, col); - IM_APPEND_VTX(s_min.x, s_max.y, opaque_uv, col); - IM_APPEND_VTX(s_min.x + t, s_max.y - t, opaque_uv, col); - - IM_APPEND_TRI(idx + 0, idx + 3, idx + 1); - IM_APPEND_TRI(idx + 0, idx + 2, idx + 3); - - IM_APPEND_TRI(idx + 2, idx + 5, idx + 3); - IM_APPEND_TRI(idx + 2, idx + 4, idx + 5); - - IM_APPEND_TRI(idx + 4, idx + 7, idx + 5); - IM_APPEND_TRI(idx + 4, idx + 6, idx + 7); - - IM_APPEND_TRI(idx + 6, idx + 1, idx + 7); - IM_APPEND_TRI(idx + 6, idx + 0, idx + 1); - - return; - } - if ((Flags & ImDrawListFlags_RoundCornersUseTex) && s_thickness <= IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX && s_rounding <= IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX) - { - IM_ASSERT_PARANOID(!(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)); - const int size = ImMax(s_rounding, s_thickness); // This is matching the baking calculations. - const int idx = (s_thickness - 1) * IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX + s_rounding - 1; - IM_ASSERT_PARANOID(idx >= 0 && idx < IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX * IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX); - const ImVec4 tex_uvs = _Data->TexUvCornerStrokes[idx]; - _AddRectBaked(s_min, s_max, col, (float)size * _FringeScale, (float)thickness, tex_uvs, flags); - return; - } - } - - const ImVec2 offset(thickness * 0.5f, thickness * 0.5f); - if (stroke_pos == ImDrawFlags_StrokeInside) - { - rounding -= (rounding != 0.0f) ? thickness * 0.5f : 0.0f; - PathRect(p_min + offset, p_max - offset, rounding, flags); - } - else if (stroke_pos == ImDrawFlags_StrokeCenter) - { - PathRect(p_min, p_max, rounding, flags); - } - else if (stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) - { - // PathRect() centers the stroke, just calculate the fractional offset. - const float fract = (thickness * 0.5f) - CalculateCenterPixelAlignedOffset(thickness, _FringeScale); - rounding += (rounding != 0.0f) ? fract : 0.0f; - PathRect(ImVec2(p_min.x + fract, p_min.y + fract), ImVec2(p_max.x - fract, p_max.y - fract), rounding, flags); - } - else if (stroke_pos == ImDrawFlags_StrokeOutside) - { - rounding += (rounding != 0.0f) ? thickness * 0.5f : 0.0f; - PathRect(p_min - offset, p_max + offset, rounding, flags); - } - PathStroke(col, thickness, ImDrawFlags_Closed); -#endif } void ImDrawList::_AddRectFilledBaked(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float r, ImVec4 tex_uvs, ImDrawFlags flags) @@ -2734,10 +2681,11 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu stroke_pos = ImDrawFlags_StrokeCenter; } - // FIXME-POLYLINE: missing ImDrawFlags_StrokeCenterPixelAligned. float outer_radius = radius; if (stroke_pos == ImDrawFlags_StrokeCenter) outer_radius = radius + thickness * 0.5f; + else if (stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) + outer_radius = radius + CalculateCenterPixelAlignedOffset(thickness, _FringeScale); else if (stroke_pos == ImDrawFlags_StrokeInside) outer_radius = radius; else if (stroke_pos == ImDrawFlags_StrokeOutside) @@ -2752,10 +2700,8 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu if (num_segments <= 0) { // Use arc with automatic segment count - // FIXME-POLYLINE: Review - //const int a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(radius); // Radius used here intentionally so that the circle matches filled circle of same radius. - //_PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, a_step); - _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); + const int a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(outer_radius); // Use outer_radius for segment count to be consistent with rounded rect. + _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, a_step); _Path.Size--; } else @@ -2823,6 +2769,8 @@ void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_ float outer_radius = radius; if (stroke_pos == ImDrawFlags_StrokeCenter) outer_radius = radius + miter_thickness * 0.5f; + else if (stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) + outer_radius = radius + CalculateCenterPixelAlignedOffset(miter_thickness, _FringeScale); else if (stroke_pos == ImDrawFlags_StrokeInside) outer_radius = radius; else if (stroke_pos == ImDrawFlags_StrokeOutside) @@ -2867,41 +2815,12 @@ void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 co if ((col & IM_COL32_A_MASK) == 0) return; - // Note: since offset ellipse is not ellipse anymore, we cannot fall back to filled ellipse when outline covers the whole shape like in other shapes. - // FIXME-POLYLINE - /* - ImVec2 r; - if (g_LEGACY_STROKES) + ImDrawFlags stroke_pos = _GetStrokePos(flags, ImDrawFlags_StrokeInside); + + if (g_LEGACY_STROKES || stroke_pos == ImDrawFlags_StrokeLegacy) { - r = radius; + stroke_pos = ImDrawFlags_StrokeCenter; } - else - { - r = radius; - ImDrawFlags stroke_pos = _GetStrokePos(flags, ImDrawFlags_StrokeInside); - if (stroke_pos == ImDrawFlags_StrokeInside) - r -= ImVec2(thickness * 0.5f, thickness * 0.5f); - else if (stroke_pos == ImDrawFlags_StrokeCenterPixelAligned) - { - const float fract = thickness * 0.5f - CalculateCenterPixelAlignedOffset(thickness, _FringeScale); - r -= ImVec2(fract, fract); - } - else if (stroke_pos == ImDrawFlags_StrokeOutside) - r += ImVec2(thickness * 0.5f, thickness * 0.5f); - } - */ - /* - float stroke_offset = 0.0f; - if (stroke_pos == ImDrawFlags_StrokeInside) - stroke_offset = -thickness * 0.5f; - else if (stroke_pos == ImDrawFlags_StrokeOutside) - stroke_offset = thickness * 0.5f; - if ((ImMin(radius.x, radius.y) + stroke_offset) < (thickness * 0.5f + _FringeScale * 0.5f)) - { - AddEllipseFilled(center, radius, col, rot, num_segments); - return; - } - */ if (num_segments <= 0) num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here. @@ -2911,7 +2830,7 @@ void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 co // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments; PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1); - PathStroke(col, thickness, ImDrawFlags_Closed | ImDrawFlags_MiterOnly | flags); + PathStroke(col, thickness, ImDrawFlags_Closed | ImDrawFlags_MiterOnly | stroke_pos); } void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments)