Skip to content

perf: vectorize image and bitplane encoders#93

Merged
g4bri3lDev merged 3 commits into
OpenDisplay:mainfrom
balloob:perf/vectorize-encoders
Jul 5, 2026
Merged

perf: vectorize image and bitplane encoders#93
g4bri3lDev merged 3 commits into
OpenDisplay:mainfrom
balloob:perf/vectorize-encoders

Conversation

@balloob

@balloob balloob commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the per-pixel Python loops in all image/bitplane encoders with vectorized numpy operations (np.packbits + LUTs). Profiling showed these loops were 60–73% of prepare_image() CPU.

Affected functions:

  • encode_1bpp, encode_2bpp, encode_4bpp (encoding/images.py)
  • encode_bitplanes, encode_gray4_bitplanes (encoding/bitplanes.py)

Measured speedups (median, current → vectorized @800×480):

Function Speedup
encode_1bpp ~1066× (45.1 ms → 42 µs)
encode_2bpp ~370×
encode_4bpp (grayscale16) ~378×
encode_4bpp (bwgbry) ~117×
encode_bitplanes ~790×
encode_gray4_bitplanes ~136×

Full prepare_image pipeline ends up ~2.3–2.9× faster overall; afterwards the Rust dither step dominates, which is where the time should be.

Correctness

The vectorized versions were verified byte-identical to the original loop implementations across widths not divisible by 8/4/2 (122, 250, 799, 127, 1), several heights, and the full 0–255 palette-index range — including the BWGBRY value map {0,1,2,3,5,6} (with its .get(…, 0) default preserved) and multiple gray4 code tables. packbits(axis=1) reproduces the existing per-row byte-boundary zero-padding exactly.

Test plan

  • uv run pytest -q → 439 passed
  • Standalone byte-identical diff against the pre-change loop implementations (8 sizes × all encoders)
  • ruff check, ruff format, mypy src/ clean

🤖 Generated with Claude Code

Replace the per-pixel Python loops in encode_1bpp/encode_2bpp/encode_4bpp
and encode_bitplanes/encode_gray4_bitplanes with numpy packbits/LUT
operations. These loops were 60-73% of prepare_image() CPU; the vectorized
versions are ~100-1000x faster (e.g. encode_1bpp @800x480: 45ms -> 42us)
and were verified byte-identical to the originals across widths not divisible
by 8/4/2 (122, 250, 799, 127, 1) and the full 0-255 palette-index range.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrm95f1qNZzDM9r2SB6KW
@balloob balloob requested a review from g4bri3lDev as a code owner July 4, 2026 06:32
@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.41935% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/opendisplay/encoding/bitplanes.py 55.55% 4 Missing ⚠️
src/opendisplay/encoding/images.py 86.36% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@g4bri3lDev g4bri3lDev merged commit f9a39fe into OpenDisplay:main Jul 5, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants