Bresenham algorithm
line drawing
computer graphics
arbitrary thickness
programming techniques

how do I create a line of arbitrary thickness using Bresenham?

Master System Design with Codemia

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

Introduction

Classic Bresenham draws a one-pixel centerline. To get arbitrary thickness, you need a second rule that fills pixels around that centerline. If you simply duplicate pixels blindly in x or y, the line usually looks uneven, especially on diagonals.

The practical approach is to keep Bresenham for the centerline and then stamp a small brush around each centerline pixel. That preserves the efficient integer stepping while producing a thicker visible stroke.

Why Naive Thickening Looks Wrong

If you always expand only vertically, shallow lines look reasonable but steep lines become distorted. If you always expand only horizontally, the opposite happens.

The reason is that perceived line width is perpendicular to the line direction. So a thick line needs some notion of width around the centerline, not just a fixed expansion on one axis.

A Practical Method

One simple and reliable strategy is:

  1. Use Bresenham to compute the single-pixel line path.
  2. For every pixel on that path, draw a small square or disk with radius based on the desired thickness.

Here is a runnable Python version:

python
1def bresenham(x0, y0, x1, y1):
2    points = []
3    dx = abs(x1 - x0)
4    dy = abs(y1 - y0)
5    sx = 1 if x0 < x1 else -1
6    sy = 1 if y0 < y1 else -1
7    err = dx - dy
8
9    while True:
10        points.append((x0, y0))
11        if x0 == x1 and y0 == y1:
12            break
13        e2 = 2 * err
14        if e2 > -dy:
15            err -= dy
16            x0 += sx
17        if e2 < dx:
18            err += dx
19            y0 += sy
20
21    return points
22
23
24def thick_line(x0, y0, x1, y1, thickness):
25    radius = thickness // 2
26    pixels = set()
27
28    for x, y in bresenham(x0, y0, x1, y1):
29        for ox in range(-radius, radius + 1):
30            for oy in range(-radius, radius + 1):
31                if ox * ox + oy * oy <= radius * radius:
32                    pixels.add((x + ox, y + oy))
33
34    return pixels
35
36
37print(sorted(thick_line(2, 2, 10, 6, 3))[:12])

This example uses a circular brush. If you want a blockier appearance, remove the circle test and fill the square directly.

Why This Works

Bresenham gives you a stable, gap-free centerline. The brush around each centerline pixel adds width. Because adjacent brush stamps overlap, the line becomes a continuous thick stroke rather than a sequence of isolated dots.

This is not mathematically identical to vector stroking, but it is often the best tradeoff when you want:

  • integer arithmetic,
  • simple implementation,
  • predictable raster behavior.

More Accurate Width Control

If you want the thickness to follow the line's perpendicular direction more closely, compute an approximate normal vector from the line direction and draw short spans across that normal at each Bresenham point.

Conceptually:

  • direction vector is (dx, dy)
  • perpendicular vector is (-dy, dx)
  • normalize or approximate it
  • offset around the centerline by half the thickness

That produces better-looking diagonals, but it is more complicated because rounding and overlap rules become important.

Common Pitfalls

  • Expanding pixels only in x or only in y regardless of slope.
  • Expecting arbitrary-thickness raster lines to look perfect without a width rule based on line direction.
  • Forgetting that even and odd thickness values center differently around the line.
  • Making the brush too sparse, which can leave holes on steep lines.
  • Using Bresenham when the real requirement is antialiased vector-quality stroking rather than integer rasterization.

Summary

  • Bresenham by itself gives a one-pixel centerline.
  • Arbitrary thickness requires a second rule that fills pixels around that centerline.
  • A practical implementation is to stamp a small disk or square at each Bresenham pixel.
  • More accurate thick lines use offsets based on the line's perpendicular direction.
  • The best method depends on whether you prioritize simplicity, speed, or visual precision.

Course illustration
Course illustration

All Rights Reserved.