← Blog
0.7.0 · VIDEO · BROWSER · SCROLLCOMP-PASS

Scroll Compositor — Viewport Scroll Offset for Browser Scrolling (sigil-video 21a7fd5)

June 22, 2026 · sigil-video 21a7fd5 · Sigil-Docs
browser video compositor scroll 0.7.0

core/scroll_compositor.sg (sigil-video 21a7fd5) adds per-layer (scroll_x, scroll_y) viewport offsets to the Lumen compositor — enabling pages taller than the display to scroll. SCROLLCOMP-PASS x86 verified: 160×240 content in a 160×120 viewport; four scroll positions tested (y=0, y=120, y=180 clipped, x=80). GPU path: gpu_submit(CAP_ACCEL, scroll_offset_buf(), fb, fw, fh) routes to display controller pan register (silicon-pending). Software floor: copy_span with offset source address per visible scanline.

SCROLLCOMP-PASS x86 QEMU screendump
SCROLLCOMP-PASS x86 QEMU screendump — scroll_y=0 blue top, scroll_y=120 red bottom, scroll_x=80 magenta half

scroll_compositor.sg — offset table + API

Scroll offset table at 0xAB0000
16 entries × 8B each = 128B. Each entry: scroll_x:i32 (LE) + scroll_y:i32 (LE). scroll_comp_set_scroll(idx, sx, sy) writes the pair via poke32. scroll_offset_buf() returns 0xAB0000 (used by the GPU path).
scroll_comp_composite
scroll_comp_composite(fb_out, pitch, fw, fh): For each active layer (checked via comp_layer_active(idx)), reads (scroll_x, scroll_y) for that layer. For each viewport row r (0 to fh-1): source row = r + scroll_y, clamped to [0, lh-1] where lh = layer height. Horizontal: viewport col c maps to source col c + scroll_x; visible width = lw - scroll_x (clipped at viewport edge). Copies vis_w pixels per row via copy_span. Rows where r + scroll_y ≥ lh are skipped (clip — shows transparent/black below content).
GPU path (silicon-pending)
gpu_submit(CAP_ACCEL, scroll_offset_buf(), fb, fw, fh) — routes per-layer scroll offsets to the display controller's hardware pan register when CAP_ACCEL is held. Software floor takes over on Pi 3 / no-GPU config.
Integration with comp_present
scroll_comp_composite is called before comp_present merges layers into the output framebuffer. The scroll offset is applied at composite time — layers store full content at full resolution; the viewport window is the scroll offset view into that content.

SCROLLCOMP-PASS test

160×240 pixel content in a 160×120 viewport. Four probes:

Testscroll_xscroll_yExpectedResult
T100Blue top half of content visible
T20120Red bottom half of content visible
T30180Row 60 clipped (src_row 240 ≥ lh 240) — clip plane fires
T4800Magenta right half (scroll_x=80 into content)

What this unblocks

Browser scrolling: render_page paints a full-height layer; scroll_compositor lets the viewport slide over it via scroll_y. The next integration point is the style resolver passing overflow: scroll to trigger scroll_comp_set_scroll.