Created
October 24, 2023 19:00
-
-
Save qdwang/12304a925f1b0d3c1231a3b2dd0d63a6 to your computer and use it in GitHub Desktop.
cannot handle RGBA data with libjxl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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