Skip to content

Add deflate compression and crushed PCM audio to .glif format#3

Merged
farkasmark merged 7 commits intomainfrom
deflate-compression
Feb 21, 2026
Merged

Add deflate compression and crushed PCM audio to .glif format#3
farkasmark merged 7 commits intomainfrom
deflate-compression

Conversation

@farkasmark
Copy link
Copy Markdown
Contributor

Summary

  • Deflate compression: 7 per-frame codecs (plain, delta, filtered, palette, planar + delta variants) with optimal selection per frame. Lossy preprocessing (color quantization + temporal thresholding) pushes compression from ~1.4x to ~5.3x on typical video.
  • Crushed PCM audio: Downsample + quantize original audio to 8kHz 8-bit unsigned PCM, deflate-compress, and append as a BLIP v2 section. Retro lo-fi sound that preserves speech and music with negligible file size overhead (~1MB for 2.5 min).
  • Web player and embedded HTML support audio playback via AudioBufferSourceNode (toggle with A key)
  • Vendored miniz for portable deflate compression (no external deps)

Test plan

  • make test — 185 tests pass across 12 modules
  • Re-encoded mk421.glif and hk421.glif with audio, verified playback in browser
  • Backwards compatibility: old decoders stop after video frames and never see audio section
  • 4-bit and 8-bit audio round-trip verified in unit tests

🤖 Generated with Claude Code

Implement channel-separated (planar) encoding and zlib-based compression
for .glif files. The encoder tries all codecs per frame and picks the
smallest: raw, RLE, delta, delta+RLE, planar RLE, planar delta+RLE,
macroblock delta, deflate, and delta+deflate.

Key techniques:
- Planar encoding: deinterleave [ch,r,g,b] into 4 byte planes before
  compression — groups similar data for better pattern matching
- Macroblock skip map: 8x8 cell blocks with a bitmap to skip unchanged
  regions entirely
- Deflate (via vendored miniz): LZ77+Huffman compression that handles
  high-entropy data where RLE fails completely

On mk421.glif (213x44 grid, 4633 frames): 122.5 MB -> 61.3 MB (2.0x).
The RLE-based codecs couldn't compress this content at all (every cell
has unique char+color, producing length-1 runs), but deflate with
planar pre-processing halves the file size.

New frame types: DEFLATE (0x04), PLANAR_DELTA_RLE (0x05),
PLANAR_RLE (0x06), BLOCK_DELTA (0x07), DELTA_DEFLATE (0x09).
v1/v2 files remain readable (backward compatible).
Drop all RLE-based codecs (RLE, delta, delta_RLE, byte_RLE,
planar_RLE, planar_delta_RLE, block_delta) and collapse v1/v2/v3
versioning into a single format version. The project hasn't been
released, so there's no backward compatibility to maintain.

Seven deflate-based frame types remain (even=keyframe, odd=P-frame):
DEFLATE, DELTA_DEFLATE, FILTERED_DEFLATE, DELTA_FILTERED_DEFLATE,
PALETTE_DEFLATE, PLANAR_DEFLATE, DELTA_PLANAR_DEFLATE.

Clean renumber frame types starting from 0x00. Update docs and tests.
153 tests pass (down from 204 — removed legacy codec tests).
…hold)

Two encoder-side preprocessing steps that push compression from ~2x to ~5x:
- Color quantization: drop N LSBs per RGB channel (default 2 bits with --compress)
- Temporal thresholding: snap cells with tiny color diffs to previous frame values

Defaults to --quant 6 --threshold 4 when --compress is active (122MB → 32MB on
hk421). Remove gzip outer compression from glif-embed.sh since .glif frames are
already deflate-compressed internally. Rebuild WASM player for current codecs.
Downsample and quantize original audio to 8-bit unsigned PCM at a
configurable sample rate (default 8kHz), deflate-compress, and append
as a BLIP v2 section after video frames. Sounds retro/lo-fi while
preserving speech and music. Negligible file size overhead (~1MB for
2.5 min at 8kHz 8-bit).

Encoder (blip.c): linear-interpolation resampling, stereo downmix,
s16→u8/u4 quantization, incremental feeding.

Decoder (glif.c): mz_uncompress + 4-bit nibble unpacking. Legacy
BLIP v1 sections skipped gracefully. Backwards compatible — old
decoders stop after video frames and never see the audio section.

Web playback: crushed PCM read from WASM HEAPU8, converted to float32
AudioBuffer, played via AudioBufferSourceNode synced to video. Speed
changes update playbackRate. Toggle with A key.

CLI: --audio, --audio-pcm, --audio-rate, --audio-depth flags.
@farkasmark farkasmark merged commit 63bf383 into main Feb 21, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant