Skip to content

sugarcraft/candy-flip

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

candy-flip

CandyFlip

CI codecov Packagist Version License PHP

demo

ASCII GIF viewer on the SugarCraft stack — port of namzug16/gifterm. Decodes a .gif on disk via ext-gd, downsamples each frame to a configurable cell grid, and renders the animation into the terminal as ANSI-coloured Unicode block-glyphs at a configurable framerate.

composer require sugarcraft/candy-flip
candy-flip my-animation.gif         # solid-block preset (default)
candy-flip my-animation.gif density # ASCII luminance ramp

Keys

Key Action
Space Pause / resume
Step back one frame
Step forward one frame
d Toggle solid ↔ density preset
q / Esc Quit

Implementation notes

The decoder uses PHP's built-in imagecreatefromstring() for in-memory single-frame extraction — no temporary files are written to disk. It walks the GIF89a byte-stream manually to:

  1. Parse the Logical Screen Descriptor and Global Color Table (GCT) from the header.
  2. Walk the frame stream, extracting each frame's Graphic Control Extension (GCE) delay, disposal method (0–3), and transparent-color index.
  3. Extract per-frame Local Color Table (LCT) when present; fall back to the GCT otherwise.
  4. Reassemble a minimal single-frame GIF payload in memory and pass it to imagecreatefromstring().
  5. Area-average downsample the resulting GdImage to the requested cell grid, skipping transparent pixels in the average.

GIF parsing is hand-rolled to avoid loading the entire animation into a GdImage at once; each frame is decoded independently so memory usage stays bounded regardless of input size.

Architecture

File Role
Decoder Reads the GIF, extracts per-frame GCE delay + disposal + transparency + local color table, hands each frame to GD via imagecreatefromstring(), area-average downsamples to a cell grid, returns a list of {@see Frame}.
Frame Pure value — 2-D RGB grid in cell coordinates with per-frame $delay (centiseconds), $disposal method (0–3), and $transparent flag.
Downsampler Image downsampler with two modes: NEAREST (center-pixel sample) and AREA_AVERAGE (area-weighted RGB average — higher quality, default).
Dither\FloydSteinberg Floyd-Steinberg error-diffusion dithering against a fixed palette. Source image is not modified; returns a new GdImage.
Renderer ANSI emitter. Two presets: solid (24-bit blocks) or density (luminance ramp). withAdaptiveSize() queries the TTY via SizeIoctl so the output never overflows the viewport; withConstraints() accepts explicit row/col limits for testing.
Cache/FrameCache WeakMap-backed memoization cache keyed by Frame object identity. Identical frames skip the rendering step on re-playback, and entries are dropped automatically when the Frame is garbage-collected.
Player SugarCraft Model — index + paused + preset state. Cmd::tick(...) schedules frame advance using per-frame delays.
TickMsg Frame-tick message produced by the Cmd.

The decoder caps at 256 frames so a runaway file can't OOM the runtime; pause + manual step are always available even on long animations.

Test

composer install
vendor/bin/phpunit

About

ASCII GIF viewer — port of namzug16/gifterm on the SugarCraft stack.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages