From 8b63c2a10e4f4eeda1d0c2afdc8c825a752d816b Mon Sep 17 00:00:00 2001 From: Tobias Christiansen Date: Sun, 16 May 2021 16:14:33 +0200 Subject: LibGfx: Add Painter::draw_circle_arc_intersecting() This adds a function to draw a circle specified by a center point ( relative to the given Rect) and a radius. The circle arc is only drawn inside the specified Rect as to allow for circle arc segments. Technically this was already possible using draw_elliptical_arc(), but the algorithm is quite involved and lead to wonky arcs when trying to draw circle arc segments. --- Userland/Libraries/LibGfx/Painter.cpp | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'Userland/Libraries/LibGfx/Painter.cpp') diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index 9a01187dd3..368e7f0370 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -384,6 +384,49 @@ void Painter::fill_rounded_corner(const IntRect& a_rect, int radius, Color color } } +void Painter::draw_circle_arc_intersecting(const IntRect& a_rect, const IntPoint& center, int radius, Color color, int thickness) +{ + if (thickness <= 0) + return; + + // Care about clipping + auto translated_a_rect = a_rect.translated(translation()); + auto rect = translated_a_rect.intersected(clip_rect()); + + if (rect.is_empty()) + return; + VERIFY(m_target->rect().contains(rect)); + + // We got cut on the top! + // FIXME: Also account for clipping on the x-axis + int clip_offset = 0; + if (translated_a_rect.y() < rect.y()) + clip_offset = rect.y() - translated_a_rect.y(); + + if (thickness > radius) + thickness = radius; + + int radius2 = radius * radius; + auto is_on_arc = [&](int x, int y) { + int distance2 = (center.x() - x) * (center.x() - x) + (center.y() - y) * (center.y() - y); + // Is within a circle of radius 1/2 around (x,y), so basically within the current pixel. + // Technically this is angle-dependent and should be between 1/2 and sqrt(2)/2, but this works. + return distance2 <= (radius2 + radius + 0.25) && distance2 >= (radius2 - radius + 0.25); + }; + + RGBA32* dst = m_target->scanline(rect.top()) + rect.left(); + const size_t dst_skip = m_target->pitch() / sizeof(RGBA32); + + for (int i = rect.height() - 1; i >= 0; --i) { + for (int j = 0; j < rect.width(); ++j) + if (is_on_arc(j, rect.height() - i + clip_offset)) + dst[j] = color.value(); + dst += dst_skip; + } + + return draw_circle_arc_intersecting(a_rect, center, radius - 1, color, thickness - 1); +} + void Painter::fill_ellipse(const IntRect& a_rect, Color color) { VERIFY(scale() == 1); // FIXME: Add scaling support. -- cgit v1.2.3