Align the container to the disc, and find the decoder-free packed player fits
Two sessions, unrecorded until now, committed together because their edits share files and cannot be split cleanly after the fact. Session 28 (FINDINGS 60): the container is DLX5 -- every record sector-aligned, 120/120 starting on a boundary where 3/120 did, +0.48% on the wire and zero clocks -- and the ring's release rounds to RECALN so no pad is stranded. Two encoder levers measured and refused: `--spans all` buys +0.19 dB for +67% of the wire, and joint span/lam selection emits byte-identical containers because `lam` never leaves its floor on any of 120 frames. Session 29 (FINDINGS 61): the packed full-frame blit is 27.3% of a 12 fps frame, a channel fills GVRAM in buffer mode off the disc with the CPU halted, and it walks the 1,024 B line stride itself through array chaining. At the 9 clk/B dual-address floor the codec is 110.4% of a frame and a decoder-free packed literal player is 55.2%, at +4.89 dB -- 2.75 dB past a ceiling the codec's scene-wide palette cannot cross. Encoder work is parked; the codec is kept and not built on. check.sh is ALL GREEN before and after, plus one new stage that gates the ORDER of the measured paint costs rather than their values. Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
+33
-5
@@ -28,12 +28,40 @@ def load_frames(d):
|
||||
return [np.asarray(Image.open(f).convert("RGB")) for f in fs]
|
||||
|
||||
|
||||
def scene_palette(rgb, colors=256, stride=3):
|
||||
"""One shared palette for the whole scene, no dithering (cel art is flat)."""
|
||||
def scene_palette(rgb, colors=256, stride=3, reserve_black=True):
|
||||
"""One shared palette for the whole scene, no dithering (cel art is flat).
|
||||
|
||||
`reserve_black` puts TRUE BLACK at index 0 and quantises the picture into
|
||||
the other 255 entries. It is not a cosmetic default (FINDINGS 23.4): the
|
||||
picture is 256x192 inside a 256x256 mode, GVRAM cleared to zero displays
|
||||
palette entry 0, and a free mediancut palette puts a real image colour
|
||||
there -- on 00020 f0001 it was (206,192,176), used by 210 image pixels, so
|
||||
the 64 blank rows of letterbox came out beige. Entry 0 also needs `I = 0`
|
||||
in the X68000's GRB555 word or the bars sit at RGB (4,4,4) (23.3); that
|
||||
half is `dlxload.pack_palette`'s and it needs no special case, because a
|
||||
(0,0,0) entry picks I=0 by its own minimum-squared-error rule.
|
||||
|
||||
Black is RESERVED, not withheld: the mapper may still spend index 0 on
|
||||
genuinely black pixels, which is the entry it would have wanted anyway.
|
||||
What the reservation buys is that index 0 is black REGARDLESS of what the
|
||||
scene contains, which is what the letterbox needs and what a free palette
|
||||
cannot promise.
|
||||
"""
|
||||
samp = np.concatenate([r.reshape(-1, 3) for r in rgb[::stride]])
|
||||
ref = Image.fromarray(samp.reshape(-1, 1, 3)).quantize(
|
||||
colors=colors, method=Image.MEDIANCUT, dither=Image.NONE)
|
||||
pal = np.array(ref.getpalette()[:colors * 3], dtype=np.uint8).reshape(-1, 3)
|
||||
n = colors - 1 if reserve_black else colors
|
||||
q = Image.fromarray(samp.reshape(-1, 1, 3)).quantize(
|
||||
colors=n, method=Image.MEDIANCUT, dither=Image.NONE)
|
||||
pal = np.array(q.getpalette()[:n * 3], dtype=np.uint8).reshape(-1, 3)
|
||||
if not reserve_black:
|
||||
return q, pal
|
||||
pal = np.vstack([np.zeros((1, 3), np.uint8), pal])
|
||||
# The quantiser above cannot be reused as the mapping reference: its
|
||||
# palette is the 255 it chose, at the wrong indices. A P-mode image
|
||||
# carrying the FINAL palette is what every frame is then mapped against,
|
||||
# so the indices in the container and the entries in the container's
|
||||
# palette section are the same table by construction.
|
||||
ref = Image.new("P", (1, 1))
|
||||
ref.putpalette(pal.tobytes().ljust(768, b"\0"))
|
||||
return ref, pal
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user