#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![deny(clippy::pedantic)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::redundant_static_lifetimes)]
#![allow(clippy::clippy::too_many_arguments)]
#![allow(clippy::unreadable_literal)]
#![allow(clippy::unseparated_literal_suffix)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
macro_rules! trait_impl {
($x:ty, [$($struct_:ident ),*]) => {
$(
impl $x for $struct_ {}
)*
};
}
trait_impl!(NewUninit, [JxlBasicInfo, JxlPixelFormat, JxlColorEncoding]);
pub trait NewUninit {
#[inline]
#[must_use]
fn new_uninit() -> std::mem::MaybeUninit<Self>
where
Self: std::marker::Sized,
{
std::mem::MaybeUninit::<Self>::uninit()
}
}
#[cfg(test)]
mod test {
use super::*;
use std::ptr;
use image::io::Reader as ImageReader;
use image::ImageError;
macro_rules! jxl_dec_assert {
($val:expr, $desc:expr) => {
if $val != JxlDecoderStatus_JXL_DEC_SUCCESS {
panic!("Decoder error by: {}, in {}", $val, $desc)
}
};
}
macro_rules! jxl_enc_assert {
($val:expr, $desc:expr) => {
if $val != JxlEncoderStatus_JXL_ENC_SUCCESS {
panic!("Encoder error by: {}, in {}", $val, $desc)
}
};
}
#[test]
fn test_bindings_version() {
unsafe {
assert_eq!(JxlDecoderVersion(), 3003);
}
}
unsafe fn decode(decoder: *mut JxlDecoder, sample: &[u8]) {
let mut status: u32;
status = JxlDecoderSubscribeEvents(
decoder,
(JxlDecoderStatus_JXL_DEC_BASIC_INFO | JxlDecoderStatus_JXL_DEC_FULL_IMAGE) as i32,
);
jxl_dec_assert!(status, "Subscribe Events");
let signature = JxlSignatureCheck(sample.as_ptr(), 2);
assert_eq!(signature, JxlSignature_JXL_SIG_CODESTREAM, "Signature");
let next_in = sample.as_ptr();
let avail_in = sample.len() as u64;
let pixel_format = JxlPixelFormat {
num_channels: 3,
data_type: JxlDataType_JXL_TYPE_UINT8,
endianness: JxlEndianness_JXL_NATIVE_ENDIAN,
align: 0,
};
let mut basic_info = JxlBasicInfo::new_uninit();
let mut buffer: Vec<f32> = Vec::new();
let mut xsize = 0;
let mut ysize = 0;
status = JxlDecoderSetInput(decoder, next_in, avail_in);
jxl_dec_assert!(status, "Set input");
loop {
status = JxlDecoderProcessInput(decoder);
match status {
JxlDecoderStatus_JXL_DEC_ERROR => panic!("Decoder error!"),
JxlDecoderStatus_JXL_DEC_NEED_MORE_INPUT => {
panic!("Error, already provided all input")
}
JxlDecoderStatus_JXL_DEC_BASIC_INFO => {
status = JxlDecoderGetBasicInfo(decoder, basic_info.as_mut_ptr());
jxl_dec_assert!(status, "BasicInfo");
let basic_info = basic_info.assume_init();
xsize = basic_info.xsize;
ysize = basic_info.ysize;
assert_eq!(basic_info.bits_per_sample, 8, "Bits per sample");
assert_eq!(basic_info.xsize, 2122, "Width");
assert_eq!(basic_info.ysize, 1433, "Height");
}
JxlDecoderStatus_JXL_DEC_NEED_IMAGE_OUT_BUFFER => {
let mut size: u64 = 0;
status = JxlDecoderImageOutBufferSize(decoder, &pixel_format, &mut size);
jxl_dec_assert!(status, "BufferSize");
buffer.resize(size as usize, 0f32);
status = JxlDecoderSetImageOutBuffer(
decoder,
&pixel_format,
buffer.as_mut_ptr() as *mut std::ffi::c_void,
size,
);
jxl_dec_assert!(status, "SetBuffer");
}
JxlDecoderStatus_JXL_DEC_FULL_IMAGE => continue,
JxlDecoderStatus_JXL_DEC_SUCCESS => {
assert_eq!(buffer.len(), (xsize * ysize * 3) as usize);
return;
}
_ => panic!("Unknown decoder status: {}", status),
}
}
}
#[test]
fn test_bindings_decoding() {
unsafe {
let dec = JxlDecoderCreate(ptr::null());
assert!(!dec.is_null());
let sample = std::fs::read("test/sample.jxl").unwrap();
decode(dec, &sample);
JxlDecoderDestroy(dec);
}
}
#[test]
fn test_bindings_thread_pool() {
unsafe {
let runner = JxlThreadParallelRunnerCreate(
std::ptr::null(),
JxlThreadParallelRunnerDefaultNumWorkerThreads(),
);
let dec = JxlDecoderCreate(ptr::null());
assert!(!dec.is_null());
let status = JxlDecoderSetParallelRunner(dec, Some(JxlThreadParallelRunner), runner);
jxl_dec_assert!(status, "Set Parallel Runner");
let sample = std::fs::read("test/sample.jxl").unwrap();
decode(dec, &sample);
JxlDecoderDestroy(dec);
JxlThreadParallelRunnerDestroy(runner);
}
}
fn encode(pixels: &[u8], xsize: u32, ysize: u32) -> Vec<u8> {
unsafe {
let enc = JxlEncoderCreate(std::ptr::null());
let runner = JxlThreadParallelRunnerCreate(
std::ptr::null(),
JxlThreadParallelRunnerDefaultNumWorkerThreads(),
);
let mut status =
JxlEncoderSetParallelRunner(enc, Some(JxlThreadParallelRunner), runner);
jxl_enc_assert!(status, "Set Parallel Runner");
let mut basic_info = JxlBasicInfo::new_uninit().assume_init();
basic_info.xsize = xsize;
basic_info.ysize = ysize;
basic_info.bits_per_sample = 8;
basic_info.exponent_bits_per_sample = 0;
basic_info.alpha_exponent_bits = 0;
basic_info.alpha_bits = 0;
basic_info.uses_original_profile = JXL_FALSE as i32;
status = JxlEncoderSetBasicInfo(enc, &basic_info);
jxl_enc_assert!(status, "Set Basic Info");
let pixel_format = JxlPixelFormat {
num_channels: 3,
data_type: JxlDataType_JXL_TYPE_UINT8,
endianness: JxlEndianness_JXL_NATIVE_ENDIAN,
align: 0,
};
let mut color_encoding = JxlColorEncoding::new_uninit().assume_init();
JxlColorEncodingSetToSRGB(&mut color_encoding, JXL_FALSE as i32);
status = JxlEncoderSetColorEncoding(enc, &color_encoding);
jxl_enc_assert!(status, "Set Color Encoding");
status = JxlEncoderAddImageFrame(
JxlEncoderOptionsCreate(enc, std::ptr::null()),
&pixel_format,
pixels.as_ptr() as *mut std::ffi::c_void,
pixels.len() as u64,
);
jxl_enc_assert!(status, "Add Image Frame");
const CHUNK_SIZE: usize = 1024 * 512;
let mut buffer = vec![0u8; CHUNK_SIZE];
let mut next_out = buffer.as_mut_ptr();
let mut avail_out = CHUNK_SIZE;
loop {
status = JxlEncoderProcessOutput(
enc,
&mut next_out as *mut *mut u8,
&mut (avail_out as u64) as *mut u64,
);
if status != JxlEncoderStatus_JXL_ENC_NEED_MORE_OUTPUT {
break;
}
let offset = next_out as usize - buffer.as_ptr() as usize;
buffer.resize(buffer.len() * 2, 0);
next_out = buffer.as_mut_ptr().add(offset);
avail_out = buffer.len() - offset;
}
buffer.truncate(next_out as usize - buffer.as_ptr() as usize);
jxl_enc_assert!(status, "Encoding");
JxlEncoderDestroy(enc);
JxlThreadParallelRunnerDestroy(runner);
buffer
}
}
#[test]
fn test_bindings_encoding() {
|| -> Result<(), ImageError> {
let img = ImageReader::open("test/sample.png")?.decode()?;
let image_buffer = img.into_rgb8();
let output = encode(
image_buffer.as_raw(),
image_buffer.width(),
image_buffer.height(),
);
unsafe {
let runner = JxlThreadParallelRunnerCreate(
std::ptr::null(),
JxlThreadParallelRunnerDefaultNumWorkerThreads(),
);
let dec = JxlDecoderCreate(ptr::null());
assert!(!dec.is_null());
let status =
JxlDecoderSetParallelRunner(dec, Some(JxlThreadParallelRunner), runner);
jxl_dec_assert!(status, "Set Parallel Runner");
decode(dec, &output);
JxlDecoderDestroy(dec);
JxlThreadParallelRunnerDestroy(runner);
}
Ok(())
}()
.unwrap();
}
}