Commit Graph
7 Commits
Author SHA1 Message Date
poprhythm a4a8e37242 Fix asymmetric album art crop -- was showing more top-left, losing bottom/right
Root-caused with the calibration tool's crosshair marker: at rest and
near-rest (zoom breathing cycle's endpoints, where Scale is momentarily
pinned to a constant value), animation.Origin has no effect at all --
confirmed empirically, varying origin_x/origin_y produced byte-identical
output whenever Scale doesn't actually change frame to frame, regardless
of the scale value itself. With nothing for Origin to pivot, the crop
fell back to render.Box's own default handling of an oversized
Transformation child, which isn't centered: the crosshair sat several
pixels down-and-right of true center, meaning the art was effectively
showing more of its own top-left and cropping away a disproportionate
chunk of the bottom-right -- exactly "losing ~10% on bottom and right,
when it's not zoomed".

Fixed with a *constant* Translate (same value at every keyframe, not
animated) -- found empirically via the calibration tool (the (tx, ty)
that centered the crosshair at rest also held up at peak zoom, checked
against both). This isn't the mistake an earlier, abandoned attempt
made: that version's Translate *varied* over the animation fighting
Origin's own pivot, and interacted badly with Scale's continuous
interpolation (the historical "periodic pop" bug, per pixlet's own
source on Translate's per-frame rounding). A constant value never
changes frame to frame, so there's nothing for it to round-mismatch
against -- confirmed smooth across a full cycle.

Scaled by `scale` (box_crop_bias = -2 * scale) to stay correct at 2x.
dev/calibration_test.star updated to match, per its own sync requirement
with the real _zoom_transformation.

Verified: crosshair now dead-center at rest (calibration tool), 0
black-corner hits for jitter+bands in isolation at worst-case settings
(strong zoom, random position) at both 1x and 2x, and 0 black/void
corners across a full zoom cycle at all four ZOOM_RANDOM_SPOTS values
with strong zoom.
2026-10-10 14:23:33 -04:00
poprhythm d8add24c5e Replace Translate-based centering with animation.Origin; add zoom position config
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.
2026-10-02 22:12:25 -04:00
poprhythm 4a2b3caac8 Fix the periodic zoom pop at its root: disable Translate's pixel rounding
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.
2026-10-02 22:03:05 -04:00
poprhythm 9f63adbfbf Revert centered zoom; Scale+Translate causes a periodic rendering pop
Combining animation.Scale with animation.Translate to center the zoom
(two commits back) visually fixed the top-left crop bias, but introduced
a worse problem: pixlet periodically re-rasterizes the whole image with a
visible "pop" roughly every 0.7-1s, confirmed with both WebP and GIF
output and independent of zoom amplitude -- a property of pixlet's
animation engine when Scale and Translate are combined, not something
fixable from this app's math. Scale alone, with no Translate, has none of
this popping (confirmed via isolated testing).

Reverted _art_widget to Scale-only (as it was before centering), and
restored ZOOM_LEVELS["strong"] to 1.7 (the strong-zoom black border only
applied to the Translate-based centering, not plain Scale). This brings
back the original top-left crop bias as a known, accepted cosmetic
trade-off -- better than a periodic visible pop. Updated the README and
dev/calibration_test.star to match and documented the full history for
anyone who wants to revisit centering later.
2026-10-01 22:48:15 -04:00
poprhythm 5f42319e26 Revert jerky zoom fix; cap strong zoom level instead
Dividing the centering shift by top_scale (previous commit) avoided the
strong-zoom black border, but made the shift a nonlinear function of the
scale value being interpolated -- mid-animation, the pan no longer
tracked the zoom at a constant rate, which read as jerky/skippy motion.

Reverted shift to the original linear formula, (1-s)*(ART_SIZE/2), which
moves smoothly in lockstep with Scale's own eased interpolation. Instead,
capped ZOOM_LEVELS["strong"] at 1.45 (down from 1.7) -- the highest value
where this plain linear formula stays safely inside the art's bounds,
verified with the calibration tool (no black border across the full
animation cycle, at both 1.45x and against a live station).
2026-10-01 22:34:45 -04:00
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
poprhythm 16ac004d06 Add a calibration test image/script for visually checking zoom cropping
dev/calibration.png is a synthetic grid (concentric color rings, corner
markers, a crosshair) and dev/calibration_test.star runs it through the
same zoom logic as the real _art_widget, so cropping can be checked
visually without depending on what a live station happens to be playing.
This is what was used to find and verify the centered-zoom fix.
2026-10-01 21:57:51 -04:00