lukehoban / lukehoban/browser

Performance: FillRect has redundant bounds checking per pixel

Open
#74 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

performance
Dominant language
Go
Stars
1
Forks
2
PR merge metrics
No merged PRs in 30d

Description

## Description

`FillRect()` calls `SetPixel()` for each pixel, which performs bounds checking on every call.

## Location

- `render/render.go:116-122` - `FillRect()`
- `render/render.go:108-111` - `SetPixel()`

## Problem

```go
func (c *Canvas) FillRect(x, y, width, height int, col color.RGBA) {
for dy := 0; dy < height; dy++ {
for dx := 0; dx < width; dx++ {
c.SetPixel(x+dx, y+dy, col) // Bounds check every iteration
}
}
}

func (c *Canvas) SetPixel(x, y int, col color.RGBA) {
if x >= 0 && x < c.Width && y >= 0 && y < c.Height { // Redundant
c.Pixels[y*c.Width+x] = col
}
}
```

For a 100x100 rectangle, this performs 10,000 redundant bounds checks.

## Suggested Improvements

1. **Clip rectangle bounds once** before the loop, then write directly to `c.Pixels`
2. **Use row-based filling** - Clip Y bounds once, fill entire rows with direct slice operations
3. **Consider `copy()` for horizontal fills** - Pre-fill a row template and copy it

Example optimized version:
```go
func (c *Canvas) FillRect(x, y, width, height int, col color.RGBA) {
// Clip bounds once
x1, y1 := max(0, x), max(0, y)
x2, y2 := min(c.Width, x+width), min(c.Height, y+height)

for py := y1; py < y2; py++ {
offset := py * c.Width
for px := x1; px < x2; px++ {
c.Pixels[offset+px] = col
}
}
}
```

## Impact

FillRect is called for every background, border, and many drawing operations. This optimization would benefit the entire rendering pipeline.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with render/render.go:116-122 and compare FillRect with SetPixel at lines 108-111. Clip the rectangle once and write rows directly to c.Pixels, preserving behavior for rectangles that extend beyond the canvas. Done means FillRect no longer calls SetPixel for every pixel while producing the same clipped result.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
computer-graphics, performance
Issue type
Refactor
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.