The previous "fix" for Scale's crop bias paired Scale with a computed Translate, on the mistaken assumption that Scale's default pivot is the top-left corner. It isn't: pixlet's docs say the default origin is already the center (0.5, 0.5). That Translate was sized assuming Scale had no self-centering at all, so it overcorrected, visibly skewing the zoom toward the bottom-right -- the opposite of the bug it meant to fix. animation.Origin does directly and correctly what that Translate was clumsily approximating, so the Translate (and its rounding="none" workaround) is removed entirely in favor of Origin. This also means "strong" no longer needs the 1.45 safety cap -- restored to 1.7, since the black-border risk was specific to the Translate formula, not Scale + Origin (verified via the calibration tool at several off-center origins). Added a "Zoom position" schema option: Center (default) or Random. Random picks from a handful of preset off-center spots, chosen per-track from subsonic_id the same way the old corner-selection logic worked, so a still-playing track doesn't jump between spots on every refresh. Verified: no black border, no popping (confirmed via frame-delta analysis -- smooth throughout, no Translate means rounding is moot), and payload well under the 460KB limit at every zoom level/position combination, against the live station.
68 lines
3.2 KiB
Python
68 lines
3.2 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
|
|
pixlet render dev/calibration_test.star ox=0.3 oy=0.3 -o calib_offcenter.gif
|
|
|
|
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 first and produced a periodic rendering "pop", root-caused to
|
|
pixlet's own source (render/animation/translate.go): Translate's x/y are
|
|
rounded to the nearest whole pixel every frame by default, while Scale
|
|
never rounds, so a continuously-interpolating Scale combined with a
|
|
rounded Translate holds still for several frames then jumps a whole pixel
|
|
at once. `rounding = "none"` fixed that. But the Translate's *magnitude*
|
|
was also wrong: it assumed Scale pivots at the top-left corner by
|
|
default, when pixlet's docs say the default `origin` is already the
|
|
center (0.5, 0.5) -- so the "centering" Translate was overcorrecting,
|
|
visibly skewing the zoom toward the bottom-right instead. Replaced with
|
|
`origin` directly (which does, correctly, what that Translate was
|
|
clumsily approximating), confirmed via this calibration tool with
|
|
genuinely different (non-default) origin values -- they do change the
|
|
output, despite an earlier, flawed test (origin set to its own default
|
|
and compared against itself) suggesting `origin` had no effect at all.
|
|
"""
|
|
|
|
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, origin_x, origin_y):
|
|
art = render.Image(src = art_bytes, width = ART_SIZE, height = ART_SIZE)
|
|
top_scale = max(zoom_scale, 1.0)
|
|
zoom = animation.Transformation(
|
|
child = art,
|
|
duration = ART_ZOOM_DURATION,
|
|
origin = animation.Origin(origin_x, origin_y),
|
|
keyframes = [
|
|
animation.Keyframe(percentage = 0.0, transforms = [animation.Scale(1.0, 1.0)]),
|
|
animation.Keyframe(percentage = 0.5, transforms = [animation.Scale(top_scale, top_scale)], curve = "ease_in_out"),
|
|
animation.Keyframe(percentage = 1.0, transforms = [animation.Scale(1.0, 1.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"))
|
|
origin_x = float(config.get("ox", "0.5"))
|
|
origin_y = float(config.get("oy", "0.5"))
|
|
return render.Root(child = render.Row(children = [
|
|
_art_widget(CALIB.readall(), zoom_scale, origin_x, origin_y),
|
|
render.Box(width = 32, height = 32, color = "#111111"),
|
|
]))
|