snatch is a plugin-driven bitmap processing pipeline for retro/pixel workflows.
It is still great for bitmap fonts, but the architecture is now more generic:
- extract bitmap data from a source
- transform that data (optional)
- export in a target format
snatch runs in three stages:
- Extractor plugin
- Reads the input source (
ttf,image, etc.) - Produces a
snatch_fontbitmap representation
- Transformer plugin (optional)
- Reads
snatch_font - Can annotate or replace data through
font->user_data
- Exporter plugin
- Reads
snatch_fontand optional transformed data - Writes the final artifact (
.png,.s,.bin,.c, ...)
Pipeline examples:
TTF -> ttf_extractor -> partner_tiny_transform -> partner_sdcc_asm_tiny -> .s
image -> image_extractor -> (no transform) -> raw_bin -> .bin
TTF -> ttf_extractor -> partner_tiny_transform -> raw_bin -> partner_tiny_bin_extractor -> partner_tiny_raster_transform -> png
This is why the new model is more flexible: with appropriate plugins, you can run non-font flows too (for example: extract one glyph/region from color image -> dither transform -> 1bpp image export).
- Extract bitmap font glyphs from image sheets (
image_extractor) - Rasterize TTF fonts to 1bpp glyph bitmaps (
ttf_extractor) - Run optional transformers before export
- Export to PNG grid, Partner SDCC ASM (tiny or bitmap), raw binary, and raw C array
- Control ASCII range, colors, margins/padding, font size, fixed/proportional mode
git clone https://github.com/retro-vault/snatch.git
cd snatch
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j$(nproc)ctest --test-dir build --output-on-failure./bin/snatch \
--plugin-dir ./bin/plugins \
--extractor-parameters "input=fonts/Retro.ttf,first_ascii=32,last_ascii=126,font_size=16" \
--exporter png \
--exporter-parameters "output=out/font-grid.png,columns=16,rows=6"./bin/snatch \
--plugin-dir ./bin/plugins \
--extractor-parameters "input=assets/fontsheet.png,margins_left=2,margins_top=2,margins_right=2,margins_bottom=2,padding_left=1,padding_top=1,padding_right=1,padding_bottom=1,columns=16,rows=6,first_ascii=32,last_ascii=126,fore_color=#000000,back_color=#FFFFFF" \
--exporter raw_bin \
--exporter-parameters "output=out/font.bin"./bin/snatch \
--plugin-dir ./bin/plugins \
--extractor-parameters "input=fonts/Retro.ttf,first_ascii=32,last_ascii=127,font_size=16" \
--transformer partner_tiny_transform \
--exporter partner_sdcc_asm_tiny \
--exporter-parameters "output=out/my_font.s,module=my_font,symbol=my_font,proportional=true,space_width=3,letter_spacing=2"./bin/snatch \
--plugin-dir ./bin/plugins \
--extractor-parameters "input=fonts/Retro.ttf,first_ascii=32,last_ascii=127,font_size=16" \
--transformer partner_bitmap_transform \
--transformer-parameters "font_mode=proportional,space_width=3,letter_spacing=2" \
--exporter raw_c \
--exporter-parameters "output=out/font_raw.c,bytes_per_line=8,symbol=font_data"| Name | Input | Purpose |
|---|---|---|
ttf_extractor |
ttf |
Rasterize TTF glyphs into 1bpp bitmap glyphs |
image_extractor |
image |
Extract glyph bitmaps from grid image sheets |
image_passthrough_extractor |
image |
Load full image as grayscale passthrough payload in user_data |
partner_tiny_bin_extractor |
bin |
Load Partner Tiny binary stream into user_data for raster decoding |
| Name | Purpose | Notes |
|---|---|---|
partner_tiny_transform |
Vectorize bitmap glyphs into Partner Tiny move streams | Intended for partner_sdcc_asm_tiny |
partner_tiny_raster_transform |
Interpret Partner Tiny moves and rebuild bitmap glyphs | Intended for partner_tiny_bin_extractor + png |
partner_bitmap_transform |
Serialize bitmap font to Partner bitmap byte stream | Intended for partner_sdcc_asm_bitmap, raw_bin, raw_c |
fzx-transform |
Compute ZX Spectrum FZX-style glyph metadata | Stores metadata in font->user_data |
dither_1bpp_transform |
Dither grayscale passthrough image to 1bpp bitmap glyph | Intended for image_passthrough_extractor + png |
| Name | Format | Standard | Purpose |
|---|---|---|---|
png |
png |
snatch-grid |
Render bitmap font as PNG grid |
partner_sdcc_asm_tiny |
asm |
partner-sdcc-asm-tiny |
SDCC assembly export for Partner tiny format |
partner_sdcc_asm_bitmap |
asm |
partner-sdcc-asm-bitmap |
SDCC assembly export for Partner bitmap format |
raw_bin |
bin |
raw-1bpp |
Raw continuous byte stream (or Partner Tiny stream when input is partner_tiny_transform) |
raw_c |
c |
raw-1bpp |
Raw byte stream as const uint8_t[] |
dummy |
txt |
debug-dump |
Diagnostic exporter |
| Option | Alias | Description |
|---|---|---|
--extractor |
-q |
Optional extractor plugin override |
--extractor-parameters |
-v |
Extractor params (k=v,...) |
--plugin-dir |
-d |
Plugin search directory override |
--transformer |
-w |
Optional transformer plugin name |
--transformer-parameters |
-y |
Transformer params (k=v,...) |
--exporter |
-e |
Exporter plugin name |
--exporter-parameters |
-x |
Exporter params (k=v,...) |
Stage-specific tuning should be passed to the owning plugin:
- extractor options via
--extractor-parameters - transformer options via
--transformer-parameters - exporter options via
--exporter-parameters
If --extractor is omitted, snatch infers it from input extension:
.ttf,.otf->ttf_extractor- common image extensions (
.png,.jpg,.jpeg, ...) ->image_extractor
Required ownership split:
- extractor owns input path: set
input=...in--extractor-parameters - exporter owns output path: set
output=...in--exporter-parameters - there is no positional input argument and no root
--outputoption anymore
Use concrete exporter names directly (no separate format parameter):
partner_sdcc_asm_tinypartner_sdcc_asm_bitmapraw_craw_binpng
Concept example (full image passthrough -> dither -> PNG):
./bin/snatch \
--plugin-dir ./bin/plugins \
--extractor image_passthrough_extractor \
--extractor-parameters "input=test/data/tut.png" \
--transformer dither_1bpp_transform \
--transformer-parameters "threshold=128" \
--exporter png \
--exporter-parameters "output=out/tut_dither_1bpp.png,columns=1,rows=1,padding=0,grid_thickness=0"Partner Tiny roundtrip (binary -> rasterized grid):
# 1) Create Partner Tiny binary stream.
./bin/snatch \
--plugin-dir ./bin/plugins \
--extractor-parameters "input=fonts/Retro.ttf,first_ascii=65,last_ascii=70,font_size=16" \
--transformer partner_tiny_transform \
--exporter raw_bin \
--exporter-parameters "output=out/retro_tiny.bin,font_mode=proportional,space_width=3,letter_spacing=1"
# 2) Load tiny stream and rasterize it back to bitmap grid.
./bin/snatch \
--plugin-dir ./bin/plugins \
--extractor partner_tiny_bin_extractor \
--extractor-parameters "input=out/retro_tiny.bin" \
--transformer partner_tiny_raster_transform \
--exporter png \
--exporter-parameters "output=out/retro_tiny_roundtrip.png,columns=3,rows=2,padding=1,grid_thickness=1"snatch searches plugin directories in this order and stops at the first one that provides the requested plugins:
--plugin-dir <dir>SNATCH_PLUGIN_DIR${CMAKE_INSTALL_FULL_LIBDIR}/snatch/plugins~/.local/lib/snatch/plugins
Plugins export:
int snatch_plugin_get(const snatch_plugin_info** out);Plugin ABI: include/snatch/plugin.h
snatch_plugin_info.kind:
SNATCH_PLUGIN_KIND_EXTRACTOR->extract_fontSNATCH_PLUGIN_KIND_TRANSFORMER->transform_fontSNATCH_PLUGIN_KIND_EXPORTER->export_font
- Create a plugin folder and CMake file under
plugins/<name>/. - Add it to
plugins/CMakeLists.txtwithadd_subdirectory(<name>). - Implement
snatch_plugin_get(...)and a staticsnatch_plugin_info. - Build and run with
--plugin-dir ./bin/plugins.
Extractor skeleton:
int my_extract(
const char* input_path,
const snatch_kv* options,
unsigned options_count,
snatch_font* out_font,
char* errbuf,
unsigned errbuf_len
);Transformer skeleton:
int my_transform(
snatch_font* font,
const snatch_kv* options,
unsigned options_count,
char* errbuf,
unsigned errbuf_len
);Exporter skeleton:
int my_export(
const snatch_font* font,
const char* output_path,
const snatch_kv* options,
unsigned options_count,
char* errbuf,
unsigned errbuf_len
);Minimal plugin descriptor shape:
const snatch_plugin_info k_info = {
"my_plugin",
"Short description",
"author",
"format-or-input",
"standard-or-profile",
SNATCH_PLUGIN_ABI_VERSION,
SNATCH_PLUGIN_KIND_EXPORTER, // or EXTRACTOR/TRANSFORMER
nullptr, // transform callback if transformer
nullptr, // export callback if exporter
nullptr // extract callback if extractor
};Notes:
- For transformers, use
font->user_datafor stage-to-stage contracts. - For exporters,
format/standardshould be non-empty. - Keep plugin-owned buffers alive for as long as
snatchmay read them.
- FreeType (
freetype): FreeType License (FTL) or GPLv2 - stb (
stb_image,stb_image_write): public domain or MIT - argparse (
cofyc/argparse): MIT - GoogleTest (
googletest): BSD 3-Clause
TTF fonts under test/data are freeware and remain copyright of their authors.