jxl-rs Documentation

repository·main·Indexed 20 days ago

https://github.com/libjxl/jxl-rs

A high-performance, safe, and conforming Rust implementation of a JPEG XL decoder (version 0.5.1). It includes the jxl_cli tool for decoding JXL bitstreams into formats such as PNG, APNG, PPM, PGM, NPY, and EXR, as well as the jxlinspect utility for analyzing image metadata, color profiles, and animation details.

Tokens
13.8K
Snippets
44
Records
63
Agent score
69%

What's inside jxl-rs

  1. Overview of JPEG XL in Rust

    main

    The jxl-rs project is a work-in-progress reimplementation of a JPEG XL decoder written in Rust. Its primary goals are to provide a conforming, safe, and fast decoder for JPEG XL bitstreams.

    If you encounter a JPEG XL image that is successfully decoded by the reference implementation djxl (from libjxl) but fails or decodes incorrectly in jxl-rs, you should report it by opening an issue in the repository.

  2. Generate DCT/IDCT implementation files using Python scripts

    main

    The DCT, IDCT, and 2D transform implementations are generated using Python scripts. To regenerate the source files for various transform sizes (2, 4, 8, 16, 32), run the following sequence of commands. This process creates the specific .rs files in the src/ directory and formats the code with cargo fmt.

    for i in 2 4 8 16 32
    do
        python3 gen_idct.py $i > src/idct$i.rs
    done 
    for i in 2 4 8 16 32
    do
        python3 gen_reinterpreting_dct.py $i > src/reinterpreting_dct$i.rs
    done 
    python3 gen_idct2d.py > src/idct2d.rs
    python3 gen_reinterpreting_dct2d.py > src/reinterpreting_dct2d.rs
    cargo fmt
  3. How the JxlDecoder typestate pattern works

    main

    The JxlDecoder uses a typestate pattern to enforce a correct decoding lifecycle at compile time. The decoder transitions through different states, and only specific methods are available depending on the current state:

    1. JxlDecoder<Initialized>: The starting state. You can only call .new() and .process().
    2. JxlDecoder<WithImageInfo>: After the initial .process() call succeeds, you can access image metadata (basic info, color profiles, pixel formats) and call .process() again to move to the next state.
    3. JxlDecoder<WithFrameInfo>: After the second .process() call, you can access frame-specific headers and call .process() with pixel buffers to actually decode image data.

    This pattern prevents common errors, such as attempting to access frame headers before the file header has been parsed.

  4. Use the SimdDescriptor trait for hardware-accelerated SIMD operations

    main

    The SimdDescriptor trait is the core abstraction for accessing SIMD (Single Instruction, Multiple Data) capabilities in jxl-rs. It provides a unified interface for different CPU architectures (x86_64, AArch64, WASM32) and instruction sets (AVX, AVX512, SSE4.2, NEON, SIMD128).

    To use SIMD, you typically call SimdDescriptor::new() to attempt to obtain a descriptor for the current hardware. If successful, you use the descriptor to perform operations on specialized vector types like F32Vec, I32Vec, U32Vec, etc.

    A critical method is call<R>(self, f: impl FnOnce(Self) -> R) -> R. This method allows you to execute a closure within the correct target feature context, ensuring that SIMD intrinsics can be used safely and efficiently without breaking the inline function chain.

    // Example of obtaining a descriptor and using it within a feature context
    if let Some(d) = <MyDescriptor as SimdDescriptor>::new() {
        d.call(|d| {
            // Perform SIMD operations here using d
        });
    }
  5. Select a JxlProgressiveMode for progressive decoding

    main

    The JxlProgressiveMode enum determines how pixels are rendered during calls to the decoder's Process method, allowing for different levels of progressive image loading.

    • JxlProgressiveMode::Eager: Renders all pixels in every call to Process.
    • JxlProgressiveMode::Pass: Renders pixels once passes are completed.
    • JxlProgressiveMode::FullFrame: Renders pixels only once the final frame is ready.
    // Example of setting a specific progressive mode
    let mut options = JxlDecoderOptions::default();
    options.progressive_mode = JxlProgressiveMode::Eager;
  6. Color Management System implementation using Lcms2

    main

    The Lcms2Cms struct provides a Color Management System (CMS) implementation for JPEG XL using the Little CMS (lcms2) library. It implements the JxlCms trait, allowing for the initialization of color transforms between different JxlColorProfile profiles and the subsequent transformation of pixel data.

    Key behaviors:

    • Profile Conversion: It converts JxlColorProfile objects into ICC profiles.
    • Thread Safety: It uses lcms2::ThreadContext to ensure that transforms are thread-safe (Send).
    • Data Format: It performs transformations on f32 pixel data, internally mapping channel counts to appropriate lcms2 pixel formats (e.g., GRAY_FLT, RGB_FLT, CMYK_FLT).
    • Error Handling: It can return specific errors such as Error::InputIccError, Error::OutputIccError, Error::Lcms2InputParseError, Error::Lcms2OutputParseError, Error::Lcms2TransformError, or Error::OutputBufferTooSmall.
  7. Decode JPEG XL images using JxlDecoder

    main

    To decode a JPEG XL image, follow the state transitions: initialize the decoder, process the bitstream to get image info, and then process again with pixel buffers to render the frame.

    Note: The process method returns a ProcessingResult. If it returns NeedsMoreInput, you must provide more data to the decoder before continuing.

    // 1. Initialize
    let mut decoder = JxlDecoder::new(options);
    
    // 2. Process to get Image Info
    match decoder.process(&mut input)? {
        ProcessingResult::Complete { result, .. } => {
            let decoder = result; // Now JxlDecoder<WithImageInfo>
            
            // 3. Process to get Frame Info and Pixels
            match decoder.process(&mut input, &mut buffers, None)? {
                ProcessingResult::Complete { result, .. } => {
                    let decoder = result; // Now JxlDecoder<WithFrameInfo>
                    decoder.flush_pixels(&mut buffers, None)?;
                }
                _ => { /* Handle more input needed or error */ }
            }
        }
        _ => { /* Handle more input needed or error */ }
    }
  8. Efficiently seek to a specific frame in animations

    main

    For efficient seeking in animated JPEG XL files, use the following workflow:

    1. Enable JxlDecoderOptions::scan_frames_only when creating the decoder.
    2. Call .process() to scan the file. Use .scanned_frames() to retrieve a list of VisibleFrameInfo objects.
    3. Identify the target frame from the scanned_frames() list and extract its seek_target (VisibleFrameSeekTarget).
    4. Call .start_new_frame(seek_target) to reset the decoder state for that specific frame.
    5. Provide the raw file input starting from seek_target.decode_start_file_offset to begin decoding the target frame.
  9. Use the jxl_cli tool for decoding

    main

    The jxl_cli tool is used to decode JPEG XL files into various output formats. Supported output formats include .ppm, .pgm, .png, .apng, and .npy.

    Basic usage requires an input JXL file and an optional output file path. If no output is provided, the tool can still be used for information retrieval (--info) or performance benchmarking (--speedtest).

    # Decode an image to PNG
    jxl_cli input.jxl output.png
    
    # Print image information without decoding
    jxl_cli input.jxl --info
    
    # Extract the preview frame
    jxl_cli input.jxl output.png --preview
  10. Configure JPEG XL decoding with JxlDecoderOptions

    main

    Use JxlDecoderOptions to control the behavior of the JPEG XL decoder. You can instantiate it using JxlDecoderOptions::default() and then modify specific fields to tune performance, precision, or output format.

    Key Configuration Fields

    FieldTypeDefaultDescription
    adjust_orientationbooltrueAutomatically adjusts image orientation based on EXIF/metadata.
    render_spot_colorsbooltrueWhether to render spot colors.
    coalescingbooltrueEnables coalescing during decoding.
    skip_previewbooltrueSkips decoding the preview image.
    high_precisionboolfalseWhen true, uses higher precision (e.g., spline rendering, f32 buffers) at the cost of performance.
    premultiply_outputboolfalseIf true, multiplies RGB by alpha for premultiplied alpha output.
    scan_frames_onlyboolfalseIf true, only parses frame headers/TOC and skips section decoding. Useful for collecting VisibleFrameInfo without producing pixels.
    sample_limitOption<usize>NoneLimits the total number of samples (pixels $\times$ channels). Fails decoding if the limit is exceeded.
    desired_intensity_targetOption<f32>NoneTarget intensity for decoding.
    progressive_modeJxlProgressiveModePassControls how pixels are rendered during progressive decoding.
    let mut options = JxlDecoderOptions::default();
    options.high_precision = true;
    options.premultiply_output = true;
    options.sample_limit = Some(10_000_000);
  11. Use byte slices as JxlBitstreamInput

    main

    The JxlBitstreamInput trait is implemented for &[u8], allowing you to pass raw byte slices directly as input to the decoder. This is the simplest way to provide in-memory bitstream data.

    use jxl::api::input::JxlBitstreamInput;
    
    let data: &[u8] = &[0x00, 0x01, 0x02, 0x03];
    let mut input = data;
    
    // input now implements JxlBitstreamInput
    let available = input.available_bytes().unwrap();
    assert_eq!(available, 4);
  12. Extract ICC profiles from a JXL file

    main

    You can extract color profiles from a JPEG XL file using the following flags:

    • --icc-out <PATH>: Saves the ICC profile of the decoded image.
    • --original-icc-out <PATH>: Saves the ICC profile of the original colorspace.
    jxl_cli input.jxl output.png --icc-out decoded_profile.icc --original-icc-out original_profile.icc