Loop the RGB-split glitch burst with pauses, instead of playing once
The jitter previously played its 4 states once (~1s total) then went quiet for the rest of the 5s static window, since render.Sequence doesn't loop. Now repeats the burst-then-pause pattern enough times to cover the whole window: GLITCH_REPEATS is computed from STATIC_OVERLAY_SECONDS so it still fully covers the window if either changes, rather than a hardcoded repeat count silently falling short. Verified via an isolated single-cycle test (burst of 4 states + 1 pause, outside the main zoom animation) that the pause frame is genuinely clean (no tint bands) -- pixlet collapses each multi-tick hold into a single deduped output frame, which made an earlier attempt to verify this by guessing raw frame indices unreliable; confirmed instead by exhaustively inspecting all 5 of the isolated test's (correctly deduped) output frames.
This commit is contained in:
1 parent
6cd5decdf5
commit
78621b3831
1 file changed
+35
-17
+35
-17
@@ -356,6 +356,7 @@ def _zoomed_art(art_bytes, zoom_scale, zoom_position, subsonic_id):
|
|||||||
# keyframe that was visible: roughly the top third and an overlapping
|
# keyframe that was visible: roughly the top third and an overlapping
|
||||||
# middle band, each about 30% of the frame's height.
|
# middle band, each about 30% of the frame's height.
|
||||||
GLITCH_HOLD_TICKS = 5 # ~250ms per jitter state at the default 50ms/tick
|
GLITCH_HOLD_TICKS = 5 # ~250ms per jitter state at the default 50ms/tick
|
||||||
|
GLITCH_PAUSE_TICKS = 15 # ~750ms of clean art between bursts
|
||||||
GLITCH_STATES = [
|
GLITCH_STATES = [
|
||||||
# (top1, height1, shift1, hue1, top2, height2, shift2, hue2)
|
# (top1, height1, shift1, hue1, top2, height2, shift2, hue2)
|
||||||
(3, 9, -2, 150.0, 19, 8, 2, -60.0),
|
(3, 9, -2, 150.0, 19, 8, 2, -60.0),
|
||||||
@@ -364,6 +365,16 @@ GLITCH_STATES = [
|
|||||||
(1, 10, 1, 150.0, 21, 7, -2, -60.0),
|
(1, 10, 1, 150.0, 21, 7, -2, -60.0),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# render.Sequence doesn't loop on its own (see _glitch_scanlines below), so
|
||||||
|
# to repeat the burst-then-pause pattern for the whole show_static window,
|
||||||
|
# the repeats have to be spelled out as one long children list up front
|
||||||
|
# rather than relying on any runtime looping. Rounds up so the window is
|
||||||
|
# always fully covered, even if that runs a little past
|
||||||
|
# STATIC_OVERLAY_SECONDS -- better than the jitter cutting out early.
|
||||||
|
_GLITCH_CYCLE_TICKS = len(GLITCH_STATES) * GLITCH_HOLD_TICKS + GLITCH_PAUSE_TICKS
|
||||||
|
_GLITCH_TICKS_PER_SECOND = 20 # 1000ms / 50ms-per-tick
|
||||||
|
GLITCH_REPEATS = int(STATIC_OVERLAY_SECONDS * _GLITCH_TICKS_PER_SECOND / _GLITCH_CYCLE_TICKS) + 1
|
||||||
|
|
||||||
def _glitch_band(zoomed_art, top, height, shift_x, hue_change):
|
def _glitch_band(zoomed_art, top, height, shift_x, hue_change):
|
||||||
tinted = filter.Hue(child = zoomed_art, change = hue_change)
|
tinted = filter.Hue(child = zoomed_art, change = hue_change)
|
||||||
band = render.Padding(
|
band = render.Padding(
|
||||||
@@ -379,29 +390,36 @@ def _glitch_band(zoomed_art, top, height, shift_x, hue_change):
|
|||||||
# the 32px art slot, bleeding into the text column next to it.
|
# the 32px art slot, bleeding into the text column next to it.
|
||||||
return render.Box(width = ART_SLOT, height = ART_SLOT, child = shifted)
|
return render.Box(width = ART_SLOT, height = ART_SLOT, child = shifted)
|
||||||
|
|
||||||
def _glitch_jitter(zoomed_art):
|
def _glitch_hold(child, duration):
|
||||||
states = []
|
# A no-op Transformation purely to pin a Sequence child's FrameCount
|
||||||
for top1, h1, shift1, hue1, top2, h2, shift2, hue2 in GLITCH_STATES:
|
# to `duration` ticks, so render.Sequence holds on it for that long
|
||||||
state = render.Stack(children = [
|
# before advancing to the next child -- Sequence has no other way to
|
||||||
_glitch_band(zoomed_art, top1, h1, shift1, hue1),
|
# control how long a given child plays.
|
||||||
_glitch_band(zoomed_art, top2, h2, shift2, hue2),
|
return animation.Transformation(
|
||||||
])
|
child = child,
|
||||||
|
duration = duration,
|
||||||
# Wrapping in a no-op Transformation just to pin this state's
|
|
||||||
# FrameCount to GLITCH_HOLD_TICKS, so render.Sequence holds on it
|
|
||||||
# for that many ticks before advancing to the next state --
|
|
||||||
# Sequence has no other way to control how long a child plays.
|
|
||||||
states.append(animation.Transformation(
|
|
||||||
child = state,
|
|
||||||
duration = GLITCH_HOLD_TICKS,
|
|
||||||
width = ART_SLOT,
|
width = ART_SLOT,
|
||||||
height = ART_SLOT,
|
height = ART_SLOT,
|
||||||
keyframes = [
|
keyframes = [
|
||||||
animation.Keyframe(percentage = 0.0, transforms = []),
|
animation.Keyframe(percentage = 0.0, transforms = []),
|
||||||
animation.Keyframe(percentage = 1.0, transforms = []),
|
animation.Keyframe(percentage = 1.0, transforms = []),
|
||||||
],
|
],
|
||||||
))
|
)
|
||||||
return render.Sequence(children = states)
|
|
||||||
|
def _glitch_jitter(zoomed_art):
|
||||||
|
pause = _glitch_hold(render.Box(width = ART_SLOT, height = ART_SLOT), GLITCH_PAUSE_TICKS)
|
||||||
|
|
||||||
|
sequence = []
|
||||||
|
for _ in range(GLITCH_REPEATS):
|
||||||
|
for top1, h1, shift1, hue1, top2, h2, shift2, hue2 in GLITCH_STATES:
|
||||||
|
state = render.Stack(children = [
|
||||||
|
_glitch_band(zoomed_art, top1, h1, shift1, hue1),
|
||||||
|
_glitch_band(zoomed_art, top2, h2, shift2, hue2),
|
||||||
|
])
|
||||||
|
sequence.append(_glitch_hold(state, GLITCH_HOLD_TICKS))
|
||||||
|
sequence.append(pause)
|
||||||
|
|
||||||
|
return render.Sequence(children = sequence)
|
||||||
|
|
||||||
def _glitch_scanlines():
|
def _glitch_scanlines():
|
||||||
# render.Sequence doesn't loop (it just stops painting once its total
|
# render.Sequence doesn't loop (it just stops painting once its total
|
||||||
|
|||||||