mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-02 01:08:05 +00:00
Vegas mode: reclaim dead space and pace the rotation
On a wide panel Vegas mode spent much of its time showing black. At 50px/s on a 512px display, one display width of blank is 10.2 seconds, which makes several long-standing behaviours expensive: - ScrollHelper prepended a full display width of black as an "initial gap", charged once per cycle — 10.2s of black at the start of every rotation. - Plugins without get_vegas_content() are captured off a full-display canvas, so their blank margins entered the ticker too. Measured: of-the-day drew 35px of "No Data" on a 512px canvas (92% blank), youtube-stats 142px of content with 185px of black either side. Only the scroll_helper path had any trimming. - Cycle transitions deliberately pushed a blank frame and then recomposed synchronously: 84ms at best, 4.8s at worst, every millisecond of it black. - buffer_ahead doubled as the cycle size, so a 21-plugin install showed 3 plugins per cycle and took ~7 cycles to come around. - separator_width was applied between every image rather than at plugin boundaries, so a per-row ticker like the F1 scoreboard (116 images, which it renders 4px apart internally) got a 32px chasm between each row — and the width budget didn't count those gaps, so the plugin quietly occupied far more of the panel than intended. Changes: - src/vegas_mode/geometry.py: numpy column-ink primitives shared by the trimmer and the audit tool, so the number reported is the number acted on. A Python per-column loop over a 17,000px strip is far too slow for the render path. - PluginAdapter trims every content path, not just scroll_helper. Only outer edges are cropped: interior blank columns are the plugin's own layout (logo left, score right) and closing them would corrupt the design. A plugin on a non-black background is inherently unaffected. - ScrollHelper.create_scrolling_image takes an explicit lead_gap, still defaulting to display_width so the many standalone-ticker callers are unchanged. Vegas passes lead_in_width (default 0). - Cycle end holds the last rendered frame instead of blanking, turning the recompose into a brief freeze rather than the panel switching off. - plugins_per_cycle (default 6) is split from buffer_ahead, which goes back to being only a prefetch low-water mark. - max_plugin_width_ratio (default 3x display width) caps one plugin's share of a cycle. Overflow is deferred, not discarded: a rotation offset advances each fetch so later rows appear on subsequent cycles. Single oversized images are cropped at a blank column so the cut misses glyphs. - Composition groups images by plugin: rows are joined by intra_plugin_gap (default 8) and separator_width applies only between plugins. The width budget now counts those gaps. - Plugin data updates no longer run on the Vegas render path. All new settings are user-configurable in Display -> Vegas Scroll, including min/max cycle duration and dynamic duration, which previously existed in code but were reachable only by hand-editing config.json. Measured with scripts/dev/vegas_audit.py on a 512x64 panel: mean ink coverage 42.7% -> 69.4% fully blank 5.9% -> 0% reads as empty 13.6% -> 0% worst blank stretch 4.8s -> 0s full rotation 414s -> 123s plugins per cycle 3 -> 6 Note the metric choice: a "fully blank" scan (>=95% black viewport) reported only 0.4% and badly understated the problem, because two full-width segments with mid-canvas content never fully blank the viewport — they hold it at ~28%. window_coverage_stats grades every viewport position by how much ink it carries, which is what tracks perceived dead time. Known remaining: cycle transitions still freeze ~3.5s while the next cycle is fetched. Fixing that needs background prefetch, which is deferred because the fallback-capture path mutates the shared display_manager.image and racing it against the render loop risks torn frames. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
This commit is contained in:
@@ -110,20 +110,30 @@ class ScrollHelper:
|
||||
self.is_scrolling = False
|
||||
self.scroll_complete = False
|
||||
|
||||
def create_scrolling_image(self, content_items: list,
|
||||
def create_scrolling_image(self, content_items: list,
|
||||
item_gap: int = 32,
|
||||
element_gap: int = 16) -> Image.Image:
|
||||
element_gap: int = 16,
|
||||
lead_gap: Optional[int] = None) -> Image.Image:
|
||||
"""
|
||||
Create a wide image containing all content items for scrolling.
|
||||
|
||||
|
||||
Args:
|
||||
content_items: List of PIL Images to include in scroll
|
||||
item_gap: Gap between different items
|
||||
element_gap: Gap between elements within an item
|
||||
|
||||
lead_gap: Blank columns before the first item. Defaults to a full
|
||||
display width, which makes a standalone ticker scroll in from
|
||||
off-screen. Callers that loop many plugins back-to-back (Vegas
|
||||
mode) pass a smaller value, since a full display width of black
|
||||
reads as the panel being switched off at the start of every
|
||||
cycle.
|
||||
|
||||
Returns:
|
||||
PIL Image containing all content arranged horizontally
|
||||
"""
|
||||
if lead_gap is None:
|
||||
lead_gap = self.display_width
|
||||
lead_gap = max(0, int(lead_gap))
|
||||
if not content_items:
|
||||
# Create empty image if no content
|
||||
# Still set total_scroll_width to 0 to indicate no scrollable content
|
||||
@@ -144,13 +154,13 @@ class ScrollHelper:
|
||||
total_width += element_gap * len(content_items)
|
||||
|
||||
# Add initial gap before first item
|
||||
total_width += self.display_width
|
||||
|
||||
total_width += lead_gap
|
||||
|
||||
# Create the full scrolling image
|
||||
full_image = Image.new('RGB', (total_width, self.display_height), (0, 0, 0))
|
||||
|
||||
|
||||
# Position items
|
||||
current_x = self.display_width # Start with initial gap
|
||||
current_x = lead_gap # Start with initial gap
|
||||
|
||||
for i, img in enumerate(content_items):
|
||||
# Paste the item image
|
||||
|
||||
Reference in New Issue
Block a user