1#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
19
20pub mod decode;
21
22pub mod color;
23pub mod common;
24pub mod encoder;
25pub mod metadata;
26pub mod threads;
27
28#[cfg(test)]
29mod test {
30 use crate::{
31 common::types::*,
32 decode::*,
33 encoder::encode::*,
34 threads::thread_parallel_runner::{
35 JxlThreadParallelRunner, JxlThreadParallelRunnerCreate,
36 JxlThreadParallelRunnerDefaultNumWorkerThreads, JxlThreadParallelRunnerDestroy,
37 },
38 };
39
40 use std::{mem::MaybeUninit, ptr};
41
42 use pretty_assertions::assert_eq;
43
44 const SAMPLE_PNG: &[u8] = include_bytes!("../../samples/sample.png");
45 const SAMPLE_JXL: &[u8] = include_bytes!("../../samples/sample.jxl");
46
47 macro_rules! jxl_dec_events {
48 ( $( $x: expr ),* ) => {
49 {
50 let mut tmp = 0;
51 $(
52 tmp |= $x as i32;
53 )*
54 tmp
55 }
56 };
57 }
58
59 macro_rules! jxl_dec_assert {
60 ($val:expr, $desc:expr) => {
61 if $val != JxlDecoderStatus::Success as _ {
62 panic!("Decoder error by: {:#?}, in {}", $val, $desc)
63 }
64 };
65 }
66
67 macro_rules! jxl_enc_assert {
68 ($val:expr, $desc:expr) => {
69 if $val != JxlEncoderStatus::Success as _ {
70 panic!("Encoder error by: {:#?}, in {}", $val, $desc)
71 }
72 };
73 }
74
75 #[test]
76 #[cfg_attr(coverage_nightly, coverage(off))]
77 fn test_bindings_version() {
78 unsafe {
79 assert_eq!(JxlDecoderVersion(), 11001);
80 assert_eq!(JxlEncoderVersion(), 11001);
81 }
82 }
83
84 #[cfg_attr(coverage_nightly, coverage(off))]
85 unsafe fn decode(decoder: *mut JxlDecoder, sample: &[u8]) {
86 use JxlDecoderStatus::{
87 BasicInfo, Error, FullImage, NeedImageOutBuffer, NeedMoreInput, Success,
88 };
89
90 let mut status = JxlDecoderSubscribeEvents(
92 decoder,
93 jxl_dec_events!(JxlDecoderStatus::BasicInfo, JxlDecoderStatus::FullImage),
94 );
95 jxl_dec_assert!(status, "Subscribe Events");
96
97 let signature = JxlSignatureCheck(sample.as_ptr(), 2);
99 assert_eq!(signature, JxlSignature::Codestream);
100
101 let next_in = sample.as_ptr();
102 let avail_in = sample.len();
103
104 let pixel_format = JxlPixelFormat {
105 num_channels: 3,
106 data_type: JxlDataType::Uint8,
107 endianness: JxlEndianness::Native,
108 align: 0,
109 };
110
111 let mut basic_info;
112 let mut buffer: Vec<f32> = Vec::new();
113 let mut x_size = 0;
114 let mut y_size = 0;
115
116 status = JxlDecoderSetInput(decoder, next_in, avail_in);
117 jxl_dec_assert!(status, "Set input");
118
119 loop {
120 status = JxlDecoderProcessInput(decoder);
121
122 match status {
123 Error => panic!("Decoder error!"),
124 NeedMoreInput => {
125 panic!("Error, already provided all input")
126 }
127
128 BasicInfo => {
130 basic_info = {
131 let mut info = MaybeUninit::uninit();
132 status = JxlDecoderGetBasicInfo(decoder, info.as_mut_ptr());
133 jxl_dec_assert!(status, "BasicInfo");
134 info.assume_init()
135 };
136
137 x_size = basic_info.xsize;
138 y_size = basic_info.ysize;
139 assert_eq!(basic_info.xsize, 40);
140 assert_eq!(basic_info.ysize, 50);
141 }
142
143 NeedImageOutBuffer => {
145 let mut size = 0;
146 status = JxlDecoderImageOutBufferSize(decoder, &pixel_format, &mut size);
147 jxl_dec_assert!(status, "BufferSize");
148
149 buffer.resize(size, 0f32);
150 status = JxlDecoderSetImageOutBuffer(
151 decoder,
152 &pixel_format,
153 buffer.as_mut_ptr().cast(),
154 size,
155 );
156 jxl_dec_assert!(status, "SetBuffer");
157 }
158
159 FullImage => {}
160 Success => {
161 assert_eq!(buffer.len(), (x_size * y_size * 3) as usize);
162 return;
163 }
164 _ => panic!("Unknown decoder status: {status:#?}"),
165 }
166 }
167 }
168
169 #[test]
170 #[cfg_attr(coverage_nightly, coverage(off))]
171 fn test_bindings_decoding() {
172 unsafe {
173 let dec = JxlDecoderCreate(ptr::null()); assert!(!dec.is_null());
175
176 decode(dec, SAMPLE_JXL);
178
179 JxlDecoderDestroy(dec);
180 }
181 }
182
183 #[test]
184 #[cfg_attr(coverage_nightly, coverage(off))]
185 fn test_bindings_thread_pool() {
186 unsafe {
187 let runner = JxlThreadParallelRunnerCreate(
188 std::ptr::null(),
189 JxlThreadParallelRunnerDefaultNumWorkerThreads(),
190 );
191
192 let dec = JxlDecoderCreate(ptr::null()); assert!(!dec.is_null());
194
195 let status = JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner);
197 jxl_dec_assert!(status, "Set Parallel Runner");
198
199 decode(dec, SAMPLE_JXL);
200
201 JxlDecoderDestroy(dec);
202 JxlThreadParallelRunnerDestroy(runner);
203 }
204 }
205
206 #[test]
207 #[cfg_attr(coverage_nightly, coverage(off))]
208 fn test_bindings_resizable() {
209 use JxlDecoderStatus::{
210 BasicInfo, Error, FullImage, NeedImageOutBuffer, NeedMoreInput, Success,
211 };
212
213 use crate::threads::resizable_parallel_runner::{
214 JxlResizableParallelRunner, JxlResizableParallelRunnerCreate,
215 JxlResizableParallelRunnerDestroy, JxlResizableParallelRunnerSetThreads,
216 JxlResizableParallelRunnerSuggestThreads,
217 };
218
219 unsafe {
220 let runner = JxlResizableParallelRunnerCreate(std::ptr::null());
221
222 let dec = JxlDecoderCreate(ptr::null()); assert!(!dec.is_null());
224
225 let status = JxlDecoderSetParallelRunner(dec, JxlResizableParallelRunner, runner);
227 jxl_dec_assert!(status, "Set Parallel Runner");
228
229 let mut status = JxlDecoderSubscribeEvents(
231 dec,
232 jxl_dec_events!(JxlDecoderStatus::BasicInfo, JxlDecoderStatus::FullImage),
233 );
234 jxl_dec_assert!(status, "Subscribe Events");
235
236 let signature = JxlSignatureCheck(SAMPLE_JXL.as_ptr(), 2);
238 assert_eq!(signature, JxlSignature::Codestream);
239
240 let next_in = SAMPLE_JXL.as_ptr();
241 let avail_in = SAMPLE_JXL.len();
242
243 let pixel_format = JxlPixelFormat {
244 num_channels: 3,
245 data_type: JxlDataType::Uint8,
246 endianness: JxlEndianness::Native,
247 align: 0,
248 };
249
250 let mut basic_info;
251 let mut buffer: Vec<f32> = Vec::new();
252 let mut x_size = 0;
253 let mut y_size = 0;
254
255 status = JxlDecoderSetInput(dec, next_in, avail_in);
256 jxl_dec_assert!(status, "Set input");
257
258 loop {
259 status = JxlDecoderProcessInput(dec);
260
261 match status {
262 Error => panic!("Decoder error!"),
263 NeedMoreInput => {
264 panic!("Error, already provided all input")
265 }
266
267 BasicInfo => {
269 basic_info = {
270 let mut info = MaybeUninit::uninit();
271 status = JxlDecoderGetBasicInfo(dec, info.as_mut_ptr());
272 jxl_dec_assert!(status, "BasicInfo");
273 info.assume_init()
274 };
275 x_size = basic_info.xsize;
276 y_size = basic_info.ysize;
277
278 let num_threads = JxlResizableParallelRunnerSuggestThreads(
279 u64::from(x_size),
280 u64::from(y_size),
281 );
282 JxlResizableParallelRunnerSetThreads(runner, num_threads as usize);
283
284 assert_eq!(basic_info.xsize, 40);
285 assert_eq!(basic_info.ysize, 50);
286 }
287
288 NeedImageOutBuffer => {
290 let mut size = 0;
291 status = JxlDecoderImageOutBufferSize(dec, &pixel_format, &mut size);
292 jxl_dec_assert!(status, "BufferSize");
293
294 buffer.resize(size, 0f32);
295 status = JxlDecoderSetImageOutBuffer(
296 dec,
297 &pixel_format,
298 buffer.as_mut_ptr().cast(),
299 size,
300 );
301 jxl_dec_assert!(status, "SetBuffer");
302 }
303
304 FullImage => {}
305 Success => {
306 assert_eq!(buffer.len(), (x_size * y_size * 3) as usize);
307 break;
308 }
309 _ => panic!("Unknown decoder status: {status:#?}"),
310 }
311 }
312
313 JxlDecoderDestroy(dec);
314 JxlResizableParallelRunnerDestroy(runner);
315 }
316 }
317
318 #[cfg_attr(coverage_nightly, coverage(off))]
319 fn encode(pixels: &[u8], x_size: u32, ysize: u32) -> Vec<u8> {
320 unsafe {
321 let enc = JxlEncoderCreate(std::ptr::null());
322
323 let runner = JxlThreadParallelRunnerCreate(
324 std::ptr::null(),
325 JxlThreadParallelRunnerDefaultNumWorkerThreads(),
326 );
327
328 let mut status = JxlEncoderSetParallelRunner(enc, JxlThreadParallelRunner, runner);
329 jxl_enc_assert!(status, "Set Parallel Runner");
330
331 let mut basic_info = {
332 let mut basic_info = MaybeUninit::uninit();
333 JxlEncoderInitBasicInfo(basic_info.as_mut_ptr());
334 basic_info.assume_init()
335 };
336 basic_info.xsize = x_size;
337 basic_info.ysize = ysize;
338
339 status = JxlEncoderSetBasicInfo(enc, &basic_info);
340 jxl_enc_assert!(status, "Set Basic Info");
341
342 let pixel_format = JxlPixelFormat {
343 num_channels: 3,
344 data_type: JxlDataType::Uint8,
345 endianness: JxlEndianness::Native,
346 align: 0,
347 };
348 let mut color_encoding = MaybeUninit::uninit();
349 JxlColorEncodingSetToSRGB(color_encoding.as_mut_ptr(), false.into());
350 status = JxlEncoderSetColorEncoding(enc, color_encoding.as_ptr());
351 jxl_enc_assert!(status, "Set Color Encoding");
352
353 status = JxlEncoderAddImageFrame(
354 JxlEncoderFrameSettingsCreate(enc, std::ptr::null()),
355 &pixel_format,
356 pixels.as_ptr().cast_mut().cast(),
357 pixels.len(),
358 );
359 jxl_enc_assert!(status, "Add Image Frame");
360
361 JxlEncoderCloseInput(enc);
362
363 let chunk_size = 1024 * 512; let mut buffer = vec![0u8; chunk_size];
365 let mut next_out = buffer.as_mut_ptr();
366 let mut avail_out = chunk_size;
367
368 loop {
369 status =
370 JxlEncoderProcessOutput(enc, std::ptr::addr_of_mut!(next_out), &mut avail_out);
371
372 if status != JxlEncoderStatus::NeedMoreOutput {
373 break;
374 }
375
376 let offset = next_out as usize - buffer.as_ptr() as usize;
377 buffer.resize(buffer.len() * 2, 0);
378 next_out = buffer.as_mut_ptr().add(offset);
379 avail_out = buffer.len() - offset;
380 }
381 buffer.truncate(next_out as usize - buffer.as_ptr() as usize);
382 jxl_enc_assert!(status, "Encoding");
383
384 JxlEncoderDestroy(enc);
385 JxlThreadParallelRunnerDestroy(runner);
386
387 buffer
388 }
389 }
390
391 #[test]
392 #[cfg_attr(coverage_nightly, coverage(off))]
393 fn test_bindings_encoding() {
394 let img = image::load_from_memory_with_format(SAMPLE_PNG, image::ImageFormat::Png).unwrap();
395 let image_buffer = img.into_rgb8();
396
397 let output = encode(
398 image_buffer.as_raw(),
399 image_buffer.width(),
400 image_buffer.height(),
401 );
402
403 unsafe {
404 let runner = JxlThreadParallelRunnerCreate(
405 std::ptr::null(),
406 JxlThreadParallelRunnerDefaultNumWorkerThreads(),
407 );
408
409 let dec = JxlDecoderCreate(ptr::null()); assert!(!dec.is_null());
411
412 let status = JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner);
413 jxl_dec_assert!(status, "Set Parallel Runner");
414
415 decode(dec, &output);
416
417 JxlDecoderDestroy(dec);
418 JxlThreadParallelRunnerDestroy(runner);
419 }
420 }
421}