mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-10 09:06:36 +00:00
feat(display): compensate for the panel's scan order while scrolling
A 1:N-scan HUB75 panel lights its rows in pairs, row d of the top half with row d of the bottom half, d running 0..N-1 across each refresh. The two rows either side of the middle of a panel are therefore lit at opposite ends of every refresh, and a strip moving a whole pixel per refresh shows a crisp 1px step across the middle of every panel -- in a phone video as well as by eye. Established on hdpi's panel (4x128x64, one chain, rotated 180) on 2026-09-24: interlaced scanning (scan_mode 1) made the step vanish, and halving the scroll speed halved it. It is the scan order, not a torn frame, and crisp vsync-locked pacing (#523, #628) makes it visible where uneven, blended motion used to hide it. Showing the upper half one refresh behind removed it completely at full speed. src/scan_order.py works out which rows lag how many refreshes from the layout: walking the logical rows, wherever a row lit near the start of a refresh follows one lit near the end, the section below takes one more refresh of lag (one less the other way), so the result is a uniform lean rather than a step. Stacked parallel chains lean further. It covers plain and parallel chains at 0 or 180 degrees with standard multiplexing and progressive scan; anything else (U-mapper, 90/270, multiplexing, interlaced, double-sided) is left alone, as is the emulator, which has no scan order. DisplayManager applies it only mid-scroll at one frame per refresh, when consecutive frames are consecutive refreshes: lagging rows come from the previous input frames, so it works for Vegas and every plugin ticker without knowing how they scroll. display.scan_order_compensation ("auto" | "off") controls it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -105,6 +105,7 @@ logical image to multiple chained physical panels.
|
||||
| `display_durations` | object, `{}` | Per-plugin display duration in seconds, keyed by plugin id (e.g. `"clock": 15`) | `DisplayController._get_display_duration()` (`src/display_controller.py`) |
|
||||
| `plugin_rotation_order` | array, `[]` | Explicit rotation order of plugin ids; empty = all enabled plugins in discovery order | `DisplayController._apply_plugin_rotation_order()` (`src/display_controller.py`) |
|
||||
| `use_short_date_format` | bool, `true` | Compact date rendering in sports scoreboards | Nothing since `src/base_classes` was removed; scoreboards read `display.use_short_date_format` from their own plugin config |
|
||||
| `scan_order_compensation` | string, `"auto"` | `"auto"` shows one half of each panel a refresh behind while something scrolls at one frame per refresh, which removes the 1px step a 1:N-scan panel shows across its middle; `"off"` disables it. Applies only to layouts whose row order is known: plain or parallel chains, 0 or 180 degree orientation, `multiplexing` 0, `scan_mode` 0, and not in the emulator | `DisplayManager._setup_scan_order_compensation()` (`src/display_manager.py`, `src/scan_order.py`) |
|
||||
| `dynamic_duration.max_duration_seconds` | int, optional | Cap for plugins that request dynamic display time | `DisplayController._get_global_dynamic_cap()` (`src/display_controller.py`) |
|
||||
|
||||
## `display.vegas_scroll` — continuous scroll mode
|
||||
|
||||
Reference in New Issue
Block a user