Xaolin Wu
antialiased circle
digital drawing
computer graphics
algorithms

Drawing an antialiased circle as described by Xaolin Wu

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Drawing a smooth circle on a pixel grid is harder than drawing the ideal mathematical curve, because pixels are discrete and the circle edge usually passes between pixel centers. Xiaolin Wu style antialiasing addresses that by assigning intensity from fractional coverage instead of switching pixels fully on or fully off. For circles, the practical version is to compute the ideal curve position and blend neighboring pixels according to that fractional offset.

The Core Antialiasing Idea

A normal circle rasterizer might choose the nearest pixel and set it to full intensity. That creates jagged edges. An antialiased algorithm instead spreads intensity between neighboring pixels.

For one octant of the circle, you can:

  • step through x
  • compute the ideal y on the circle
  • split brightness between the two nearest vertical pixels
  • mirror the result into the other symmetric octants

That is the same conceptual move Wu used for lines: represent geometry continuously, then distribute brightness fractionally.

A Runnable Python Example

The following example draws a grayscale antialiased circle into a 2D array using octant symmetry and fractional intensity:

python
1import math
2
3
4def plot(img, x, y, brightness):
5    if 0 <= y < len(img) and 0 <= x < len(img[0]):
6        img[y][x] = min(255, img[y][x] + int(brightness * 255))
7
8
9def draw_wu_circle(width, height, cx, cy, radius):
10    img = [[0 for _ in range(width)] for _ in range(height)]
11
12    x = 0
13    while x <= radius / math.sqrt(2):
14        y_real = math.sqrt(radius * radius - x * x)
15        y_int = int(math.floor(y_real))
16        frac = y_real - y_int
17
18        upper = 1.0 - frac
19        lower = frac
20
21        points = [
22            (cx + x, cy + y_int, upper),
23            (cx + x, cy + y_int + 1, lower),
24            (cx - x, cy + y_int, upper),
25            (cx - x, cy + y_int + 1, lower),
26            (cx + x, cy - y_int, upper),
27            (cx + x, cy - y_int - 1, lower),
28            (cx - x, cy - y_int, upper),
29            (cx - x, cy - y_int - 1, lower),
30            (cx + y_int, cy + x, upper),
31            (cx + y_int + 1, cy + x, lower),
32            (cx - y_int, cy + x, upper),
33            (cx - y_int - 1, cy + x, lower),
34            (cx + y_int, cy - x, upper),
35            (cx + y_int + 1, cy - x, lower),
36            (cx - y_int, cy - x, upper),
37            (cx - y_int - 1, cy - x, lower),
38        ]
39
40        for px, py, brightness in points:
41            plot(img, px, py, brightness)
42
43        x += 1
44
45    return img
46
47
48if __name__ == "__main__":
49    image = draw_wu_circle(40, 20, 20, 10, 8)
50    for row in image:
51        line = ''.join('#' if value > 180 else '+' if value > 80 else '.' if value > 0 else ' ' for value in row)
52        print(line)

This is not a production renderer, but it does capture the essential idea clearly: compute the ideal arc continuously, then distribute brightness to the nearest discrete pixels.

Why Symmetry Matters

A circle has eight-way symmetry. That means you only need to compute points for one octant and mirror them. The antialiasing weights travel with the mirrored points.

Using symmetry reduces work and keeps the rasterization consistent. Without it, you would compute the same geometry repeatedly.

Fractional Brightness Is the Important Part

The algorithm hinges on the fractional part of the true y position. If the ideal curve passes mostly through one pixel row, that row gets more intensity. The adjacent row gets the remainder. This is what softens the edge and removes the staircase look.

That means the visual quality comes from coverage approximation, not from drawing more geometry.

This Is an Approximation, Not Physical Coverage Integration

A full area-coverage renderer could be even more accurate, but Wu-style antialiasing is attractive because it gives a very good visual result with modest computation. That is why it remains a useful teaching algorithm in raster graphics.

Common Pitfalls

  • Turning on only the nearest pixel and losing the whole point of antialiasing.
  • Forgetting to mirror points across all symmetric octants.
  • Letting brightness values accumulate above the valid range.
  • Assuming the circle equation alone gives a smooth raster without intensity blending.
  • Confusing exact area coverage with the cheaper fractional approximation used here.

Summary

  • Wu-style circle antialiasing distributes brightness based on fractional coverage.
  • You compute the ideal circle position continuously and rasterize it with weighted neighboring pixels.
  • Circle symmetry lets you draw one octant and mirror the result.
  • The key visual improvement comes from fractional intensity, not from more pixels.
  • A small approximation can produce a much smoother circle on a discrete grid.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.