feat(gainmap): ISO 21496-1 URN + PRIMARY_APP2_BODY + JpegApp2BodyWithUrn/JxlJhgm#15
Merged
feat(gainmap): ISO 21496-1 URN + PRIMARY_APP2_BODY + JpegApp2BodyWithUrn/JxlJhgm#15
Conversation
273fe20 to
2e8b3c7
Compare
2e8b3c7 to
d9470b1
Compare
…Urn/JxlJhgm - Add `Iso21496Format::JxlJhgm` (bare payload, canonical name) and `Iso21496Format::JpegApp2BodyWithUrn` (URN + payload = body of a JPEG APP2 segment after the FF E2 marker and u16 BE length have been stripped). Discriminants pinned with explicit `= 0..3` values plus a `const _: () = assert!(...)` block so future variant changes trip at compile time instead of silently shifting `as u8` results. - Deprecate `Iso21496Format::JpegApp2` — misleading name (the bytes aren't a standalone APP2 body, they lack the URN). Kept at its original discriminant `0` so existing `as u8` casts keep working. It produces and accepts identical bytes to `JxlJhgm` but is a distinct variant (Rust does not allow shared discriminants). - Add `ISO_21496_1_URN: &[u8; 28]` public constant — the ISO-defined namespace URN. Cross-codec; byte-identical to libultrahdr's `kIsoNameSpace`. - Add `ISO_21496_1_PRIMARY_APP2_BODY: &[u8; 32]` public constant — the complete JPEG APP2 body (URN + `min_version=0, writer_version=0`) that the primary image of a canonical Ultra HDR JPEG carries to advertise ISO 21496-1 awareness. One constant emit-and-detect rather than requiring callers to concatenate URN + version bytes themselves. - `parse_iso21496_fmt` / `serialize_iso21496_fmt` now handle all four variants with a single allocation. - Add `serialize_iso21496_fmt_into(params, format, &mut Vec<u8>)` for callers that want to embed the ISO payload in a larger buffer without an intermediate `Vec` (e.g., building a JPEG APP2 marker + length + body in one allocation). - Add `GainMapParseError::UrnMismatch` for URN-aware parsing failures. Rationale: the URN is ISO-defined and belongs next to `Iso21496Format`, which lets us fold URN framing into the enum and collapse the API surface to one parse/serialize pair. Each variant now names the container that consumes exactly those bytes (JxlJhgm, AvifTmap, JpegApp2BodyWithUrn). The `Body` qualifier signals explicitly that the FF E2 marker and u16 BE length envelope are NOT included — those stay with zenjpeg as pure JPEG syntax. `ISO_21496_1_PRIMARY_APP2_BODY` hands callers the full 32 bytes they need rather than a 4-byte `VERSION_SIGNAL` that was only meaningful when concatenated with the URN. Agent survey of zenjpeg/zenavif/zenjxl confirmed no codec needs the version tail in isolation.
d9470b1 to
5e31270
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Promotes the ISO 21496-1 namespace URN and the primary-image signal marker
into
zencodec::gainmap, and restructuresIso21496Formatso each variantnames the container that consumes exactly those bytes.
New format variants
JpegApp2(deprecated)JxlJhgmAvifTmapversion(u8=0) + <JxlJhgm>tmapitem payloadJxlJhgmmin_ver + writer_ver + flags + channel datajhgmbundlegain_map_metadatafieldJpegApp2BodyWithUrnURN(28) + <JxlJhgm>FF E2 + u16 BE length)The
Bodyqualifier onJpegApp2BodyWithUrnsignals explicitly that theFF E2marker andu16 BElength envelope are NOT included — those staywith zenjpeg as pure JPEG syntax.
Discriminants are pinned with explicit
= 0..3values plus aconst _: () = assert!(...)block so future variant changes trip at compile time instead of silently
shifting
as u8results.Deprecation
Iso21496Format::JpegApp2→ misleading (bytes lack the URN). Deprecatedalias for
JxlJhgm, kept at discriminant0soas u8casts keep working.Public constants at
zencodec::gainmap::(re-exported at the crate root)ISO_21496_1_URN: &[u8; 28]— theurn:iso:std:iso:ts:21496:-1\0namespacestring. Byte-identical to libultrahdr's
kIsoNameSpace.ISO_21496_1_PRIMARY_APP2_BODY: &[u8; 32]— the full JPEG APP2 body(
URN + min_version=0 + writer_version=0) that the primary image of acanonical Ultra HDR JPEG carries. Callers memcmp against this to emit or
detect; no need to concatenate URN + version bytes themselves.
GainMapParseError::UrnMismatchadded for URN-aware parsing failures.Agent survey of codec needs
Before settling on the single 32-byte
PRIMARY_APP2_BODYconstant vs. a4-byte
VERSION_SIGNAL+ helper function, I had agents audit zenjpeg,zenavif, and zenjxl. Result:
writer_version, delegatesmin_versioncheck toparse_iso21496_fmtiref/dimgfor signaling; version bytes are always bundled in thetmappayloadgain_map_metadatais an opaque round-trip blobOne codec memcmps the full APP2 body; zero codecs extract version fields in
isolation. The 32-byte constant is what callers actually need.
Single-allocation serialization
Extracted
serialized_size(params, format)plus*_into(params, &mut Vec<u8>)appenders so each public path does exactly one allocation.
JpegApp2BodyWithUrnshares the same buffer as its payload — no intermediate
Vec.Why this and not the full JPEG APP2 marker (FF E2 + length)
That envelope is pure JPEG syntax and belongs in zenjpeg; only the URN/payload
pieces are ISO-defined and cross-codec. This PR is intentionally scoped to
zencodec; a follow-up will DRY up zenjpeg's three copies of the
push 0xFF; push 0xE_; push length_hi; push length_lo;pattern in itsXMP / MPF / ISO APP2 writers and rewire the ISO writers to consume
Iso21496Format::JpegApp2BodyWithUrnandISO_21496_1_PRIMARY_APP2_BODYfrom zencodec directly.
Test plan
cargo test --all-targets(423 + 100 + 29 + 0 pass)cargo test --doc(13 pass)cargo clippy --all-targets -- -D warnings(clean)cargo semver-checks check-release(clean — discriminants pinned and preserved, deprecated variant kept as alias, new variants appended; enum is#[non_exhaustive])cargo build --target wasm32-wasip1 --lib(no_std clean)JpegApp2BodyWithUrn, URN-mismatch / wrong-URN / short-input rejection, deprecatedJpegApp2alias equivalence, discriminants pinned to explicit values