jpegxl-rs icon indicating copy to clipboard operation
jpegxl-rs copied to clipboard

enc_ans.cc:228: JXL_DASSERT: n <= 255

Open y-guyon opened this issue 1 year ago • 1 comments

Hello, I got the following failure:

cargo test

running 1 test
./lib/jxl/enc_ans.cc:228: JXL_DASSERT: n <= 255
error: test failed, to rerun pass `--bin reprojxl`

with the following repro project:

Cargo.toml

[package]
name = "reprojxl"
version = "0.1.0"
edition = "2021"

[dependencies]
jpegxl-sys = { version = "=0.11.1" }
jpegxl-rs = { version = "=0.11.1", features = ["vendored"] }

main.rs

fn main() {}

#[test]
fn encode() {
    let width = 1;
    let height = 1;
    let pixels: &[u16] = &[1020u16];

    let mut encoder_builder = jpegxl_rs::encoder_builder();
    encoder_builder.has_alpha(false);
    encoder_builder.lossless(true);
    encoder_builder.uses_original_profile(true);
    encoder_builder.color_encoding(jpegxl_rs::encode::ColorEncoding::SrgbLuma);
    encoder_builder.speed(jpegxl_rs::encode::EncoderSpeed::Kitten);
    encoder_builder
        .build()
        .unwrap()
        .encode_frame::<u16, u16>(
            &jpegxl_rs::encode::EncoderFrame::new(pixels)
                .num_channels(1)
                .endianness(jpegxl_rs::Endianness::Native)
                .align(0),
            width,
            height,
        )
        .unwrap();
}

I was not able to reproduce with a CMake-built libjxl:

git clone -b v0.11.0 --depth 1 https://github.com/libjxl/libjxl.git
cd libjxl
./deps.sh
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build --parallel
build/tools/cjxl 1x1_u16.png 1x1_u16.jxl -e 8
# Works fine but maybe PNG is not read as 16-bit monochrome

This is why I am filing an issue here.

y-guyon avatar Oct 11 '24 16:10 y-guyon

Found the culprit, setting the speed (or effort in libjxl term) beyond 7 and also lossless cause this problem, can be reproduced directly using jpegxl-sys or C examples. I guess that they enabled some optimizations beyond that threshold.

use jpegxl_sys::{common::types::*, encoder::encode::*};

#[test]
fn encode() {
    unsafe {
        let width = 1;
        let height = 1;
        let pixels: &[u16] = &[1020u16];

        let encoder = JxlEncoderCreate(ptr::null_mut());
        let pixel_format = JxlPixelFormat {
            num_channels: 1,
            data_type: JxlDataType::Uint16,
            endianness: Endianness::Native,
            align: 0,
        };

        let mut basic_info = {
            let mut basic_info = MaybeUninit::uninit();
            JxlEncoderInitBasicInfo(basic_info.as_mut_ptr());
            basic_info.assume_init()
        };

        basic_info.xsize = width;
        basic_info.ysize = height;
        basic_info.bits_per_sample = 16;
        basic_info.exponent_bits_per_sample = 0;
        basic_info.uses_original_profile = JxlBool::True;
        basic_info.num_color_channels = 1;

        check(JxlEncoderSetBasicInfo(
            encoder,
            &basic_info,
        )); 

        let mut color_encoding = MaybeUninit::uninit();
        JxlColorEncodingSetToSRGB(color_encoding.as_mut_ptr(), true);
        check(JxlEncoderSetColorEncoding(
            encoder,
            color_encoding.as_ptr(),
        ));

        let frame_settings = JxlEncoderFrameSettingsCreate(encoder, ptr::null());
        check(JxlEncoderFrameSettingsSetOption(frame_settings, JxlEncoderFrameSettingId::Effort, 8));
        check(JxlEncoderSetFrameLossless(frame_settings, true));
        check(JxlEncoderAddImageFrame(
            frame_settings,
            &pixel_format,
            pixels.as_ptr().cast(),
            pixels.len() * 2,
        ));

        JxlEncoderCloseInput(encoder);

        let mut buffer = vec![0; 64];
        let mut next_out = buffer.as_mut_ptr();
        let mut avail_out = buffer.len();

        loop {
            let status = JxlEncoderProcessOutput(
                encoder,
                &mut next_out,
                &mut avail_out,
            );

            if status == JxlEncoderStatus::NeedMoreOutput {
                let offset = next_out.offset_from(buffer.as_mut_ptr());
                buffer.resize(buffer.len() * 2, 0);
                next_out = buffer.as_mut_ptr().add(offset.unsigned_abs());
                avail_out = buffer.len() - offset.unsigned_abs();
            } else {
                check(status);
                break;
            }
        }

        println!("Output: {buffer:?}");
    }
}

#[track_caller]
fn check(status: JxlEncoderStatus)  {
    let JxlEncoderStatus::Success = status else {
        panic!("Error: {status:?}")
    };
}

inflation avatar Oct 12 '24 04:10 inflation