Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

## 0.3.0

### ⚠️ BREAKING CHANGES

* **codecs:** `sqzer_codecs::png::PngEncoder`, the plain `png` writer, is removed. `sqzer_codecs::oxipng::OxipngEncoder` writes PNG on every target; effort 0 is its fastest setting
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Guidance for Claude Code working in this repository. Read `docs/adr/0001-system-

## What this is

`sqzer` is a multi-format image optimizer: a Rust library, a CLI and later a WASM package. The goal is Squoosh-quality defaults as a scriptable tool. Pure Rust by default, C codecs opt-in, AGPL codecs never in the library or default builds.
`sqzer` is a multi-format image optimizer: a Rust library, a CLI and a WASM package, `sqzer` on npm. The goal is Squoosh-quality defaults as a scriptable tool. Pure Rust by default, C codecs opt-in, AGPL codecs never in the library or default builds.

Solo-maintained. Prefer fewer, well-chosen dependencies over coverage. Never write a decoder, encoder, resampler, colour transform or metric; mature crates exist for each and the job here is the container, the traits and the pipeline around them.

Expand Down
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ members = ["crates/*"]
exclude = ["tools/calibrate"]

[workspace.package]
version = "0.2.0"
version = "0.3.0"
edition = "2024"
# 1.92 is what `gamut-jxl` (the `native-jxl` backend) asks for; the
# portable tier itself needs nothing newer than 1.89.
Expand All @@ -21,19 +21,19 @@ keywords = ["image", "optimization", "avif", "jpegxl", "webp"]
categories = ["multimedia::images", "command-line-utilities"]

[workspace.dependencies]
sqzer-core = { path = "crates/sqzer-core", version = "0.2.0" }
sqzer-codecs = { path = "crates/sqzer-codecs", version = "0.2.0", default-features = false }
sqzer-metrics = { path = "crates/sqzer-metrics", version = "0.2.0" }
sqzer = { path = "crates/sqzer", version = "0.2.0", default-features = false }
sqzer-core = { path = "crates/sqzer-core", version = "0.3.0" }
sqzer-codecs = { path = "crates/sqzer-codecs", version = "0.3.0", default-features = false }
sqzer-metrics = { path = "crates/sqzer-metrics", version = "0.3.0" }
sqzer = { path = "crates/sqzer", version = "0.3.0", default-features = false }
# The three crates that hold the workspace's `unsafe`, one per HEIC
# decoder (ADR-0005 D4). Not public API; published because `sqzer-codecs`
# depends on them.
heif-dl = { path = "crates/heif-dl", version = "0.2.0" }
heif-imageio = { path = "crates/heif-imageio", version = "0.2.0" }
heif-wic = { path = "crates/heif-wic", version = "0.2.0" }
heif-dl = { path = "crates/heif-dl", version = "0.3.0" }
heif-imageio = { path = "crates/heif-imageio", version = "0.3.0" }
heif-wic = { path = "crates/heif-wic", version = "0.3.0" }
# The per-target selection behind `sqzer/native` (ADR-0006). Not public
# API; published because `sqzer` depends on it.
sqzer-native-tier = { path = "crates/sqzer-native-tier", version = "0.2.0" }
sqzer-native-tier = { path = "crates/sqzer-native-tier", version = "0.3.0" }

[workspace.lints.rust]
unsafe_code = "forbid"
Expand Down
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# sqzer

Multi-format image optimizer with best-in-class defaults. A library and a CLI, pure Rust by default, C codecs when you want the last few percent.
Multi-format image optimizer with best-in-class defaults. A library, a CLI and a WebAssembly package for the browser, pure Rust by default, C codecs when you want the last few percent.

> **Note**: Early days. The command line and its defaults are what this README describes; the library API will still change before `1.0`, and the browser build is not there yet. The design is in [`docs/adr/0001-system-design.md`](docs/adr/0001-system-design.md), the command line in [`docs/adr/0003-cli-interface.md`](docs/adr/0003-cli-interface.md), the native backends in [`docs/adr/0004-native-tier.md`](docs/adr/0004-native-tier.md), colour in [`docs/adr/0007-colour-management.md`](docs/adr/0007-colour-management.md).
> **Note**: Early days. The command line and its defaults are what this README describes; the library API will still change before `1.0`. The browser build is the `sqzer` package on npm, described [below](#in-the-browser); the page to drop an image on is next. The design is in [`docs/adr/0001-system-design.md`](docs/adr/0001-system-design.md), the command line in [`docs/adr/0003-cli-interface.md`](docs/adr/0003-cli-interface.md), the native backends in [`docs/adr/0004-native-tier.md`](docs/adr/0004-native-tier.md), colour in [`docs/adr/0007-colour-management.md`](docs/adr/0007-colour-management.md), the browser build in [`docs/adr/0011-browser-build.md`](docs/adr/0011-browser-build.md).

## What it is for

Expand Down Expand Up @@ -161,6 +161,23 @@ rimage avif in.jpg sqzer -f avif in.jpg

> **Note**: `rimage` scales an image up to the size asked for unless `--no-upscale` is given, which is why the lines above carry `--enlarge`. Most runs only mean to shrink, and for those `--enlarge` can go.

## In the browser

The same pipeline and defaults as a WebAssembly package, `sqzer` on npm: the portable tier minus SVG, which the browser's canvas rasterises instead.

```js
// npm install sqzer
import init, { optimize } from "sqzer";
await init();

// AVIF for a photograph, lossless WebP for a graphic, searched to a score of 70
const out = optimize(bytes);
// the command line's flags in camel case
const avif = optimize(bytes, { format: "avif", target: 60, width: 1600 });
```

It is synchronous and single-threaded, so a page runs it in a Web Worker. The options, `decode` once and `encode` many times, `decodeAny` for SVG and HEIC, the result record and the errors are in [`crates/sqzer-wasm/README.md`](crates/sqzer-wasm/README.md), which is the package's README on npm.

## Formats

Decode: JPEG, PNG, WebP, AVIF, JPEG XL, GIF, TIFF, BMP, TGA, ICO, QOI, PNM, OpenEXR, SVG. HEIC behind a feature. OpenEXR is read as linear light in its own primaries, rotated to sRGB and encoded to 16-bit sRGB with anything above display white clipped; there is no tone mapping.
Expand Down Expand Up @@ -205,7 +222,7 @@ crates/sqzer-codecs every backend behind a feature flag
crates/sqzer-metrics SSIMULACRA2 and the target-quality search
crates/sqzer library facade, the thing you depend on
crates/sqzer-cli the binary, `sqzer`
crates/sqzer-wasm browser build: the JavaScript API, portable tier minus SVG. On npm with 0.3
crates/sqzer-wasm browser build: the JavaScript API, portable tier minus SVG. `sqzer` on npm
crates/sqzer-native-tier what `native` means per target, no code
docs/adr design decisions
dist-workspace.toml the release matrix for `cargo-dist`
Expand Down
9 changes: 4 additions & 5 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ What comes next for `sqzer`, roughly in order. `sqzer` is maintained by one pers

Want to help with an item? Open an issue or a discussion first, see [`CONTRIBUTING.md`](CONTRIBUTING.md).

## 0.3: in the browser
## The page at `sqzer.dev`

The "Squoosh replacement" is not complete without a page you can drop an image on (ADR-0001 item 10).
The "Squoosh replacement" is not complete without a page you can drop an image on (ADR-0001 item 10). The package it runs on shipped with 0.3.0: the portable tier as WebAssembly, `sqzer` on npm ([`docs/adr/0011-browser-build.md`](docs/adr/0011-browser-build.md)).

- `sqzer-wasm`: the portable tier as a WebAssembly package, `sqzer` on npm. The API, the build and how it is published are [`docs/adr/0011-browser-build.md`](docs/adr/0011-browser-build.md).
- A small drag-and-drop page on top of it, at `sqzer.dev`.
- A small drag-and-drop page on top of that package, at `sqzer.dev`, in a repository of its own, `sqzer-dev/sqzer.dev`.

> **Note**: The browser build inherits the portable tier's licence gaps: lossy WebP and JPEG XL cannot be written. Everything else the portable tier does, it does on `wasm32` too, AVIF decoding and `oxipng` included (ADR-0011).

Expand All @@ -25,7 +24,7 @@ Palette reduction for PNG, `rimage`'s `--quantization`, as a codec option:

## A full build in the browser

After 0.3, a second browser build over `sqzer-codecs-agpl` that fills the portable tier's gaps with imazen's pure-Rust codecs: lossy WebP (`zenwebp`), JPEG XL encoding, AVIF decoding, HEIC input, and PNG quantisation through `imagequant`. It would power the drag-and-drop page at `sqzer.dev` and make it a full Squoosh replacement.
After the page, a second browser build over `sqzer-codecs-agpl` that fills the portable tier's gaps with imazen's pure-Rust codecs: lossy WebP (`zenwebp`), JPEG XL encoding, AVIF decoding, HEIC input, and PNG quantisation through `imagequant`. It would power the drag-and-drop page at `sqzer.dev` and make it a full Squoosh replacement.

> **Note**: That build is AGPL-3.0, and so is anything that bundles it. It ships as its own package, never as the default `sqzer` package or in the library, which stay permissive. Whether those crates build for `wasm32` is not verified yet; that check and a new ADR come first.

Expand Down
73 changes: 36 additions & 37 deletions tools/calibrate/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading