algorithms
computer graphics
circle drawing
optimization
programming

fast algorithm for drawing filled circles?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A fast filled-circle algorithm usually does not “paint every point inside the radius” with a naive distance test. The common optimization is to compute the circle boundary incrementally and then fill horizontal spans between symmetric points. That avoids expensive square roots and reduces overdraw, which is exactly what raster graphics code wants.

The Naive Method and Why It Is Slow

A straightforward approach loops over a bounding square and checks whether each pixel lies inside the circle.

python
1def draw_filled_circle_naive(cx, cy, r, put_pixel):
2    for y in range(cy - r, cy + r + 1):
3        for x in range(cx - r, cx + r + 1):
4            dx = x - cx
5            dy = y - cy
6            if dx * dx + dy * dy <= r * r:
7                put_pixel(x, y)

This works, but it checks every pixel in the square around the circle, including many pixels that will be rejected. Its time cost is proportional to the area of the bounding box.

For small circles that may be fine. For real-time drawing, it is usually wasteful.

Use Symmetry and Draw Spans

A much better approach is:

  1. compute one octant of the circle boundary with integer math
  2. use symmetry to find the matching points
  3. draw horizontal spans between left and right boundary points

This turns the fill problem into a set of scan lines instead of a flood fill or per-pixel distance test.

The midpoint circle algorithm is a classic choice for computing the boundary efficiently.

Midpoint Circle Plus Span Fill

Here is a practical implementation in Python:

python
1def draw_hline(x1, x2, y, put_pixel):
2    for x in range(x1, x2 + 1):
3        put_pixel(x, y)
4
5
6def draw_filled_circle(cx, cy, r, put_pixel):
7    x = 0
8    y = r
9    d = 1 - r
10
11    while x <= y:
12        draw_hline(cx - x, cx + x, cy + y, put_pixel)
13        draw_hline(cx - x, cx + x, cy - y, put_pixel)
14        draw_hline(cx - y, cx + y, cy + x, put_pixel)
15        draw_hline(cx - y, cx + y, cy - x, put_pixel)
16
17        x += 1
18        if d < 0:
19            d += 2 * x + 1
20        else:
21            y -= 1
22            d += 2 * (x - y) + 1

This algorithm uses only integer arithmetic and fills the circle a row at a time through symmetric spans.

That is usually the right general-purpose answer for software rasterization.

Why Horizontal Spans Are Better Than Flood Fill

Some developers outline a circle and then use flood fill to fill the inside. That works, but it is usually slower and more fragile than drawing known-valid spans directly.

Flood fill has downsides:

  • recursive implementations can blow the stack
  • iterative fills still touch many pixels repeatedly
  • boundary gaps can cause fill leakage
  • it solves a harder problem than necessary

If you already know the geometry is a circle, direct span filling is simpler and faster.

If You Can Afford Multiplication, Scanline Width Works Too

Another efficient software approach is to iterate over y and compute the horizontal half-width directly.

python
1import math
2
3
4def draw_filled_circle_scanline(cx, cy, r, put_pixel):
5    for dy in range(-r, r + 1):
6        dx = int(math.sqrt(r * r - dy * dy))
7        for x in range(cx - dx, cx + dx + 1):
8            put_pixel(x, cy + dy)

This version is easy to read, but it uses sqrt, which can be slower than the midpoint-style incremental integer method.

So the tradeoff is:

  • midpoint span fill: faster and integer-only
  • sqrt scanline fill: simpler to explain and often good enough

Performance Depends on Your Drawing Backend

If you are drawing into a CPU-side pixel buffer, integer span filling is a strong choice. If you are using a GPU-backed API, the fastest answer may be completely different, such as drawing a triangle fan, a shader-generated disc, or an antialiased primitive provided by the graphics API.

So “fastest” depends on context:

  • software rasterizer
  • immediate-mode UI toolkit
  • game engine
  • GPU rendering pipeline

Still, for classic algorithm questions and low-level pixel buffers, midpoint-plus-spans is the standard high-signal answer.

Antialiasing Is a Separate Problem

A filled circle can be fast and still look jagged. Smooth edges require antialiasing, multisampling, or alpha blending around the border.

Do not mix those concerns by accident. A fast filled-circle algorithm is about coverage and speed. A visually smooth circle is about edge treatment.

That usually means an additional rendering step or a different primitive.

Common Pitfalls

The biggest pitfall is using flood fill after drawing the boundary. It works, but it is usually solving the wrong subproblem.

Another issue is checking every pixel in the bounding box with a distance test when a scanline span method would do less work.

Developers also often forget that the best algorithm depends on whether rendering is CPU-based or GPU-based.

Finally, optimize the pixel-write path too. A good circle algorithm does not help much if put_pixel is extremely slow.

Summary

  • For software rendering, a fast filled-circle algorithm usually combines circle symmetry with horizontal span filling.
  • The midpoint circle algorithm avoids square roots and uses efficient integer arithmetic.
  • Drawing spans directly is usually better than outlining and then flood filling.
  • A sqrt-based scanline method is simpler but may be slower.
  • The truly fastest method depends on the rendering backend, especially when GPUs are involved.

Course illustration
Course illustration

All Rights Reserved.