brushkit
Rust crates that read brush files and render their tip shapes. They read only. Nothing here writes or converts a brush file.
- Photoshop
.abr, versions 1, 2, 6, 7, 9 and 10: brush names (v1 files have none), sampled tip bitmaps (8 or 16 bit, raw, RLE or zlib), computed-tip geometry, the full brush descriptor and embedded patterns. - Procreate
.brushand.brushset: brush names, set order frombrushset.plist, tip shapes fromShape.png. - Tip rendering: grayscale bitmaps, thumbnails sized for a grid, contact sheets.
Crates
| Crate | Purpose |
|---|---|
brushkit |
One dependency over the two readers, exposed as brushkit::abr and brushkit::preview. |
brushkit-abr |
Parses an .abr pack into brushes (name, tip bitmap, descriptor), computed presets, patterns, and a per-file report of what was dropped and why. |
brushkit-preview |
One tip bitmap per brush for any supported file, plus contact sheets. |
brushkit-fixture |
Byte writers for the descriptor encoding, used by brushkit-abr's own tests to build synthetic descriptors. |
Usage
[]
= "0.2"
use parse_abr_deferred_without_patterns;
use ;
The same code is crates/brushkit/examples/readme.rs. A test checks the two
are identical, so the block compiles whenever the test suite does.
preview_abr, preview_brush and preview_brushset return every brush in
file order exactly once, available or not. A brush whose tip cannot be
rendered carries a reason (NoShapePng, UnsupportedTipKind, Corrupt,
TooLarge) rather than being dropped, so a caller can lay out a complete
grid. Each entry also carries optional source_dimensions for the original
raster, independent of the preview size and retained if pixel decoding fails.
Computed tips have no source raster dimensions. No returned bitmap has a side
larger than max_cell, and a tip that already fits keeps its size. A .brushset
without brushset.plist is read in zip order and has no set name.
parse_abr decodes every tip up front. parse_abr_deferred and
parse_abr_deferred_without_patterns keep tips as byte ranges to decode on
demand. The preview path uses the latter, so previewing a pack with a large
pattern block does not copy or decode the patterns.
Features
text(default,brushkitandbrushkit-preview): the contact-sheet API. Labels need a font, so it pulls inab_glyphand an embedded copy of Inter Regular.serde(brushkitandbrushkit-abr):Serializeon the raw descriptor dump inbrushkit_abr::dump.
Every crate builds for wasm32-unknown-unknown. The readers take &[u8] and
touch neither the filesystem nor threads.
Untrusted input
Every size, count and dimension read from a file is checked against a ceiling
before anything is allocated, and malformed input is an error, not a panic.
Four libFuzzer targets under fuzz/ cover both readers. CI replays their
committed seed corpus and fuzzes each target for 30 seconds on every pull
request and push to main.
Building
See fuzz/README.md for the other targets and seed regeneration.
Real-file tests
Real brush packs are copyrighted and are not in the repository. Tests that
assert facts about real files read BRUSHKIT_CORPUS_DIR and are skipped when
it is unset. Synthetic files in the unit tests and the fuzz corpus cover the
parsing mechanics without it.
Versioning
The crates are at 0.x. Minor releases may change the public API. Pin the
minor version. Every release is a git tag vX.Y.Z on this repository.
License
MIT, see LICENSE. The Inter font embedded by the text feature is under
the SIL Open Font License 1.1, see crates/preview/assets/fonts/OFL.txt.