Releases: jysf/crustyimg
Release list
0.7.0 - 2026-08-10
Release Notes
A correctness release. Several commands were returning the wrong file — a much
larger one, a sideways one, or none at all — on ordinary inputs. If you use
web, convert, resize, thumbnail, responsive, edit or build, the
output for the same command and input may change, and in each case the new
output is the correct one. Also opens RAW files in the browser demo.
Added
- RAW files open in the browser demo. Drop a
.dng,.cr2,.nefor
.arwon the demo page and it extracts the camera's embedded preview, the
same way the CLI does. Previews above 60 megapixels are declined with a note
pointing at the CLI, rather than risking the tab on a decode a phone may not
survive.
Changed
- Every command that re-encodes pixels now applies EXIF orientation. This is
a behavior change.convert,resize,thumbnail,responsiveand
editpreviously discarded the orientation tag without applying the rotation
it described, so a phone photo came out sideways — andresize --maxapplied
the bound to the wrong edge, so the output was also the wrong size. They now
rotate the pixels first, matching whatweb,optimizeandauto-orient
already did. For an image carrying a rotation tag, output dimensions will
differ from previous releases. Images with no orientation tag, or the default
one, are unaffected and produce byte-identical output. edit --save-recipenow records the auto-orient step. Recipes saved by
editgained an explicitauto-orientstep, so replaying one withapply
reproduces whateditdid. Previously the replay could differ from the
original on any image carrying a rotation tag.- A truncated JPEG now says so. Decoding one still succeeds — every image
viewer tolerates a partial JPEG, and so does crustyimg — but it now prints a
warning to stderr instead of handing back a partially-grey image silently. The
exit code is unchanged.
Fixed
- Downscaling no longer turns a graphic into a much larger file. Dithered
and halftoned images — scans, print artifacts, archival material — were being
classified from the resized image rather than the original, so--maxcould
flip an image's content type and route it to lossy AVIF. One 45 KB halftone
came back at 845 KB, 18.5 times larger and visibly degraded, through the
defaultwebpath with no flags. Content is now judged from the source image,
so the same file is classified the same way at every size. - Photographs are no longer mistaken for graphics. A black-and-white frame
or a photo whose metadata had been stripped could trip the palette-graphic
test and come out as a lossless file many times larger than a correct lossy
encode. High-detail images are now always treated as photographs. buildcan run the recipes crustyimg ships with. A manifest target bound
toweb,galleryorproductfailed outright withunknown operation 'optimize'. It now runs them, picking the output format the same way
apply --recipedoes — or honouring the format when the target's name
template names one.- The wasm
transform()binding can run them too. The same defect on the
browser and npm surface: handingtransform()a bundledweb,galleryor
productrecipe failed withunknown operation 'optimize', although the
README points readers at exactly that. It now runs the recipe and encodes to
the format the caller asked for. The browser demo was never affected — it
builds its own recipe — and a recipe without the terminaloptimizestep,
which is the shape the demo sends, produces byte-identical output to before. docs/data-model.md's example recipe used operations that do not exist.
Three of its five steps would have failed. Rewritten against the real
operations, and a test now keeps it that way.
Security
- No advisories. Hostile and malformed inputs — truncated, zero-byte,
mislabelled, and decompression bombs — are now driven against a committed
corpus on every build, on both the CLI and the WebAssembly library. None hangs,
panics or exhausts memory; each produces a clear message and a documented exit
code.
Install crustyimg 0.7.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.7.0/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.7.0/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.7.0
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.6.0 - 2026-07-24
Release Notes
A small release with one behavior change: AVIF now works out of the box. Also
adds published benchmarks and the WebAssembly library on npm.
Added
crustyimg-wasmon npm — the engine compiled to WebAssembly, installable
withnpm install crustyimg-wasm. It runs client-side in the browser or in
Node: no native addon, no postinstall build step, and no dependencies. Same
engine as the CLI, so a recipe behaves the same in both.BENCHMARKS.md— an equal-quality comparison against sharp, ImageMagick,
@squoosh/cli, and cwebp over real photographs, with the machine, the pinned
tool versions, and the exact commands stated so you can check it. Every tool's
output is scored with the same perceptual metric rather than compared at
whatever quality it happened to produce. crustyimg is neither the smallest nor
the fastest, and the document says where it loses and why. Re-run it on your
own images withjust bench-compare --corpus /path/to/photos.
Changed
- AVIF encode is now in the default build. Every distributed binary
(Homebrew, the Releases-page downloads, the shell/PowerShell installers,
and a plaincargo install crustyimg) now includes the AVIF encoder with
no extra flag — previously it needed a--features avifbuild from
source. This is a behavior change:webandoptimizecan now pick
AVIF for lossy-family photos where they couldn't before, so upgrading may
change the output file for the same command and input. A
--no-default-features(lean) build still leaves AVIF out.
Fixed
- The README quoted a median saving of 98%; re-measuring the same corpus with
the same command gives 97%. Corrected.
Install crustyimg 0.6.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.6.0/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.6.0/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.6.0
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.5.0 - 2026-07-20
Release Notes
A large release: a faster, smarter default engine and the web flagship command,
broad new input-format support, a reproducible incremental build system, a
WebAssembly build with a client-side browser demo, and a frozen CLI surface.
This release has breaking CLI changes. While the version is 0.x, a minor bump
may rename or remove commands — see Removed and Changed before upgrading.
Added
web— make an image web-ready in one command: downscale, re-encode to the
smallest modern format that beats the downscaled image (AVIF for photos, lossless
WebP or PNG for graphics), strip metadata, auto-orient, and report an SSIMULACRA2
quality score. Size-insensitive — a 24 MP photo is about as fast as a small one.- Declarative, reusable recipes —
apply --recipewith bundledweb,gallery,
andproductrecipes; tune settings on one image, save the recipe, and replay it
across a batch. The same recipe file runs in the browser via the WebAssembly build. - New input formats, pure Rust and on by default — AVIF decode, SVG rasterize,
and RAW embedded-preview extraction (reads.DNG/.CR2/etc. by pulling the
embedded JPEG preview, not a full RAW develop). HEIC decode is available behind an
off-by-defaultheicfeature (system libheif, local builds only). crustyimg build— a declared, incremental build from acrustyimg.build.toml
manifest, with a content-addressed cache and--watch. A no-change re-run is a full
cache hit that skips every decode/encode;--no-cachebypasses it.optimize --verify— opt in to computing the SSIMULACRA2 score for a run
(added to the JSON report asssim).- Machine-readable output —
--jsonand--timingonoptimize/web/apply,
lint --format json, and a committed offline benchmark (just bench,--corpus). - WebAssembly build — the pure-Rust engine compiles to wasm. A client-side demo
(drop an image, convert to AVIF, read the score — entirely in your browser, nothing
uploaded) runs at https://jysf.github.io/crustyimg/. - CI integration — two GitHub Actions wrap the binary:
jysf/setup-crustyimg
(install in CI) andjysf/crustyimg-action(lint or optimize with inline PR
annotations).
Changed
- The default optimization is now fast and AVIF-aware. The default decision does a
single fixed-quality encode to the smallest modern format that beats the source,
with a first-class "kept, already optimal" passthrough — instead of the slower
quality search that ran by default before. optimizeis now a keep-dimensions byte primitive — best format at good quality,
never larger than the source. The perceptual and byte-budget searches are opt-in via
--target/--ssim/--max-size.- Metadata commands moved into a
metagroup —strip→meta strip,clean→
meta clean,copy-metadata→meta copy,set→meta set. (auto-orientstays
top-level.) - Dependency requirements use caret ranges instead of exact pins, so crustyimg is
friendlier to depend on as a library; reproducible builds come from the committed
Cargo.lock(usecargo install --locked).
Removed
shrink— its behavior is folded intoweb(and upgraded with AVIF).
Fixed
- The metadata write path corrupted numeric EXIF tags (orientation, GPS) on big-endian
input; the writer now preserves the input's byte order. - AVIF decode: a data race under load and an empty-stream abort, both fixed.
web's "never larger than the source" guarantee is now reconciled with the
downscaled baseline.
Security
- Peak decode memory is bounded before allocation by a declared-pixel budget, and a
hardening pass tightened decode limits and error handling.
Install crustyimg 0.5.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.5.0/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.5.0/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.5.0
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.4.0 - 2026-07-06
Release Notes
Image linting (PROJ-004). crustyimg lint is a format-aware, no-URL,
per-file CI linter for an image asset tree ("clippy for image assets"): it flags
privacy / format / size / colorspace problems, names a runnable crustyimg fix
for each, and exits 7 on an error finding. Plus a GitHub Actions on-ramp. Zero
new default dependencies; the default build stays pure-Rust / zero-system-deps.
Added
crustyimg lint [PATHS]…— a read-only, advisory image-asset linter:
source-resolution fan-out (globs / dirs / files, non-images skipped), a
Rule/Finding/Severityframework, grouped-by-file output, and
CI-native exit codes (0clean ·7error finding ·2usage ·3no
inputs) reusing the exit-7CheckFailedgate.- Rule catalog (each names a runnable fix):
privacy/gps-metadata-leak,
privacy/camera-metadata,orient/orientation-not-baked,
size/oversized-bytes,size/truncated-or-corrupt,
dims/oversized-dimensions,color/wrong-colorspace,color/missing-icc,
color/unexpected-icc,format/animated-gif. .crustyimg-lint.tomlconfig (auto-discovered): ruff-style
select/ignore+per-file-ignores, eslint-style per-rule severity,
per-glob[[budget]], and a savings threshold; plus the CLI flags
--config/--no-config/--select/--ignore/--max-warnings/
--max-intended-width/--savings-threshold.lint --format json|sarif— hand-rolled reports, no new dependency: a
stablecrustyimg.lint/v1JSON report and SARIF 2.1.0 for GitHub
code-scanning (github/codeql-action/upload-sarif).- CI on-ramp: the
setup-crustyimg
andcrustyimg-actionGitHub
Actions (their own repos), a.pre-commit-hooks.yamlhook, and a
just lint-imagesrecipe — drop image linting into any CI in three lines.
Install crustyimg 0.4.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.4.0/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.4.0/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.4.0
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.3.1 - 2026-07-06
Release Notes
Dependency-hygiene patch: no user-facing behavior change.
Security
- Bumped
crossbeam-epoch0.9.18 → 0.9.20 to clear RUSTSEC-2026-0204 — an
invalid pointer dereference in itsfmt::Displayimpl forAtomic/Shared
on a null pointer. It reaches crustyimg only as a deep transitive of
rayon/ravifand is neverDisplay-formatted, so real exposure was nil;
this restores a greencargo denysupply-chain gate.
Install crustyimg 0.3.1
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.3.1/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.3.1/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.3.1
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.3.0 - 2026-07-06
Release Notes
The optimization engine (PROJ-002). optimize now looks at the image and picks
the best output format for you — the "local f_auto" — and explains why. Built
on a new shared image-analysis layer. Zero new default dependencies; the default
build stays pure-Rust / zero-system-deps.
Added
optimizeauto-decides the output format (the "localf_auto"). With no
--format, it analyzes the image, shortlists up to three candidate formats,
drives the existing SSIMULACRA2 perceptual search (or the--max-sizebyte
budget) across them, and ships the smallest artifact that beats the source —
never a larger file (SPEC-048).optimize --profile <web|docs|preserve>selects the bias:web(default)
auto-picks the format;docswidens the lossless/crisp-text bias;preserve
keeps the input's format (the previous behavior) (SPEC-048).optimize --explain/--explain=jsonprint an auditable trace of the
decision — detected features, class, every candidate tried
(format/quality/bytes/met-target), the winner, and the savings — human-readable
to stderr or JSON to stdout (schemacrustyimg.optimize.explain/v1) (SPEC-049).- A new internal image-analysis layer (
src/analysis/): a computed-once
Analysiscontext (histogram, entropy, edge density, alpha coverage, capped
unique-color count, dominant color) plus deterministic, no-ML classification
(photograph / graphic-logo / icon / document / ui-screenshot) that biases the
format decision (SPEC-046, SPEC-047).
Changed
optimize's default now auto-decides the output format instead of
preserving the input format — e.g. a photographic PNG may be shipped as a
smaller JPEG or WebP. Pass--profile preserve(or pin--format/-o <ext>) for the previous format-preserving behavior. The chosen format and
savings are always reported on stderr (silence with--quiet). Breaking
for scripts that relied onoptimizekeeping the input format.
Notes
- AVIF appears as an auto-decision candidate only in
--max-size(byte-budget)
mode and only when built with--features avif— it has no decoder, so it
cannot be perceptually scored (DEC-020). - Indexed/lossy-PNG output is still deferred (it needs a permissive quantizer);
few-color graphics use lossless WebP in the interim.
Install crustyimg 0.3.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.3.0/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.3.0/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.3.0
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.2.1 - 2026-07-05
Release Notes
Maintenance release: dependency currency + a scheduled advisory audit. No
user-facing behavior change.
Changed
- Updated the
fast_image_resizeSIMD resize backend 5.5.0 → 6.0.0 and
indicatif0.18.4 → 0.18.6. Resize output and behavior are unchanged
(PATCH-003).
Security
- Added a weekly scheduled
cargo-denyadvisory audit
(.github/workflows/scheduled-audit.yml) so newly-published RustSec
advisories against existing dependencies are caught between commits, not just
on push (PATCH-003).
Install crustyimg 0.2.1
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.2.1/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.2.1/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.2.1
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.2.0 - 2026-07-05
Release Notes
Dependency-hygiene release. The advisory-bearing dependencies behind the three
accepted deny.toml ignores were eliminated at the source (behavior-preserving
swaps), and the --help text was cleaned up for end users. cargo deny now carries
a single documented residual ignore, down from three.
Changed
--helptext now reads for end users: internal stage/spec/decision references
and stale "stub"/"placeholder" wording were removed from command and option
descriptions (PATCH-002).- Text-watermark glyph rasterization now uses
skrifa+zeno(the Google
fontationsstack) instead ofab_glyph. Behavior-preserving — same rendered
output, minus legacykern-table kerning (a nil change for the bundled font)
(SPEC-044). - EXIF writing (
set,clean --gps) now uses an in-house binary TIFF-IFD writer
instead oflittle_exif. Behavior-preserving, and the parser is hardened against
malformed/untrusted EXIF (bounds-checked, no panics) (SPEC-045).
Removed
- Dropped the
ab_glyph,ttf-parser,little_exif,quick-xml, andbrotli
dependencies from the tree.
Security
- Eliminated three
deny.tomladvisory ignores at the source:
RUSTSEC-2026-0192 (ttf-parser, unmaintained) via theskrifa+zenoswap,
and RUSTSEC-2026-0194 / -0195 (quick-xmlmemory-DoS) via the in-house
EXIF writer. One documented ignore remains — RUSTSEC-2024-0436 (paste, an
unmaintained build-time proc-macro reached only viarav1e/avif; no upstream
fix; revisit whenrav1edropspaste).
Install crustyimg 0.2.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.2.0/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.2.0/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.2.0
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.1.1 - 2026-07-04
Release Notes
Fixed
--out-dirnow creates the target directory (and parents) if missing,
consistently across all batch commands; genuine creation failures return a
clear error. Output-name path/symlink guards unchanged (DEC-035).
Install crustyimg 0.1.1
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.1.1/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.1.1/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.1.1
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.1.0 - 2026-07-03
Release Notes
This is the initial MVP release: a single static Rust binary that turns image
prep from guess-a-quality-knob to declare-an-intent. Zero system dependencies
by default; all formats handled in pure Rust.
Added
Inspect and view
view <INPUT>— display an image directly in the terminal viaviuer(the
displayfeature is on by default; headless builds omit it with
--no-default-features).info <INPUT> [--exif] [--json]— print dimensions, format, file size on disk,
color type, bit depth, alpha presence, and ICC/EXIF presence.--exifdumps EXIF
tags;--jsonemits machine-readable output to stdout.
Geometry / transform
resize <INPUT...>— resize with a SIMD backend in six modes:--max(long-edge
bound, never upscales),--exact WxH,--percent P,--fit WxH(letterbox,
never upscales),--cover WxH(fill box, may upscale),--fill WxH
(cover + center-crop to exact dimensions). Batch-ready with--out-dir.thumbnail <INPUT...> [--size N] [--square]— convenience resize: bounds the
longest edge to N (default 256), or produces an exact N×N square via cover +
center-crop.shrink <INPUT...>— optimize for web: resize to a long-edge bound (default 1600),
re-encode at quality 80, drop metadata. Accepts--target visually-lossless|high|mediumor--ssim <0-100>for perceptual auto-quality
(binary-searches SSIMULACRA2; see below), or--max-size <KB/MB>for a byte
budget with automatic dimension-reduction fallback.convert <INPUT...> --format FMT— pure re-encode to another format with no pixel
transform. Supports all core formats plus WebP (default build) and AVIF (opt-in
feature).--max-size <SIZE>fits the output under a byte budget for every format.auto-orient <INPUT...>— bake the EXIF orientation into pixels and clear the tag,
fixing the common silent-rotation bug. A no-op when no orientation tag is present.
Optimize, diff, and responsive web delivery (STAGE-009)
optimize <INPUT...>— one-command web prep: auto-orient + strip metadata +
perceptual visually-lossless re-encode in a single pass. The "just make this
web-good" default.diff <A> <B> [--fail-under N] [--json]— compute an SSIMULACRA2 perceptual
similarity score between two images.--fail-under Nturns it into a CI
visual-regression gate: a score below N exits with code 7 (distinct from a
runtime error), so CI can tell "regression detected" from "couldn't run".responsive <INPUT> --widths W1,W2,… --out-dir DIR [--formats …]— generate a
width × format responsive image set and print a paste-ready<picture>/srcset
HTML snippet to stdout.
Perceptual auto-quality and byte budgets (STAGE-008)
- SSIMULACRA2-driven quality search: binary-searches the encoder quality against a
perceptual target — the smallest file that still clears a visual quality level.
Available via--target visually-lossless|high|mediumor--ssim <score>on
shrinkandoptimize. - Byte-budget mode (
--max-size <SIZE>) onshrinkandconvert: lowers quality
first, then progressively downscales dimensions as a fallback when quality alone
cannot meet the budget. Works for every output format, including lossless ones. - Modern formats: WebP is the pure-Rust default (lossless; lossy via opt-in
webp-lossyfeature); AVIF is a pure-Rust opt-in feature (--features avif,
viaravif). Both are available as output targets for all quality/budget modes.
Compositing and text overlays (STAGE-004 / SPEC-029-030)
watermark <INPUT...> --image LOGO [--gravity G] [--opacity O] [--scale S] [--margin M] [--tile]— overlay an image watermark at a compass gravity anchor
(defaultsoutheast); supports tiling, opacity, and proportional scaling.watermark <INPUT...> --text STRING [--font PATH] [--size N] [--color HEX]—
rasterize text (viaab_glyph) and composite it as an overlay. Default font is
the bundled BSD-3 Go font.
Metadata lane — container-level ops, no pixel re-encode (STAGE-004)
All four commands operate on the image container directly — pixels are never
re-decoded, so privacy ops carry no quality cost and no recompression.
strip <INPUT...>— remove all container metadata (EXIF/IPTC/XMP/ICC). Supports
JPEG and PNG.clean <INPUT...> --gps— selectively remove GPS/location tags while preserving
all other metadata (orientation, copyright, ICC). Supports JPEG and PNG.set <INPUT...> [--artist S] [--copyright S] [--description S]— write named
EXIF tags, creating a fresh EXIF block when the input has none.copy-metadata --from SRC --to DST— copy EXIF + ICC from one image onto another
without touching pixels or XMP. JPEG only in v1.- Default drop-GPS policy on all pixel-lane encodes (
--keep-gpsto opt out).
Recipes and parallel batch (STAGE-005)
edit <INPUT> [--auto-orient] [--resize-max N] [--invert] [--save-recipe FILE]—
chain an ordered op list on a single image in one decode→ops→encode pass. Ops
apply in a fixed canonical order regardless of flag order, so the result is
deterministic.--save-recipe FILEserializes the chain to a TOML recipe.apply --recipe FILE <INPUT...> [--out-dir DIR] [-j N]— replay a saved recipe
across a file, glob, or directory in parallel (rayon,-j Nbounds workers) with
anindicatifprogress bar on stderr. The recipe that tuned one image runs
unchanged across thousands. Per-input failures are summarized and exit with
code 6; other inputs still write.- Recipe round-trip is byte-pinned:
editoutput equalsapply-of-the-saved-recipe
output on the same input.
Global options and exit-code contract
- Global flags across all subcommands:
--output / -o,--out-dir,--format,
--quality / -q,--verbose / -v,--quiet / -Q,--yes / -y,--keep-gps,
--jobs / -j. - Stdin/stdout piping:
-as input or output keeps diagnostic output on stderr so
pipes stay clean. - Typed exit codes: 0 success, 1 runtime error, 2 usage error, 3 input not found,
4 unsupported format/codec not built, 5 output write refused, 6 partial batch
failure, 7 check/gate not satisfied. completions <bash|zsh|fish|powershell|elvish>— print a clap-generated shell
completion script to stdout (e.g.crustyimg completions zsh > _crustyimg).
Security
- Decode resource limits — every image load is bounded via
image::Limits:
per-dimension ≤ 65 535 px, decoded allocation ≤ 512 MiB. Decompression bombs and
forged-dimension inputs are rejected with a typed error (exit 1) before pixels are
produced; never a panic or OOM. - Recipe resource limits — untrusted recipe files over 64 KiB or with more than
1024 steps are rejected before being read into memory. - Resize output cap — a resize whose output buffer would exceed 512 MiB (upscale
bomb via exact/percent/cover/fill, from CLI or recipe) is rejected before
allocation. - Path and symlink guards —
.., separator characters, and absolute paths in
output names are rejected; symlinked destinations are refused even with--yes. - Supply-chain CI —
cargo deny check(advisories, bans, sources, licenses)
runs in CI on every push. - A recorded threat model (
SECURITY.md) maps each untrusted-input surface to its
mitigation and the spec/decision that built it; an adversarial review over the
cumulative diff found no unresolved finding.
Install crustyimg 0.1.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jysf/crustyimg/releases/download/v0.1.0/crustyimg-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/jysf/crustyimg/releases/download/v0.1.0/crustyimg-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install jysf/tap/crustyimgDownload crustyimg 0.1.0
| File | Platform | Checksum |
|---|---|---|
| crustyimg-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| crustyimg-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| crustyimg-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| crustyimg-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |