Browse Source

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.
Tobias Christiansen 4 years ago
parent
commit
8b63c2a10e
2 changed files with 44 additions and 0 deletions
  1. 43 0
      Userland/Libraries/LibGfx/Painter.cpp
  2. 1 0
      Userland/Libraries/LibGfx/Painter.h

+ 43 - 0
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.

+ 1 - 0
Userland/Libraries/LibGfx/Painter.h

@@ -71,6 +71,7 @@ public:
     void draw_glyph(const IntPoint&, u32, const Font&, Color);
     void draw_emoji(const IntPoint&, const Gfx::Bitmap&, const Font&);
     void draw_glyph_or_emoji(const IntPoint&, u32 code_point, const Font&, Color);
+    void draw_circle_arc_intersecting(const IntRect&, const IntPoint&, int radius, Color, int thickness);
 
     enum class CornerOrientation {
         TopLeft,