Skip to content

Instantly share code, notes, and snippets.

@qdwang
Created October 24, 2023 19:00
Show Gist options
  • Save qdwang/12304a925f1b0d3c1231a3b2dd0d63a6 to your computer and use it in GitHub Desktop.
Save qdwang/12304a925f1b0d3c1231a3b2dd0d63a6 to your computer and use it in GitHub Desktop.
cannot handle RGBA data with libjxl
#![allow(non_camel_case_types)]
use core::ffi::c_void;
const WIDTH: usize = 4;
const HEIGHT: usize = 4;
const NUM_CHANNELS: usize = 3; // 3 is ok, but 4 will cause the JXL_ENC_ERR_API_USAGE issue
fn main() {
let mut img_data = [255u8; WIDTH * HEIGHT * NUM_CHANNELS];
for s in img_data.chunks_exact_mut(NUM_CHANNELS) {
s[1] = 0;
s[2] = 0;
}
let jxl_encoder = unsafe { JxlEncoderCreate(core::ptr::null()) };
if !jxl_encoder.is_null() {
let preview_header = JxlPreviewHeader {
xsize: WIDTH as u32,
ysize: HEIGHT as u32,
};
let animation_header = JxlAnimationHeader {
tps_denominator: 0,
tps_numerator: 0,
num_loops: 0,
have_timecodes: 0,
};
let orientation = JxlOrientation::JXL_ORIENT_IDENTITY;
let info = JxlBasicInfo {
have_container: 0,
xsize: WIDTH as u32,
ysize: HEIGHT as u32,
bits_per_sample: 8,
exponent_bits_per_sample: 0,
intensity_target: 0.,
min_nits: 0.,
relative_to_max_display: 0,
linear_below: 0.,
uses_original_profile: 0,
have_preview: 0,
have_animation: 0,
orientation,
num_color_channels: 3,
alpha_bits: 0,
num_extra_channels: 0,
alpha_exponent_bits: 0,
alpha_premultiplied: 0,
preview: preview_header,
animation: animation_header,
intrinsic_xsize: WIDTH as u32,
intrinsic_ysize: HEIGHT as u32,
padding: [0; 100],
};
unsafe { JxlEncoderSetBasicInfo(jxl_encoder, &info) };
let frame_settings =
unsafe { JxlEncoderFrameSettingsCreate(jxl_encoder, core::ptr::null()) };
let pixel_format = JxlPixelFormat {
num_channels: NUM_CHANNELS as u32,
data_type: JxlDataType::JXL_TYPE_UINT8,
endianness: JxlEndianness::JXL_NATIVE_ENDIAN,
align: 0,
};
unsafe {
JxlEncoderAddImageFrame(
frame_settings,
&pixel_format,
img_data.as_mut_ptr(),
img_data.len() * core::mem::size_of::<u8>(),
);
JxlEncoderCloseFrames(jxl_encoder);
}
let mut jxl_data: Vec<u8> = Vec::with_capacity(1024);
loop {
let size = jxl_data.len();
let jxl_data_ptr = &mut jxl_data[size..].as_mut_ptr();
let mut capacity = jxl_data.capacity() - size;
let start_capacity = capacity;
let status = unsafe {
let status = JxlEncoderProcessOutput(jxl_encoder, jxl_data_ptr, &mut capacity);
jxl_data.set_len(jxl_data.len() + start_capacity - capacity);
status
};
jxl_data.reserve(1);
match status {
JxlEncoderStatus::JXL_ENC_NEED_MORE_OUTPUT => {}
JxlEncoderStatus::JXL_ENC_ERROR => panic!(),
JxlEncoderStatus::JXL_ENC_SUCCESS => break,
}
}
unsafe { JxlEncoderDestroy(jxl_encoder) };
println!("{:x?}", jxl_data);
std::fs::write("x.jxl", jxl_data).unwrap();
}
}
#[repr(C)]
pub enum JxlEncoderStatus {
JXL_ENC_SUCCESS = 0,
JXL_ENC_ERROR = 1,
JXL_ENC_NEED_MORE_OUTPUT = 2,
}
#[repr(C)]
pub struct JxlPreviewHeader {
xsize: u32,
ysize: u32,
}
#[repr(C)]
pub struct JxlAnimationHeader {
tps_numerator: u32,
tps_denominator: u32,
num_loops: u32,
have_timecodes: i32,
}
#[repr(C)]
pub enum JxlOrientation {
JXL_ORIENT_IDENTITY = 1,
JXL_ORIENT_FLIP_HORIZONTAL = 2,
JXL_ORIENT_ROTATE_180 = 3,
JXL_ORIENT_FLIP_VERTICAL = 4,
JXL_ORIENT_TRANSPOSE = 5,
JXL_ORIENT_ROTATE_90_CW = 6,
JXL_ORIENT_ANTI_TRANSPOSE = 7,
JXL_ORIENT_ROTATE_90_CCW = 8,
}
#[repr(C)]
pub struct JxlBasicInfo {
have_container: i32,
xsize: u32,
ysize: u32,
bits_per_sample: u32,
exponent_bits_per_sample: u32,
intensity_target: f32,
min_nits: f32,
relative_to_max_display: i32,
linear_below: f32,
uses_original_profile: i32,
have_preview: i32,
have_animation: i32,
orientation: JxlOrientation,
num_color_channels: u32,
num_extra_channels: u32,
alpha_bits: u32,
alpha_exponent_bits: u32,
alpha_premultiplied: i32,
preview: JxlPreviewHeader,
animation: JxlAnimationHeader,
intrinsic_xsize: u32,
intrinsic_ysize: u32,
padding: [u8; 100],
}
#[repr(C)]
pub enum JxlDataType {
JXL_TYPE_FLOAT = 0,
JXL_TYPE_UINT8 = 2,
JXL_TYPE_UINT16 = 3,
JXL_TYPE_FLOAT16 = 5,
}
#[repr(C)]
pub enum JxlEndianness {
JXL_NATIVE_ENDIAN = 0,
JXL_LITTLE_ENDIAN = 1,
JXL_BIG_ENDIAN = 2,
}
#[repr(C)]
pub struct JxlPixelFormat {
num_channels: u32,
data_type: JxlDataType,
endianness: JxlEndianness,
align: usize,
}
#[repr(C)]
pub enum JxlEncoderError {
JXL_ENC_ERR_OK = 0,
// Generic encoder error due to unspecified cause
JXL_ENC_ERR_GENERIC = 1,
// Out of memory
JXL_ENC_ERR_OOM = 2,
/** JPEG bitstream reconstruction data could not be
* represented (e.g. too much tail data)
*/
JXL_ENC_ERR_JBRD = 3,
// Input is invalid (e.g. corrupt JPEG file or ICC profile)
JXL_ENC_ERR_BAD_INPUT = 4,
/** The encoder doesn't (yet) support this. Either no version of libjxl
* supports this, and the API is used incorrectly, or the libjxl version
* should have been checked before trying to do this.
*/
JXL_ENC_ERR_NOT_SUPPORTED = 0x80,
/** The encoder API is used in an incorrect way.
* In this case, a debug build of libjxl should output a specific error
* message. (if not, please open an issue about it)
*/
JXL_ENC_ERR_API_USAGE = 0x81,
}
#[repr(C)]
pub enum JxlBitDepthType {
/** This is the default setting, where the encoder expects the input pixels
* to use the full range of the pixel format data type (e.g. for UINT16, the
* input range is 0 .. 65535 and the value 65535 is mapped to 1.0 when
* converting to float), and the decoder uses the full range to output
* pixels. If the bit depth in the basic info is different from this, the
* encoder expects the values to be rescaled accordingly (e.g. multiplied by
* 65535/4095 for a 12-bit image using UINT16 input data type). */
JXL_BIT_DEPTH_FROM_PIXEL_FORMAT = 0,
/** If this setting is selected, the encoder expects the input pixels to be
* in the range defined by the bits_per_sample value of the basic info (e.g.
* for 12-bit images using UINT16 input data types, the allowed range is
* 0 .. 4095 and the value 4095 is mapped to 1.0 when converting to float),
* and the decoder outputs pixels in this range. */
JXL_BIT_DEPTH_FROM_CODESTREAM = 1,
/** This setting can only be used in the decoder to select a custom range for
* pixel output */
JXL_BIT_DEPTH_CUSTOM = 2,
}
#[repr(C)]
pub struct JxlBitDepth {
ty: JxlBitDepthType,
bits_per_sample: u32,
exponent_bits_per_sample: u32,
}
#[link(name = "c++")]
extern "C" {
fn JxlEncoderCreate(memory_manager: *const c_void) -> *mut c_void;
fn JxlEncoderDestroy(enc: *const c_void);
fn JxlEncoderSetBasicInfo(enc: *const c_void, info: *const JxlBasicInfo) -> JxlEncoderStatus;
fn JxlEncoderFrameSettingsCreate(enc: *mut c_void, source: *const c_void) -> *mut c_void;
fn JxlEncoderAddImageFrame(
frame_settings: *const c_void,
pixel_format: *const JxlPixelFormat,
buffer: *const u8,
size: usize,
) -> JxlEncoderStatus;
fn JxlEncoderCloseFrames(enc: *mut c_void);
fn JxlEncoderProcessOutput(
enc: *mut c_void,
next_out: *mut *mut u8,
avail_out: *mut usize,
) -> JxlEncoderStatus;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment