Files
subwave-nowplaying/dev/calibration_test.star
T
poprhythm 9f2216c692 Fix black border at strong album art zoom by scaling the recenter shift
The centering Translate formula, (1-s)*(ART_SIZE/2), was derived assuming
it's applied in final output pixels. That held up at a moderate zoom
(1.4x) but pushed past the image's actual edge at a strong one (1.7x) --
a real, on-device black border in the bottom-right corner, confirmed with
the calibration tool (true black RGB(0,0,0) pixels, not just dark
content).

Dividing the shift by the scale factor keeps the crop inside the image's
bounds at every zoom level the app offers, verified via the calibration
tool at 1.0/1.15/1.4/1.7x and against a live station -- at the cost of
the most extreme (1.7x) setting being very slightly off-center rather
than perfectly centered, which is a trade worth making since going
outside the art's bounds is worse. Moderate and subtle remain visibly
centered.
2026-10-01 22:23:52 -04:00

51 lines
2.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. At rest, you should see a
thin, EVEN sliver of magenta on all four edges. As it zooms in, each corner
marker should get clipped by the same amount -- if one side/corner loses
noticeably more than the others, the zoom isn't centered.
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.
"""
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) / top_scale
zoom = animation.Transformation(
child = art,
duration = ART_ZOOM_DURATION,
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"),
]))