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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- *(image)* **breaking**: PNG decode migrated to gamut-png (pure Rust; every
colour type and bit depth, incl. Adam7 interlace and 16-bit sources decoded
natively). `DecodeOptions::PngZune(ZunePngDecodeConfig)` becomes
`DecodeOptions::Png(PngDecodeConfig)` (codec id `png/zune` → `png/gamut`)
exposing gamut's hostile-input resource guards
(`max_width`/`max_height`/`max_image_bytes`/`max_metadata_bytes`); the old
`confirm_crc`/`strict` knobs are gone — critical-chunk CRCs and spec
conformance are now always enforced (ancillary chunks with bad CRCs are
skipped per spec, never errors). PNG metadata reads now also surface ICC
(iCCP) and XMP (iTXt) alongside EXIF via `gamut_png::PngDecoder::decode`,
and require the `png-decode`/`png-encode` feature (previously the bare
`exif` feature sufficed for the eXIf scan).
- *(image)* **breaking**: JPEG migrated to gamut-jpeg (pure Rust, baseline +
progressive both ways). Decode replaces `zune-jpeg` (`DecodeOptions::Jpeg`,
codec id `jpeg/gamut`; CMYK/YCCK conversion is bit-identical to the previous
Expand All @@ -30,6 +42,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Removed

- *(image)* **breaking**: the `png-decode-zune` feature and the `zune-png`
dependency; `ExifContainer::Png` and the hand-rolled PNG eXIf chunk scanner
(`zune-core` stays: rawshift's PPM decode path uses its codec primitives,
and `zune-ppm` is a permanent exception to the gamut migration).
- *(image)* **breaking**: the `jpeg-decode-zune`, `jpeg-encode-jpeg-enc`,
`jpeg-encode-jpegli`, `jpeg-encode-jpegli-vendored`, and `container-embed`
features; the `zune-jpeg`, `jpeg-encoder`, and `img-parts` dependencies; the
Expand Down
20 changes: 0 additions & 20 deletions Cargo.lock

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

41 changes: 23 additions & 18 deletions crates/rawshift-image/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ gamut-ifd = { workspace = true, optional = true, features = ["bigtiff"] }
# uncompressed LinearRaw DNGs today.
gamut-dng = { workspace = true, optional = true }
gamut-metadata = { workspace = true, optional = true }
# gamut-png backs PNG encode (PngEncoder: 8/16-bit RGB, DEFLATE level + filter
# gamut-png backs PNG decode (PngDecoder: every colour type and bit depth,
# Adam7 interlace, eXIf/iCCP/XMP chunk extraction, hostile-input resource
# guards) and encode (PngEncoder: 8/16-bit RGB, DEFLATE level + filter
# strategy + lossless auto-reduce, and eXIf/iCCP/XMP-iTXt chunk embedding).
# PNG decode stays on zune-png until the gamut decoder migration (#32).
gamut-png = { workspace = true, optional = true }
# gamut-avif backs AVIF encode (AvifEncoder: 8-bit RGB, lossless or lossy AV1
# intra at identity 4:4:4 — pure Rust, no C toolchain). 10/12-bit and alpha
Expand Down Expand Up @@ -83,7 +84,6 @@ serde = { workspace = true, optional = true }
thiserror = { workspace = true }
tracing = { workspace = true }
zune-core = { version = "0.5", optional = true }
zune-png = { version = "0.5", optional = true }
zune-ppm = { version = "0.5", optional = true }
gif = { version = "0.13", optional = true }
tiff = { version = "0.11", optional = true }
Expand Down Expand Up @@ -117,12 +117,13 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# tier 1 bundles full, experimental, raw-stabilizing, …
# tier 2 format jpeg, png, dng, … (decode + encode)
# tier 3 direction jpeg-decode, dng-encode, … (default impl alias)
# tier 4 implementation png-decode-zune, … (compressed only)
# tier 4 implementation ppm-decode-zune, … (compressed only)
# tier 5 infrastructure ifd-parser, serde, zune-runtime, exif, …
#
# Only tier-4 features (plus RAW tier-3 features and the gamut-backed
# `jpeg-decode` / `jpeg-encode` / `png-encode` / `jxl-decode` / `jxl-encode` /
# `avif-encode`, which have a single implementation) reference `dep:*`.
# `jpeg-decode` / `jpeg-encode` / `png-decode` / `png-encode` / `jxl-decode` /
# `jxl-encode` / `avif-encode`, which have a single implementation) reference
# `dep:*`.
default = ["jpeg", "png", "webp", "jxl-decode", "gif-decode", "tiff-decode", "ppm-decode"]

# ── Tier 1: bundle features ───────────────────────────────────────────────────
Expand Down Expand Up @@ -156,23 +157,27 @@ raf = ["raf-decode"]
# format+direction's DEFAULT implementation (tier 4) — this is where the notion
# of a per-format default lives. RAW direction features reference infra/deps
# directly: RAW formats have a single in-repo implementation and no tier-4 layer.
# JPEG is backed directly by gamut-jpeg (no tier-4 layer): like `png-encode`
# JPEG is backed directly by gamut-jpeg (no tier-4 layer): like PNG
# and the RAW direction features, each half has a single gamut-backed
# implementation, so the format-direction feature itself pulls the dependency.
# Decode covers baseline + progressive streams (grayscale/YCbCr/RGB/CMYK/YCCK);
# encode writes baseline or progressive 8-bit DCT JPEG. `exif` supplies the
# EXIF builder/parser for the APP1 segment gamut-jpeg reads and writes.
jpeg-decode = ["dep:gamut-jpeg", "exif"]
jpeg-encode = ["dep:gamut-jpeg", "exif"]
png-decode = ["png-decode-zune"]
# PNG encode is backed directly by gamut-png (no tier-4 layer): like the RAW
# direction features, it has a single gamut-backed implementation, so the
# format-direction feature itself pulls the dependency. `exif` supplies the
# ExifBuilder used to serialise the eXIf chunk payload.
# PNG is backed directly by gamut-png (no tier-4 layer): like JPEG and the RAW
# direction features, each half has a single gamut-backed implementation, so
# the format-direction feature itself pulls the dependency. Decode covers
# every PNG colour type and bit depth (incl. Adam7 interlace) with
# hostile-input resource guards, and surfaces eXIf/iCCP/XMP chunk metadata;
# `exif` supplies the parser that maps the eXIf payload into `ImageMetadata`.
png-decode = ["dep:gamut-png", "exif"]
# Encode: `exif` supplies the ExifBuilder used to serialise the eXIf chunk
# payload.
png-encode = ["dep:gamut-png", "exif"]
webp-decode = ["webp-decode-libwebp"]
webp-encode = ["webp-encode-libwebp"]
# JXL is backed directly by gamut-jxl (no tier-4 layer): like `png-encode` and
# JXL is backed directly by gamut-jxl (no tier-4 layer): like PNG and
# the RAW direction features, each half has a single gamut-backed
# implementation, so the format-direction feature itself pulls the dependency.
# Decode wraps the pure-Rust jxl-rs decoder; encode wraps the reference libjxl
Expand All @@ -184,7 +189,7 @@ gif-decode = ["gif-decode-gif"]
tiff-decode = ["tiff-decode-tiff"]
avif-decode = ["avif-decode-image"]
# AVIF encode is backed directly by gamut-avif (no tier-4 layer): like
# `png-encode` and the RAW direction features, it has a single gamut-backed
# PNG and the RAW direction features, it has a single gamut-backed
# implementation, so the format-direction feature itself pulls the dependency.
# Pure Rust (gamut-av1 AV1 intra + gamut-isobmff container) — 8-bit RGB,
# lossless or lossy; 10/12-bit output is pending justin13888/gamut#251.
Expand Down Expand Up @@ -219,9 +224,8 @@ raf-decode = []
# standalone. Multiple implementations of the same format+direction may be
# enabled at once; the active backend is chosen at the API level — see
# `DecodeOptions` and `EncodeOptions`.
# `zune-png` builds on the `zune-core` codec primitives, so each zune-backed
# impl feature also pulls `zune-runtime`.
png-decode-zune = ["png-decode", "dep:zune-png", "zune-runtime", "exif"]
# `zune-ppm` builds on the `zune-core` codec primitives, so its impl feature
# also pulls `zune-runtime`.
webp-decode-libwebp = ["webp-decode", "dep:libwebp-sys", "exif"]
webp-encode-libwebp = ["webp-encode", "dep:libwebp-sys", "exif"]
gif-decode-gif = ["gif-decode", "dep:gif"]
Expand All @@ -236,7 +240,8 @@ ppm-decode-zune = ["ppm-decode", "dep:zune-ppm", "zune-runtime"]
ifd-parser = ["dep:gamut-ifd"]
serde = ["dep:serde", "rawshift-core/serde"]
# `zune-core` codec primitives (bit depth, colorspace, decoder/encoder option
# builders). Pulled by every zune-backed decode/encode impl feature.
# builders). Pulled by the zune-backed PPM impl feature (`zune-ppm` is a
# permanent exception to the gamut migration — see AGENTS.md).
zune-runtime = ["dep:zune-core"]
# Typed EXIF read/write via the gamut metadata stack (`gamut-exif` for the
# blob model, `gamut-metadata` + `gamut-xmp` for the unified-model bridge).
Expand Down
15 changes: 7 additions & 8 deletions crates/rawshift-image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ or an explicit hardware-decode backend pin (`hw-*`).
| Nikon NEF | Custom TIFF parser (Incomplete) | N/A | No test fixtures. |
| Fujifilm RAF | Custom RAF parser (Incomplete) | N/A | No test fixtures. |
| JPEG | [gamut-jpeg](https://github.com/justin13888/gamut) (Stable) | [gamut-jpeg](https://github.com/justin13888/gamut) (Stable) | Pure Rust. Decode: baseline + progressive, grayscale/YCbCr/RGB/CMYK/YCCK. Encode: baseline or progressive 8-bit DCT (quality/subsampling/restart/density); APP1/APP2 EXIF/XMP/ICC both ways. |
| PNG | [zune-png](https://github.com/etemesi254/zune-image/tree/dev/crates/zune-png) (Stable) | [gamut-png](https://github.com/justin13888/gamut) (Stable) | Encode via gamut (8/16-bit RGB, eXIf/iCCP/XMP chunks). |
| PNG | [gamut-png](https://github.com/justin13888/gamut) (Stable) | [gamut-png](https://github.com/justin13888/gamut) (Stable) | Pure Rust. Decode: every colour type/bit depth incl. Adam7, eXIf/iCCP/XMP extraction, resource guards. Encode: 8/16-bit RGB, eXIf/iCCP/XMP chunks. |
| WebP | [libwebp-sys](https://github.com/noxf/libwebp-sys) (Stable) | [libwebp-sys](https://github.com/noxf/libwebp-sys) (Stable) | C FFI bindings to libwebp. |
| GIF | [gif](https://github.com/image-rs/image-gif) (Stable) | Not planned | |
| TIFF | [tiff](https://github.com/image-rs/image-tiff) (Stable) | Not planned | |
Expand All @@ -46,8 +46,8 @@ implementations are named and selected.
Cargo features are organised in five tiers, from high-level bundles down to
individual library bindings. Each tier is defined purely in terms of the tier
below it; only tier-4 features (plus RAW tier-3 features and the gamut-backed
`jpeg-decode` / `jpeg-encode` / `png-encode` / `jxl-decode` / `jxl-encode` /
`avif-encode`) pull in an external crate.
`jpeg-decode` / `jpeg-encode` / `png-decode` / `png-encode` / `jxl-decode` /
`jxl-encode` / `avif-encode`) pull in an external crate.

1. **Bundle features** — coarse, ready-made groupings.
- `default` — `jpeg`, `png`, `webp`, `jxl-decode`, `gif-decode`, `tiff-decode`, `ppm-decode`.
Expand All @@ -67,8 +67,8 @@ below it; only tier-4 features (plus RAW tier-3 features and the gamut-backed
`ppm-decode` — each is an **alias for that format+direction's default
implementation**.
This is where the per-format default is defined. Exception: `jpeg-decode`,
`jpeg-encode`, `png-encode`, `jxl-decode`, `jxl-encode`, `avif-encode`,
and `heic-decode` each have a single gamut-backed implementation
`jpeg-encode`, `png-decode`, `png-encode`, `jxl-decode`, `jxl-encode`,
`avif-encode`, and `heic-decode` each have a single gamut-backed implementation
(`gamut-jpeg` / `gamut-png` / `gamut-jxl` / `gamut-avif` / `gamut-heic`)
and pull it directly, with no tier-4 layer
below them. (`jxl-encode` wraps the
Expand All @@ -85,7 +85,6 @@ below it; only tier-4 features (plus RAW tier-3 features and the gamut-backed
only tier that pulls an external crate. Multiple implementations of the same
format+direction may be enabled simultaneously; the active backend is chosen
at the API level via `DecodeOptions` / `EncodeOptions`.
- `png-decode-zune`
- `webp-decode-libwebp`, `webp-encode-libwebp`
- `gif-decode-gif`, `tiff-decode-tiff`
- `avif-decode-image`
Expand All @@ -111,8 +110,8 @@ below it; only tier-4 features (plus RAW tier-3 features and the gamut-backed
format implementations that need them — they exist so that a minimal
`rawshift-image` build links no decoder/metadata crate it does not use.

Resolution example: enabling `default` pulls in `png` → `png-decode` →
`png-decode-zune` → the `zune-png` crate. To use a non-default implementation,
Resolution example: enabling `default` pulls in `ppm` → `ppm-decode` →
`ppm-decode-zune` → the `zune-ppm` crate. To use a non-default implementation,
enable its tier-4 feature explicitly and select it per call through
`DecodeOptions` / `EncodeOptions`; the default implementation stays available
alongside it.
Expand Down
4 changes: 2 additions & 2 deletions crates/rawshift-image/src/formats/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ pub use heic::{HeicAuxImage, HeicAuxKind, HeicFile, heic_hw_decode_available};
pub use registry::{available_decoders, available_encoders};
pub use standard::{
DecodeOptions, GifDecodeConfig, HeicDecodeConfig, ImageAvifDecodeConfig, ImageProbe,
JpegDecodeConfig, JxlDecodeConfig, LibwebpDecodeConfig, ResvgDecodeConfig, StandardFormat,
TiffDecodeConfig, ZunePngDecodeConfig, ZunePpmDecodeConfig, decode_standard_image,
JpegDecodeConfig, JxlDecodeConfig, LibwebpDecodeConfig, PngDecodeConfig, ResvgDecodeConfig,
StandardFormat, TiffDecodeConfig, ZunePpmDecodeConfig, decode_standard_image,
decode_standard_image_with, detect_standard_format, probe_standard_image,
read_standard_image_metadata,
};
Expand Down
6 changes: 4 additions & 2 deletions crates/rawshift-image/src/formats/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,12 @@ pub fn available_decoders() -> Vec<CodecInfo> {
"0.1",
CodecDirection::Decode,
));
// Version tracks the gamut-png crate at the pinned gamut commit (git
// dependency). Hand-maintained — bump together with the gamut pin.
#[cfg(feature = "png-decode")]
decoders.push(CodecInfo::new(
CodecId::new("png/zune"),
"0.5",
CodecId::new("png/gamut"),
"0.1",
CodecDirection::Decode,
));
#[cfg(feature = "webp-decode")]
Expand Down
Loading