Skip to content

Conversation

@hjanuschka
Copy link
Collaborator

@hjanuschka hjanuschka commented Nov 26, 2025

What this adds

The decoder can now read gain maps from JXL files. Gain maps let images store both SDR and HDR versions with conversion metadata - pretty useful for cross-device HDR support.

Implementation

  • Added GainMapBundle struct matching libjxl's API
  • Parses jhgm boxes from containers
  • Binary format is byte-for-byte compatible with libjxl
  • Includes example showing how to use it
  • Test file generator + integration tests

Testing

All tests pass - verified against libjxl's test vectors to make sure the binary format matches exactly.

cargo test --package jxl --lib api::gain_map     # unit tests
cargo test --package jxl --test gain_map_parsing # integration tests
cargo run --example gain_map_info -- file.jxl   # try it out

The only thing not implemented yet is ColorEncoding serialization (would need BitWriter), but that's rarely used since ICC profiles work fine.

Let me know if you'd like any changes!

Implements parsing and serialization of gain maps in JPEG XL files.
Gain maps allow images to store both SDR and HDR representations
with metadata describing the conversion between them.

The implementation:
- Parses jhgm boxes from JXL containers
- Provides GainMapBundle API matching libjxl's structure
- Includes serialization/deserialization (binary compatible with libjxl)
- Adds decoder.gain_map() method to access gain map data
- Includes example and tests with sample file

All tests passing, format verified against libjxl test vectors.
- Fix ICC profile comment: clarify it uses JXL-specific compression, not Brotli
- Fix gain_map comment: can be container, not just naked codestream
- Use valid 12-byte minimal JXL codestream from mathiasbynens/small
- Implement ColorEncoding parsing using BitReader/read_unconditional
- Add try_reserve for gain_map_data to prevent DoS from large allocations
- Use std::mem::take instead of clear() to actually free memory
- Add warning log when gain map parsing fails
@github-actions
Copy link

github-actions bot commented Dec 17, 2025

Benchmark @ 1361237


====================================================================================================
MULTI-FILE BENCHMARK RESULTS (4 files, 5 revisions)
  https://github.com/zond/jxl-perfhistory
  CPU architecture: x86_64
  WARNING: System appears noisy: high system load (2.28). Results may be unreliable.
====================================================================================================

Statistics:
  Revisions:                        5
  Confidence:                    99.0%
  Max relative error:             3.0%

[ 1] edecd8ff Merge 13612377f7986f6b70d79183ae17e4517ca082f2 into 35103...
     vs 13612377 Fix clippy never_loop warning in gain_map_info example
----------------------------------------------------------------------------------------------------------------------------------------------------------------
     bike.jxl                   |                                        ┃                               ├─│─┤     |     23678014 px/s | 1.242 / prev ▲ faster (1.24 / prev)
     green_queen_modular_e3.jxl |                                        ┃                          ├──│┤          |      6297665 px/s | 1.212 / prev ▲ faster (1.21 / prev)
     green_queen_vardct_e3.jxl  |                                        ┃              ├───│┤                     |     20088359 px/s | 1.133 / prev ▲ faster (1.13 / prev)
     sunset_logo.jxl            |                                        ┃              │┤                         |      2282955 px/s | 1.109 / prev ▲ faster (1.11 / prev)
                                  Scale: 0.72 to 1.28 (1.0 = same speed, '┃' marks 1.0)

[ 2] 13612377 Fix clippy never_loop warning in gain_map_info example
     vs 35103ba7 Bump version to 0.2.0.
----------------------------------------------------------------------------------------------------------------------------------------------------------------
     bike.jxl                   |    ├────│─┤                            ┃                                         |     19065536 px/s | 0.806 / prev ▼ slower (0.81 / prev)
     green_queen_modular_e3.jxl |        ├─│                             ┃                                         |      5197494 px/s | 0.811 / prev ▼ slower (0.81 / prev)
     green_queen_vardct_e3.jxl  |                    ├│─┤                ┃                                         |     17731936 px/s | 0.879 / prev ▼ slower (0.88 / prev)
     sunset_logo.jxl            |                       ├──│┤            ┃                                         |      2058000 px/s | 0.909 / prev ▼ slower (0.91 / prev)
                                  Scale: 0.75 to 1.25 (1.0 = same speed, '┃' marks 1.0)

[ 3] 35103ba7 Bump version to 0.2.0.
     vs eaf69230 Implement `AtomicRefCell`
----------------------------------------------------------------------------------------------------------------------------------------------------------------
     bike.jxl                   |                           ├────────────╂│──┤                                     |     23660076 px/s | 1.002 / prev 
     green_queen_modular_e3.jxl |                                        ┃        ├─│┤                             |      6410564 px/s | 1.030 / prev ▲ faster (1.03 / prev)
     green_queen_vardct_e3.jxl  |                                    ├───│────┤                                    |     20181638 px/s | 1.001 / prev 
     sunset_logo.jxl            |                          ├────────────│╂─┤                                       |      2263314 px/s | 0.996 / prev 
                                  Scale: 0.89 to 1.11 (1.0 = same speed, '┃' marks 1.0)

[ 4] eaf69230 Implement `AtomicRefCell`
     vs 99e245a7 Add a SmallVec type and optimize the low mem render pipel...
----------------------------------------------------------------------------------------------------------------------------------------------------------------
     bike.jxl                   |                                  ├─────╂│──┤                                     |     23604145 px/s | 1.004 / prev 
     green_queen_modular_e3.jxl |                                      ├─│                                         |      6224046 px/s | 0.999 / prev 
     green_queen_vardct_e3.jxl  |                                   ├────╂│─┤                                      |     20157939 px/s | 1.002 / prev 
     sunset_logo.jxl            |                                      ├─│─┤                                       |      2272456 px/s | 1.000 / prev 
                                  Scale: 0.89 to 1.11 (1.0 = same speed, '┃' marks 1.0)

[ 5] 99e245a7 Add a SmallVec type and optimize the low mem render pipel... (oldest, baseline for comparisons)

================================================================================================================================================================

- Fix comment: minimal JXL is 512x256 RGB, not 1x1 grayscale
- Add warning log when color_encoding is set but serialization
  not implemented (requires BitWriter)
- Remove conservative size estimate since color_encoding is always 0
- Update documentation to clarify the limitation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants