Expand description
§ultrajpeg
ultrajpeg is a Rust library for working with JPEG images.
It provides a native Rust API for encoding and decoding plain JPEG images, MPF-bundled gain-map JPEGs, ICC/EXIF payloads, UltraHDR XMP, and ISO 21496-1 metadata.
§What The Crate Does
ultrajpeg sits at the point where three concerns meet:
- JPEG pixel coding
- JPEG container metadata and marker layout
- Ultra HDR gain-map packaging and recovery
The crate is synchronous and native-first. The public API is centered around:
inspect(...)for metadata-only inspectioninspect_container_layout(...)for codestream-boundary inspectiondecode(...)anddecode_with_options(...)for pixel decodeencode(...)andEncoderfor structured JPEG and Ultra HDR authoringparse_gain_map_xmp(...)andparse_iso_21496_1(...)for raw payload parsingprepare_sdr_primary(...)for caller-managed HDR workflowscompute_gain_map(...)andencode_ultra_hdr(...)for gain-map workflows
§Choosing An Entry Point
Use:
inspect(...)when you only need JPEG, ICC, EXIF, XMP, or ISO 21496-1 metadata and do not want to decode pixelsdecode(...)when you want the decoded primary image and, when present, the decoded gain-map imageinspect_container_layout(...)when you need codestream offsets and lengths without decoding pixelsdecode_with_options(...)when you also need to retain the raw primary JPEG or gain-map JPEG codestream bytesencode(...)when you already have the primary image and optional gain-map payload you want to package choose scan mode withprogressiveand size-vs-time policy withCompressionEffortparse_gain_map_xmp(...)orparse_iso_21496_1(...)when you need to validate or compare raw Ultra HDR metadata payloads yourselfprepare_sdr_primary(...)when you manage HDR pixel transforms yourself and need a supported SDR primary image plus matching metadata before computing a gain map use your own SDR primary instead when you already have a caller-specific SDR rendering policy you want to preserve exactlycompute_gain_map(...)when you want to generate a gain map from HDR and SDR inputs without encoding yetencode_ultra_hdr(...)when you want the crate to compute the gain map and package the final Ultra HDR JPEG in one step
§Public Surface Summary
The main public API lives at the crate root:
- functions:
inspectinspect_container_layoutdecodedecode_with_optionsencodeparse_gain_map_xmpparse_iso_21496_1prepare_sdr_primarycompute_gain_mapencode_ultra_hdr
- core types:
ImagePixelFormatColorGamutColorTransferGainMapGainMapMetadataHdrOutputFormat
- structured crate types:
ParsedGainMapXmpContainerKindCodestreamLayoutContainerLayoutColorMetadataPrimaryMetadataUltraHdrMetadataMetadataLocationGainMapMetadataSourceDecodedGainMapDecodedImageInspectionDecodeOptionsGainMapChannelsGainMapScaleComputeGainMapOptionsPreparePrimaryOptionsPreparedPrimaryComputedGainMapGainMapBundleCompressionEffortEncodeOptionsUltraHdrEncodeOptionsEncoder
- module:
icc
§Major Scenarios
§Plain JPEG Encode
let image = Image::from_data(
2,
2,
PixelFormat::Rgb8,
ColorGamut::DisplayP3,
ColorTransfer::Srgb,
vec![
255, 0, 0, 0, 255, 0,
0, 0, 255, 255, 255, 255,
],
)?;
let jpeg = ultrajpeg::encode(&image, &EncodeOptions::default())?;§Plain JPEG Decode
let decoded = decode(bytes)?;
assert_eq!(decoded.image.width, 4);
assert!(decoded.gain_map.is_none());
assert!(decoded.primary_jpeg.is_none());§Inspect Metadata Without Decoding Pixels
let inspection = inspect(bytes)?;
assert!(inspection.primary_jpeg_len > 0);
assert!(inspection.gain_map_jpeg_len.is_some());
assert!(inspection.primary_metadata.color.icc_profile.is_some());
assert!(inspection.ultra_hdr.is_some());§Inspect Container Layout Without Decoding Pixels
let layout = inspect_container_layout(bytes)?;
assert_eq!(layout.kind, ContainerKind::Mpf);
assert_eq!(layout.primary_index, 0);
assert_eq!(layout.gain_map_index, Some(1));
assert_eq!(layout.codestreams.len(), 2);§Parse Raw Ultra HDR Payloads Explicitly
let inspection = inspect(bytes)?;
let ultra_hdr = inspection.ultra_hdr.as_ref().unwrap();
let parsed_xmp = parse_gain_map_xmp(ultra_hdr.xmp.as_deref().unwrap())?;
let parsed_iso = parse_iso_21496_1(ultra_hdr.iso_21496_1.as_deref().unwrap())?;
// `ultra_hdr.iso_21496_1` is the effective gain-map payload. Do not pass the
// primary JPEG's four-byte version-only ISO APP2 block to `parse_iso_21496_1`.
assert!(parsed_xmp.metadata.hdr_capacity_max >= 4.0);
assert!(parsed_iso.hdr_capacity_max >= 4.0);§Encode An Ultra HDR JPEG From An Existing Gain Map
let primary = Image::from_data(
2,
2,
PixelFormat::Rgb8,
ColorGamut::DisplayP3,
ColorTransfer::Srgb,
vec![
255, 0, 0, 0, 255, 0,
0, 0, 255, 255, 255, 255,
],
)?;
let gain_map = Image::from_data(
2,
2,
PixelFormat::Gray8,
ColorGamut::Bt709,
ColorTransfer::Linear,
vec![0, 64, 128, 255],
)?;
let jpeg = ultrajpeg::encode(
&primary,
&EncodeOptions {
gain_map: Some(GainMapBundle {
image: gain_map,
metadata: GainMapMetadata::new(),
quality: 85,
progressive: false,
compression: CompressionEffort::Balanced,
}),
..EncodeOptions::ultra_hdr_defaults()
},
)?;§Compute A Gain Map First, Then Bundle It
let hdr = Image::from_data(
1,
1,
PixelFormat::Rgba32F,
ColorGamut::DisplayP3,
ColorTransfer::Linear,
[1.5f32, 0.5, 0.5, 1.0]
.into_iter()
.flat_map(f32::to_le_bytes)
.collect(),
)?;
let primary = Image::from_data(
1,
1,
PixelFormat::Rgb8,
ColorGamut::DisplayP3,
ColorTransfer::Srgb,
vec![255, 128, 128],
)?;
let computed = ultrajpeg::compute_gain_map(
&hdr,
&primary,
&ComputeGainMapOptions {
scale: GainMapScale::Default,
..ComputeGainMapOptions::default()
},
)?;
let jpeg = ultrajpeg::encode(
&primary,
&EncodeOptions {
gain_map: Some(computed.into_bundle(90, false, CompressionEffort::Balanced)),
..EncodeOptions::ultra_hdr_defaults()
},
)?;GainMapScale::Default computes the gain map at half primary-image width and
height. Use GainMapScale::Full when quality matters most, or
GainMapScale::Smallest when you explicitly want the most aggressive
quarter-resolution tradeoff.
§Prepare An SDR Primary Image From HDR Pixels
let hdr = Image::from_data(
1,
1,
PixelFormat::Rgba32F,
ColorGamut::DisplayP3,
ColorTransfer::Linear,
[1.5f32, 0.5, 0.5, 1.0]
.into_iter()
.flat_map(f32::to_le_bytes)
.collect(),
)?;
let prepared = prepare_sdr_primary(&hdr, &PreparePrimaryOptions::ultra_hdr_defaults())?;
let computed = compute_gain_map(&hdr, &prepared.image, &Default::default())?;
let jpeg = ultrajpeg::encode(
&prepared.image,
&EncodeOptions {
primary_metadata: prepared.metadata.clone(),
gain_map: Some(computed.into_bundle(90, false, CompressionEffort::Balanced)),
..EncodeOptions::default()
},
)?;If the source HDR peak is known more precisely, pass it explicitly:
let prepared = prepare_sdr_primary(
&hdr,
&PreparePrimaryOptions {
source_peak_nits: Some(4000.0),
..PreparePrimaryOptions::ultra_hdr_defaults()
},
)?;§One-Shot Ultra HDR Packaging
let hdr = Image::from_data(
1,
1,
PixelFormat::Rgba32F,
ColorGamut::DisplayP3,
ColorTransfer::Linear,
[1.5f32, 0.5, 0.5, 1.0]
.into_iter()
.flat_map(f32::to_le_bytes)
.collect(),
)?;
let primary = Image::from_data(
1,
1,
PixelFormat::Rgb8,
ColorGamut::DisplayP3,
ColorTransfer::Srgb,
vec![255, 128, 128],
)?;
let jpeg = encode_ultra_hdr(&hdr, &primary, &UltraHdrEncodeOptions::default())?;For both EncodeOptions and UltraHdrEncodeOptions, the progressive flags
select scan mode only. Use the matching CompressionEffort field when you
want to choose between balanced and size-oriented encoding.
§Decode And Reconstruct HDR Output
let decoded = decode(bytes)?;
let hdr = decoded.reconstruct_hdr(4.0, HdrOutputFormat::LinearFloat)?;
assert!(decoded.gain_map.is_some());
assert_eq!(hdr.width, decoded.image.width);§Retain Raw JPEG Codestreams Explicitly
let decoded = decode_with_options(
bytes,
DecodeOptions {
retain_primary_jpeg: true,
retain_gain_map_jpeg: true,
..DecodeOptions::default()
},
)?;
assert!(decoded.primary_jpeg.is_some());
assert!(decoded.gain_map.as_ref().unwrap().jpeg_bytes.is_some());§Inspect Metadata Provenance
let inspection = inspect(bytes)?;
let ultra_hdr = inspection.ultra_hdr.as_ref().unwrap();
assert!(matches!(
ultra_hdr.xmp_location,
Some(MetadataLocation::Primary | MetadataLocation::GainMap)
));§Color, ICC Profiles, And Gamuts
ultrajpeg intentionally does not collapse all color semantics into one opaque
type.
Instead, the stable model separates:
icc_profile: the embedded ICC payload, if presentgamut: a convenience named gamut classificationgamut_info: the authoritative structured gamut representation when gamut coordinates could be recoveredtransfer: the explicitly tracked transfer function
This matters because a JPEG may contain:
- an ICC profile with precise primaries and white point
- explicit crate-tracked gamut and transfer signaling
- both
- or neither
gamut_info is the richer result. gamut is only the best matching named
classification when one is available.
§Bundled Display-P3 Helper
let color = ColorMetadata::display_p3();
assert_eq!(color.icc_profile.as_deref(), Some(ultrajpeg::icc::display_p3()));
assert_eq!(color.gamut, Some(ColorGamut::DisplayP3));
assert_eq!(color.transfer, Some(ColorTransfer::Srgb));
let options = EncodeOptions::ultra_hdr_defaults();
assert_eq!(
options.primary_metadata.color.icc_profile.as_deref(),
Some(ultrajpeg::icc::display_p3())
);When packaging a gain map:
- if
EncodeOptions::primary_metadata.color.icc_profileis already set, it is embedded as-is - if no ICC profile is set and the resolved primary image is Display-P3 plus
sRGB,
ultrajpeginjects the bundled Display-P3 ICC profile automatically - otherwise gain-map packaging preserves the caller-provided absence of an ICC profile
The crate does not synthesize arbitrary ICC profiles.
§Practical Guidance
If you want a spec-friendly Display-P3 primary image for Ultra HDR packaging, prefer:
ColorMetadata::display_p3()EncodeOptions::ultra_hdr_defaults()icc::display_p3()
If your primary image uses a different color space, provide the explicit ICC profile you want embedded in the primary JPEG instead of relying on the Display-P3 helper path.
§Ultra HDR Metadata Behavior
UltraHdrMetadata exposes the effective metadata used by the crate after
fallback and recovery logic.
It includes:
xmpandxmp_locationiso_21496_1andiso_21496_1_locationgain_map_metadatagain_map_metadata_source
Important behavior:
- ISO 21496-1 is preferred over XMP when both are present and valid
- metadata may come from the primary JPEG or the gain-map JPEG
- the crate can recover malformed-but-usable files where the primary JPEG metadata is incomplete but MPF still points to a gain-map JPEG that carries usable gain-map semantics
§What Gets Written On Encode
When EncodeOptions::gain_map is Some(...), the crate writes:
- MPF directory metadata on the primary JPEG
- container or directory XMP on the primary JPEG
- a version-only ISO 21496-1 APP2 block on the primary JPEG
hdrgm:*XMP on the gain-map JPEG- canonical ISO 21496-1 gain-map metadata on the gain-map JPEG
§What Gets Resolved On Decode
On decode and inspect, the crate resolves the effective gain-map metadata from the available payloads and exposes where those payloads came from.
That means callers do not need to parse raw XMP or raw ISO 21496-1 bytes themselves unless they want to.
When callers do want to reason about the raw payloads directly, the crate also provides:
parse_gain_map_xmp(...)parse_iso_21496_1(...)
Those entry points are intentionally raw:
- they do not apply decode-time precedence
- they do not apply the crate’s defensive fallback filters
- they are meant for explicit validation and comparison workflows
parse_iso_21496_1(...) expects one gain-map ISO payload. The primary
JPEG’s four-byte version-only ISO APP2 block is structural only and returns an
error if passed to that raw parser directly.
§Gain-Map Computation Scale
ComputeGainMapOptions exposes two independent knobs:
channels, which controls whether the computed gain map is single-channel or multichannelscale, which controls gain-map resolution relative to the primary image
The supported GainMapScale values are:
GainMapScale::Full, which keeps full primary-image resolution and is recommended for best qualityGainMapScale::Default, which computes the gain map at half width and height and is the crate’s defaultGainMapScale::Smallest, which computes the gain map at quarter width and height and matches the most aggressive Android Ultra HDR-style recommendation, but may noticeably reduce quality
The default compute_gain_map(...) path therefore computes a single-channel
gain map at half primary-image resolution unless the caller opts into a
different channel layout or scale.
§Container Structure Inspection
inspect_container_layout(...) exposes:
- codestream offsets and lengths
- whether the input was recognized as MPF or only as concatenated JPEG codestreams
- which codestreams the crate treats as the primary and gain-map JPEG payloads
This surface is intentionally structural and inspection-oriented. It does not yet expose a generic public MPF rewrite API.
§SDR Primary Preparation
prepare_sdr_primary(...) is the supported high-level bridge for workflows
where the caller:
- starts from HDR pixels
- resizes, crops, or otherwise edits those pixels first
- then needs an SDR primary image before calling
compute_gain_map(...)
The helper returns both:
- the prepared
Rgb8primary image - matching
PrimaryMetadata
That metadata should be used together with the returned image on subsequent
encode(...) calls.
The current helper:
- supports
Rgb8,Rgba8,Rgba16F,Rgba32F,Rgba1010102Pq, andRgba1010102Hlginputs - produces sRGB-transfer output in either BT.709 or Display-P3 gamut
- floors the SDR primary brightness so the default
compute_gain_map(...)path stays within the crate’s default gain-map boost envelope - injects bundled Display-P3 ICC metadata automatically when Display-P3 output is requested
§Policy Notes For prepare_sdr_primary(...)
prepare_sdr_primary(...) is a supported default policy, not a promise to
reproduce every caller’s preferred SDR rendering intent.
In practice:
- if you already have a caller-chosen SDR primary image, keep using that image
with
compute_gain_map(...)andencode(...) - if you want
ultrajpegto derive a reasonable SDR primary for a transformed HDR image, useprepare_sdr_primary(...) - if you know the source HDR peak more precisely, set
PreparePrimaryOptions::source_peak_nitsexplicitly instead of relying on the transfer-based default
Current defaults are:
- PQ input with
source_peak_nits: Noneassumes10000nits - HLG input with
source_peak_nits: Noneassumes1000nits - linear input with
source_peak_nits: Noneassumes1000nits - sRGB input with
source_peak_nits: Noneassumes203nits
The helper also enforces a small but important compatibility rule: it floors
the derived SDR primary brightness so the returned image composes with the
crate’s default compute_gain_map(...) configuration instead of immediately
falling outside the default gain-map boost range.
That makes the default workflow easier to use, but it also means the output is
not just a naive one-pass tone map. If you need exact custom SDR rendering
intent, prepare the SDR primary image yourself and treat prepare_sdr_primary
as the optional convenience path rather than as the only supported one.
§Ownership And Performance Semantics
The public API is explicit about allocation behavior:
inspect(...)does not decode pixelsdecode(...)decodes pixels and retains no raw JPEG codestreams by defaultdecode_with_options(...)is the explicit escape hatch for retained JPEG codestream bytes- large Ultra HDR decodes may use internal Rayon-based parallelism, but the public API remains synchronous
The crate is designed so that callers do not accidentally retain large input codestreams unless they opt in.
§Limitations And Non-Goals
The crate deliberately does not:
- choose an SDR primary image for you implicitly during encode
- downscale, filter, or otherwise reshape gain maps automatically
- synthesize arbitrary ICC profiles
- infer complete color policy from partial hints
- act as a full conformance validator for every malformed Ultra HDR file shape
The caller remains responsible for:
- selecting the SDR primary image, unless it explicitly uses
prepare_sdr_primary(...) - deciding EXIF policy
- providing explicit ICC data when the primary image is not Display-P3 plus sRGB and a gain map is being bundled
- choosing the desired HDR reconstruction output format and display boost
Current limitations:
- the public API targets JPEG and MPF-bundled gain-map JPEG workflows
- Ultra HDR decode can recover some malformed files, but recovery is pragmatic, not a guarantee of full conformance validation
inspect_container_layout(...)is structural inspection only; it is not yet a public generic MPF rewrite API- the crate re-encodes JPEG pixel data on encode; it is not a marker-only remuxer for arbitrary already-encoded primary and gain-map codestream pairs
CompressionEffort::Smallestcurrently provides an extra size-oriented backend path only for progressive JPEGs; sequential JPEGs still accept it for API consistency, but currently use the same effective backend settings asCompressionEffort::Balanced- ICC parsing is currently used to recover structured gamut information, not to expose a full public ICC inspection API
§Migration Guide: 0.4.0 to 0.5.0
This guide covers migration from the 0.4.0 API line to the native stable-API direction implemented in
0.5.0.
It covers both:
- the main public-API refactor that established the
0.5.0line - the additive issue-
#4APIs added afterward on the same0.5.0line
§Summary
0.5.0 removes the wrapper-era public API and keeps one coherent native
surface at the crate root.
The biggest changes are:
- the compatibility API is no longer public
- the main image type is now
ultrajpeg::Image DecodedJpegbecameDecodedImageInspectedJpegbecameInspectionGainMapEncodeOptionsbecameGainMapBundleUltraJpegEncoderbecameEncoderEncodeOptions::color_metadatabecameEncodeOptions::primary_metadata- EXIF moved out of
ColorMetadataintoPrimaryMetadata decode(...)no longer retains raw JPEG codestream bytes by defaultComputedGainMap::into_encode_options(...)becameinto_bundle(...)- primary and gain-map encode settings now include an explicit
CompressionEffort - raw Ultra HDR payload parsing is now available directly from
ultrajpeg - structural bundled-container inspection is now available directly from
ultrajpeg - a supported SDR-primary preparation helper now exists for caller-managed HDR workflows
§Import Mapping
Old:
use ultrajpeg::{
ColorMetadata, DecodeOptions, EncodeOptions, GainMapEncodeOptions,
UltraJpegEncoder, decode, inspect,
};New:
use ultrajpeg::{
ColorMetadata, DecodeOptions, EncodeOptions, Encoder, GainMapBundle,
PrimaryMetadata, decode, inspect,
};§Type Renames
Direct renames:
CoreRawImage->ImageDecodedJpeg->DecodedImageInspectedJpeg->InspectionGainMapEncodeOptions->GainMapBundleUltraJpegEncoder->Encoder
Method rename:
ComputedGainMap::into_encode_options(...)->ComputedGainMap::into_bundle(...)
§Metadata Model Changes
§ColorMetadata
Old ColorMetadata bundled together:
- ICC profile
- EXIF payload
- gamut
- transfer
New ColorMetadata contains only color-related state:
icc_profilegamutgamut_infotransfer
EXIF moved to PrimaryMetadata.
§PrimaryMetadata
New:
pub struct PrimaryMetadata {
pub color: ColorMetadata,
pub exif: Option<Vec<u8>>,
}If you previously wrote:
let options = EncodeOptions {
color_metadata: ColorMetadata {
icc_profile: Some(profile),
exif: Some(exif),
gamut: Some(ColorGamut::DisplayP3),
transfer: Some(ColorTransfer::Srgb),
},
..EncodeOptions::default()
};You now write:
let options = EncodeOptions {
primary_metadata: PrimaryMetadata {
color: ColorMetadata {
icc_profile: Some(profile),
gamut: Some(ColorGamut::DisplayP3),
gamut_info: None,
transfer: Some(ColorTransfer::Srgb),
},
exif: Some(exif),
},
..EncodeOptions::default()
};§Encode Migration
§Old
let bytes = UltraJpegEncoder::new(options).encode(&primary)?;§New
Either:
let bytes = Encoder::new(options).encode(&primary)?;Or, when you do not need a reusable encoder instance:
let bytes = ultrajpeg::encode(&primary, &options)?;§Gain-map payload
Old:
gain_map: Some(GainMapEncodeOptions {
image,
metadata,
quality,
progressive,
})New:
gain_map: Some(GainMapBundle {
image,
metadata,
quality,
progressive,
compression: CompressionEffort::Balanced,
})§compute_gain_map(...)
Old:
let computed = ultrajpeg::compute_gain_map(&hdr, &primary, &Default::default())?;
let options = EncodeOptions {
gain_map: Some(computed.into_encode_options(90, false)),
..EncodeOptions::ultra_hdr_defaults()
};New:
let computed = ultrajpeg::compute_gain_map(&hdr, &primary, &Default::default())?;
let options = EncodeOptions {
gain_map: Some(computed.into_bundle(90, false, CompressionEffort::Balanced)),
..EncodeOptions::ultra_hdr_defaults()
};To preserve the previous default encode behavior explicitly, set:
compression: CompressionEffort::BalancedUse CompressionEffort::Smallest when you want the most size-oriented
configuration available for the chosen scan mode.
ComputeGainMapOptions also now exposes a typed scale control through
GainMapScale. The default is GainMapScale::Default, which computes the
gain map at half primary-image width and height. Use GainMapScale::Full for
best quality or GainMapScale::Smallest for the most aggressive
quarter-resolution size tradeoff.
§Decode Migration
§Result type and fields
Old:
let decoded = ultrajpeg::decode(bytes)?;
let image = decoded.primary_image;
let icc = decoded.color_metadata.icc_profile;
let exif = decoded.color_metadata.exif;New:
let decoded = ultrajpeg::decode(bytes)?;
let image = decoded.image;
let icc = decoded.primary_metadata.color.icc_profile;
let exif = decoded.primary_metadata.exif;Field mapping:
decoded.primary_image->decoded.imagedecoded.color_metadata->decoded.primary_metadata.colordecoded.color_metadata.exif->decoded.primary_metadata.exif
§Default codestream retention changed
In 0.4.0, decode(...) retained the raw primary JPEG and gain-map JPEG
codestreams by default.
In 0.5.0, decode(...) retains neither by default.
If you previously relied on:
decoded.primary_jpegdecoded.gain_map.as_ref().unwrap().jpeg_bytes
you must opt in explicitly:
let decoded = ultrajpeg::decode_with_options(
bytes,
ultrajpeg::DecodeOptions {
retain_primary_jpeg: true,
retain_gain_map_jpeg: true,
..Default::default()
},
)?;Also note the field types changed:
- old
primary_jpeg: Vec<u8> - new
primary_jpeg: Option<Vec<u8>> - old
jpeg_bytes: Vec<u8> - new
jpeg_bytes: Option<Vec<u8>>
§Inspect Migration
Old:
let inspected = ultrajpeg::inspect(bytes)?;
let icc = inspected.color_metadata.icc_profile;New:
let inspected = ultrajpeg::inspect(bytes)?;
let icc = inspected.primary_metadata.color.icc_profile;Field mapping:
inspected.color_metadata->inspected.primary_metadata.color
§New Metadata Provenance
UltraHdrMetadata now exposes provenance:
xmp_locationiso_21496_1_locationgain_map_metadata_source
If your previous code only consumed gain_map_metadata, it can keep doing so.
If you need to know whether effective metadata came from the primary JPEG or the gain-map JPEG, or whether parsed effective gain-map metadata came from ISO 21496-1 or XMP, you can now inspect those fields directly.
§ColorMetadata::gamut_info
In 0.4.0, ColorMetadata exposed a gamut_info() helper method.
In 0.5.0, gamut_info is a field:
Old:
let standard = decoded
.color_metadata
.gamut_info()
.as_ref()
.and_then(|info| info.standard);New:
let standard = decoded
.primary_metadata
.color
.gamut_info
.as_ref()
.and_then(|info| info.standard);The semantics are the same: gamut_info is the richer, authoritative gamut
representation, and gamut remains the convenience named classification.
§Display-P3 Helpers
The helpers still exist, but their placement changed through the
PrimaryMetadata split.
These remain available:
ColorMetadata::display_p3()EncodeOptions::ultra_hdr_defaults()icc::display_p3()
When packaging a gain map, the crate still auto-injects the bundled Display-P3 ICC profile when:
- the resolved primary image is Display-P3 plus sRGB
- and no explicit primary ICC profile is already present
§encode_ultra_hdr(...)
This API remains, but the primary subtree inside UltraHdrEncodeOptions
inherits the new EncodeOptions structure.
That means:
options.primary.primary_metadatanow holds the primary JPEG metadataoptions.primary.progressiveandoptions.primary.compressioncontrol the primary JPEG scan mode and compression effortoptions.primary.gain_mapmust still remainNoneoptions.gain_map_progressiveandoptions.gain_map_compressioncontrol the computed secondary gain-map JPEG scan mode and compression effort
§New Additive APIs After The Main Refactor
The final 0.5.0 surface also adds a few APIs that were not present in the
initial 0.5.0 refactor pass. These are additive rather than breaking, but they matter if you
were still depending directly on ultrahdr-core for a few gaps.
§Raw Ultra HDR Payload Parsing
New root functions:
parse_gain_map_xmp(...)parse_iso_21496_1(...)
New supporting type:
ParsedGainMapXmp
Use these when you need to reason about one raw payload directly.
Use inspect(...) or decode(...) when you want the crate’s effective
metadata view after precedence and recovery rules.
In other words:
parse_gain_map_xmp(...)parses one rawhdrgm:*XMP payloadparse_iso_21496_1(...)parses one raw gain-map ISO 21496-1 payloadinspect(...)anddecode(...)expose the crate’s effective metadata view
Do not pass the primary JPEG’s four-byte version-only ISO APP2 block to
parse_iso_21496_1(...); that block is structural only and does not carry
gain-map parameters.
If your old 0.4.0 code imported ultrahdr-core only to parse one of
those raw payload forms, you can now keep that workflow inside ultrajpeg.
§Structural Container Inspection
New root function:
inspect_container_layout(...)
New supporting types:
ContainerKindCodestreamLayoutContainerLayout
Use this API when you need codestream offsets and lengths from a bundled JPEG container without decoding pixels.
This is intentionally structural:
- it tells you where codestreams are
- it tells you which codestreams
ultrajpegtreats as primary and gain-map candidates structurally - it does not by itself prove that the second codestream is semantically a valid gain map
- it does not expose a generic public MPF rewrite API
If your old code imported ultrahdr-core only for parse_mpf(...) or
find_jpeg_boundaries(...) in order to inspect container structure, this new
root API is the supported replacement for the common inspection case.
§SDR Primary Preparation
New root function:
prepare_sdr_primary(...)
New supporting types:
PreparePrimaryOptionsPreparedPrimary
This is the supported high-level path for workflows where you:
- start from HDR pixels
- resize, crop, or otherwise edit them
- then need an SDR primary image before calling
compute_gain_map(...)
prepare_sdr_primary(...) returns both:
- the prepared SDR primary image
- matching
PrimaryMetadata
Those two values are intended to be used together on subsequent encode calls.
If you already have a caller-specific SDR rendering intent and SDR primary
image, keep using that image directly with compute_gain_map(...) and
encode(...). prepare_sdr_primary(...) is the supported default policy, not
a forced replacement for bespoke SDR preparation.
Also note one important policy detail: the helper floors the derived SDR
primary brightness so the returned image composes with the crate’s default
compute_gain_map(...) configuration instead of immediately falling outside
the default gain-map boost range.
§Compatibility API Removal
The most disruptive change is that the wrapper-era compatibility API is no longer public.
Removed root exports include:
CompressedImageRawImageDecoderEncodedStreamDecodedPackedImgLabelsysjpegmozjpeg- the compatibility
Encoder
If your code depended on that API, you have two options:
- stay on
0.4.0for now - port to the native root API
For most users, the target surface should now be:
Imageencode(...)orEncoderdecode(...)andDecodedImageinspect(...)andInspection
§Migration Checklist
- Replace legacy type names with the new native names.
- Replace
EncodeOptions::color_metadatawithEncodeOptions::primary_metadata. - Move EXIF payload handling into
PrimaryMetadata::exif. - Replace
GainMapEncodeOptionswithGainMapBundle. - Replace
UltraJpegEncoder::new(...).encode(...)with eitherEncoder::new(...).encode(...)orencode(...). - Replace
ComputedGainMap::into_encode_options(...)withinto_bundle(...)and pass an explicitCompressionEffort. - Audit all decode call sites that relied on retained codestream bytes and add
DecodeOptionsretention flags explicitly. - Update any
gamut_info()method calls to field access. - If you used the compatibility API, plan a full port or remain on
0.4.0. - If you still depend directly on
ultrahdr-coreonly for raw Ultra HDR payload parsing, structural codestream inspection, or SDR-primary preparation, switch those use cases to the newultrajpegroot APIs.
§Rationale For The Break
The 0.5.0 changes are intentionally opinionated:
- one coherent root API instead of mixed native and wrapper-era surfaces
- explicit ownership behavior
- better separation between primary-JPEG metadata and Ultra HDR metadata
- more discoverable naming
- less hidden allocation in the default decode path
- fewer reasons for consumers to reach through
ultrajpegintoultrahdr-corefor normal HDR JPEG workflows
That is why this release is a migration-heavy pre-1.0 step rather than an
incremental rename-only release.
Modules§
- icc
- Built-in ICC profiles for common JPEG workflows.
Structs§
- Chromaticity
- An xy chromaticity coordinate. An xy chromaticity coordinate.
- Codestream
Layout - Structural layout of one embedded JPEG codestream. Byte range of one embedded JPEG codestream inside an input buffer.
- Color
Metadata - Color-related metadata attached to the primary JPEG image. Color-related metadata attached to the primary JPEG image.
- Compute
Gain MapOptions - Options for gain-map computation from HDR and SDR inputs. Options for gain-map computation from HDR and SDR primary images.
- Computed
Gain Map - Result of computing an Ultra HDR gain map from HDR and SDR inputs. Result of computing an Ultra HDR gain map from HDR and SDR images.
- Container
Layout - Structural layout of a JPEG or multi-image JPEG container. Structural layout of a JPEG or multi-image JPEG container.
- Decode
Options - Decode configuration. Decode configuration.
- Decoded
Gain Map - Decoded gain-map JPEG payload and associated metadata. Decoded gain-map payload and associated metadata.
- Decoded
Image - Fully decoded JPEG or Ultra HDR JPEG. Fully decoded JPEG/UltraHDR image.
- Encode
Options - Encode configuration for the primary JPEG and optional gain-map bundle. Encode configuration for the primary image and optional bundled gain map.
- Encoder
- Reusable stateful encoder. Reusable stateful encoder.
- GainMap
- Gain-map image representation produced by decode and used for HDR reconstruction. A gain map image (8-bit grayscale or per-channel).
- Gain
MapBundle - Gain-map payload and metadata to bundle into an Ultra HDR JPEG. Gain-map payload and metadata to bundle into the final output.
- Gain
MapMetadata - Structured Ultra HDR gain-map metadata. Gain map metadata (linear scale values). These values describe how to interpret the gain map.
- Gamut
Info - Structured gamut information recovered from explicit metadata or ICC data. Structured gamut information derived from explicit signaling or an ICC profile.
- Image
- Stable image type used by
ultrajpegfor decoded pixels, encoder input, and gain-map computation. A raw (uncompressed) image. - Inspection
- Metadata-only inspection result. Metadata-only JPEG or Ultra HDR inspection result.
- Parsed
Gain MapXmp - Parsed raw
hdrgm:*XMP payload. Parsedhdrgm:*XMP payload. - Prepare
Primary Options - Options for deriving an SDR primary image from source pixels. Options for deriving an SDR primary image from source pixels.
- Prepared
Primary - Prepared SDR primary image and matching primary-JPEG metadata. Prepared SDR primary image and matching primary-JPEG metadata.
- Primary
Metadata - Primary-JPEG metadata handled by the crate. Primary-JPEG metadata handled by the crate.
- Ultra
HdrEncode Options - Convenience options for one-shot Ultra HDR packaging. High-level convenience options for direct Ultra HDR packaging from HDR and SDR inputs.
- Ultra
HdrMetadata - Structured effective Ultra HDR metadata resolved by the crate. Structured effective Ultra HDR metadata resolved by the crate.
- Ultra
HdrMetadata Emission - Opt-out controls for Ultra HDR metadata emission during gain-map packaging. Opt-out controls for Ultra HDR metadata emission during gain-map packaging.
Enums§
- Chroma
Subsampling - JPEG chroma-subsampling modes supported when encoding primary images. Chroma subsampling modes exposed by the public API.
- Color
Gamut - Named color gamut classification used by decoded images and metadata. Color gamut / color space primaries.
- Color
Transfer - Color transfer function used by decoded images and metadata. Electro-optical transfer function (EOTF/OETF).
- Compression
Effort - JPEG compression effort used during encoding. JPEG compression effort.
- Container
Kind - Structural classification of a JPEG container. Structural classification of a JPEG container.
- Error
- Public error type for codec, container, and metadata failures. Public error type for codec, container, and metadata failures.
- Gain
MapChannels - Channel layout used when computing a gain map. Gain-map channel layout for computed Ultra HDR metadata.
- Gain
MapMetadata Source - Representation from which effective gain-map metadata was parsed. Representation from which effective gain-map metadata was parsed.
- Gain
MapScale - Supported spatial scales for computed gain maps. Supported spatial scales for computed gain maps.
- HdrOutput
Format - HDR reconstruction output formats supported by the crate. Output format for HDR reconstruction.
- Metadata
Location - Location from which Ultra HDR metadata was resolved. Location from which Ultra HDR metadata was resolved.
- Pixel
Format - Stable pixel-format type used by
Image. Pixel format for raw images.
Functions§
- compute_
gain_ map - Compute a gain map from an HDR image and a caller-chosen SDR primary image.
- decode
- Decode a JPEG or Ultra HDR JPEG using the default decode configuration.
- decode_
with_ options - Decode a JPEG or Ultra HDR JPEG using explicit decode options.
- encode
- Encode a primary JPEG, optionally bundling a gain map and Ultra HDR metadata.
- encode_
ultra_ hdr - Convenience wrapper that computes a gain map and packages an Ultra HDR JPEG.
- inspect
- Inspect JPEG or Ultra HDR container metadata without decoding image pixels.
- inspect_
container_ layout - Inspect JPEG codestream boundaries and bundled-container structure.
- parse_
gain_ map_ xmp - Parse a raw
hdrgm:*XMP payload into structured gain-map metadata. - parse_
iso_ 21496_ 1 - Parse a raw ISO 21496-1 gain-map payload into structured metadata.
- prepare_
sdr_ primary - Prepare an SDR primary image from source pixels for gain-map workflows.
Type Aliases§
- Result
- Public error type for codec, container, and metadata failures. Result type used by the crate.