Root-caused the "pop" from the Scale+Translate centered zoom (reverted a few commits back) to pixlet's own source: render/animation/translate.go rounds a Translate's x/y to the nearest whole pixel every frame by default (rounding = "round"), while animation/scale.go never rounds Scale. A continuously-interpolating Scale combined with a rounded Translate holds still for several frames, then jumps a whole pixel at once -- a real, confirmed pixlet behavior, not a flaw in this app's formula. Transformation accepts a `rounding` parameter; passing "none" disables the snapping. Confirmed via frame-by-frame pixel-delta analysis: the per-frame change is now even throughout the animation (consistently ~900 AE out of 1024 possible, every frame) instead of long near-zero stretches punctuated by sudden full-frame jumps. Re-enables the centered zoom (Scale + Translate) that was reverted for jerkiness, now smooth. ZOOM_LEVELS["strong"] stays capped at 1.45 -- that black-border ceiling is unrelated to rounding and still applies. Verified against the live station: no pop, no black border at any zoom level, payload ~370KB (up from ~220-320KB, since rounding="none" means WebP can dedupe fewer near-identical frames) but still well under the 460KB device limit.
64 lines
2.9 KiB
Python
64 lines
2.9 KiB
Python
"""
|
|
Dev tool, not part of the deployed app: renders dev/calibration.png through
|
|
the exact same zoom logic as _art_widget in subwave_nowplaying.star, so you
|
|
can visually check how much margin gets cropped at rest and at full zoom,
|
|
and whether the crop is even on all sides.
|
|
|
|
calibration.png is a 360x360 grid: concentric 10%-wide color rings (magenta
|
|
outermost, then yellow, then cyan) plus four corner markers (red TL, green
|
|
TR, blue BL, white BR) and a center crosshair.
|
|
|
|
Usage:
|
|
pixlet render dev/calibration_test.star -o calib.gif
|
|
pixlet render dev/calibration_test.star zoom_scale=1.0 -o calib_rest.gif # rest only
|
|
|
|
Keep ART_SIZE/ART_SLOT/the zoom math below in sync with the real
|
|
_art_widget if that ever changes.
|
|
|
|
History, if this ever needs revisiting: a Scale+Translate centered zoom
|
|
was tried, then reverted after it produced a periodic rendering "pop"
|
|
(confirmed with both WebP and GIF output, independent of zoom amplitude).
|
|
Root cause, found in pixlet's own source
|
|
(render/animation/translate.go): a Translate's x/y are rounded to the
|
|
nearest whole pixel every frame by default (`rounding = "round"`), while
|
|
Scale is never rounded -- so a continuously-interpolating Scale combined
|
|
with a rounded Translate holds still for several frames, then jumps a
|
|
whole pixel at once. Passing `rounding = "none"` on the Transformation
|
|
(see below) disables that snapping and fixed it -- confirmed via
|
|
frame-by-frame pixel-delta analysis (even per-frame change throughout,
|
|
instead of long still stretches punctuated by sudden jumps). If a future
|
|
change reintroduces jerkiness, check whether `rounding = "none"` is still
|
|
present before suspecting the shift formula.
|
|
"""
|
|
|
|
load("render.star", "render")
|
|
load("animation.star", "animation")
|
|
load("calibration.png", CALIB = "file")
|
|
|
|
ART_SIZE = 36
|
|
ART_SLOT = 32
|
|
ART_ZOOM_DURATION = 200
|
|
|
|
def _art_widget(art_bytes, zoom_scale):
|
|
art = render.Image(src = art_bytes, width = ART_SIZE, height = ART_SIZE)
|
|
top_scale = max(zoom_scale, 1.0)
|
|
shift = (1.0 - top_scale) * (ART_SIZE / 2.0)
|
|
zoom = animation.Transformation(
|
|
child = art,
|
|
duration = ART_ZOOM_DURATION,
|
|
rounding = "none",
|
|
keyframes = [
|
|
animation.Keyframe(percentage = 0.0, transforms = [animation.Scale(1.0, 1.0), animation.Translate(0, 0)]),
|
|
animation.Keyframe(percentage = 0.5, transforms = [animation.Scale(top_scale, top_scale), animation.Translate(shift, shift)], curve = "ease_in_out"),
|
|
animation.Keyframe(percentage = 1.0, transforms = [animation.Scale(1.0, 1.0), animation.Translate(0, 0)], curve = "ease_in_out"),
|
|
],
|
|
)
|
|
return render.Box(width = ART_SLOT, height = ART_SLOT, child = zoom)
|
|
|
|
def main(config):
|
|
zoom_scale = float(config.get("zoom_scale", "1.4"))
|
|
return render.Root(child = render.Row(children = [
|
|
_art_widget(CALIB.readall(), zoom_scale),
|
|
render.Box(width = 32, height = 32, color = "#111111"),
|
|
]))
|