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(), 11002);
80 assert_eq!(JxlEncoderVersion(), 11002);
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(
147 decoder,
148 &raw const pixel_format,
149 &raw mut size,
150 );
151 jxl_dec_assert!(status, "BufferSize");
152
153 buffer.resize(size, 0f32);
154 status = JxlDecoderSetImageOutBuffer(
155 decoder,
156 &raw const pixel_format,
157 buffer.as_mut_ptr().cast(),
158 size,
159 );
160 jxl_dec_assert!(status, "SetBuffer");
161 }
162
163 FullImage => {}
164 Success => {
165 assert_eq!(buffer.len(), (x_size * y_size * 3) as usize);
166 return;
167 }
168 _ => panic!("Unknown decoder status: {status:#?}"),
169 }
170 }
171 }
172
173 #[test]
174 #[cfg_attr(coverage_nightly, coverage(off))]
175 fn test_bindings_decoding() {
176 unsafe {
177 let dec = JxlDecoderCreate(ptr::null()); assert!(!dec.is_null());
179
180 decode(dec, SAMPLE_JXL);
182
183 JxlDecoderDestroy(dec);
184 }
185 }
186
187 #[test]
188 #[cfg_attr(coverage_nightly, coverage(off))]
189 fn test_bindings_thread_pool() {
190 unsafe {
191 let runner = JxlThreadParallelRunnerCreate(
192 std::ptr::null(),
193 JxlThreadParallelRunnerDefaultNumWorkerThreads(),
194 );
195
196 let dec = JxlDecoderCreate(ptr::null()); assert!(!dec.is_null());
198
199 let status = JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner);
201 jxl_dec_assert!(status, "Set Parallel Runner");
202
203 decode(dec, SAMPLE_JXL);
204
205 JxlDecoderDestroy(dec);
206 JxlThreadParallelRunnerDestroy(runner);
207 }
208 }
209
210 #[test]
211 #[cfg_attr(coverage_nightly, coverage(off))]
212 fn test_bindings_resizable() {
213 use JxlDecoderStatus::{
214 BasicInfo, Error, FullImage, NeedImageOutBuffer, NeedMoreInput, Success,
215 };
216
217 use crate::threads::resizable_parallel_runner::{
218 JxlResizableParallelRunner, JxlResizableParallelRunnerCreate,
219 JxlResizableParallelRunnerDestroy, JxlResizableParallelRunnerSetThreads,
220 JxlResizableParallelRunnerSuggestThreads,
221 };
222
223 unsafe {
224 let runner = JxlResizableParallelRunnerCreate(std::ptr::null());
225
226 let dec = JxlDecoderCreate(ptr::null()); assert!(!dec.is_null());
228
229 let status = JxlDecoderSetParallelRunner(dec, JxlResizableParallelRunner, runner);
231 jxl_dec_assert!(status, "Set Parallel Runner");
232
233 let mut status = JxlDecoderSubscribeEvents(
235 dec,
236 jxl_dec_events!(JxlDecoderStatus::BasicInfo, JxlDecoderStatus::FullImage),
237 );
238 jxl_dec_assert!(status, "Subscribe Events");
239
240 let signature = JxlSignatureCheck(SAMPLE_JXL.as_ptr(), 2);
242 assert_eq!(signature, JxlSignature::Codestream);
243
244 let next_in = SAMPLE_JXL.as_ptr();
245 let avail_in = SAMPLE_JXL.len();
246
247 let pixel_format = JxlPixelFormat {
248 num_channels: 3,
249 data_type: JxlDataType::Uint8,
250 endianness: JxlEndianness::Native,
251 align: 0,
252 };
253
254 let mut basic_info;
255 let mut buffer: Vec<f32> = Vec::new();
256 let mut x_size = 0;
257 let mut y_size = 0;
258
259 status = JxlDecoderSetInput(dec, next_in, avail_in);
260 jxl_dec_assert!(status, "Set input");
261
262 loop {
263 status = JxlDecoderProcessInput(dec);
264
265 match status {
266 Error => panic!("Decoder error!"),
267 NeedMoreInput => {
268 panic!("Error, already provided all input")
269 }
270
271 BasicInfo => {
273 basic_info = {
274 let mut info = MaybeUninit::uninit();
275 status = JxlDecoderGetBasicInfo(dec, info.as_mut_ptr());
276 jxl_dec_assert!(status, "BasicInfo");
277 info.assume_init()
278 };
279 x_size = basic_info.xsize;
280 y_size = basic_info.ysize;
281
282 let num_threads = JxlResizableParallelRunnerSuggestThreads(
283 u64::from(x_size),
284 u64::from(y_size),
285 );
286 JxlResizableParallelRunnerSetThreads(runner, num_threads as usize);
287
288 assert_eq!(basic_info.xsize, 40);
289 assert_eq!(basic_info.ysize, 50);
290 }
291
292 NeedImageOutBuffer => {
294 let mut size = 0;
295 status = JxlDecoderImageOutBufferSize(
296 dec,
297 &raw const pixel_format,
298 &raw mut size,
299 );
300 jxl_dec_assert!(status, "BufferSize");
301
302 buffer.resize(size, 0f32);
303 status = JxlDecoderSetImageOutBuffer(
304 dec,
305 &raw const pixel_format,
306 buffer.as_mut_ptr().cast(),
307 size,
308 );
309 jxl_dec_assert!(status, "SetBuffer");
310 }
311
312 FullImage => {}
313 Success => {
314 assert_eq!(buffer.len(), (x_size * y_size * 3) as usize);
315 break;
316 }
317 _ => panic!("Unknown decoder status: {status:#?}"),
318 }
319 }
320
321 JxlDecoderDestroy(dec);
322 JxlResizableParallelRunnerDestroy(runner);
323 }
324 }
325
326 #[cfg_attr(coverage_nightly, coverage(off))]
327 fn encode(pixels: &[u8], x_size: u32, ysize: u32) -> Vec<u8> {
328 unsafe {
329 let enc = JxlEncoderCreate(std::ptr::null());
330
331 let runner = JxlThreadParallelRunnerCreate(
332 std::ptr::null(),
333 JxlThreadParallelRunnerDefaultNumWorkerThreads(),
334 );
335
336 let mut status = JxlEncoderSetParallelRunner(enc, JxlThreadParallelRunner, runner);
337 jxl_enc_assert!(status, "Set Parallel Runner");
338
339 let mut basic_info = {
340 let mut basic_info = MaybeUninit::uninit();
341 JxlEncoderInitBasicInfo(basic_info.as_mut_ptr());
342 basic_info.assume_init()
343 };
344 basic_info.xsize = x_size;
345 basic_info.ysize = ysize;
346
347 status = JxlEncoderSetBasicInfo(enc, &raw const basic_info);
348 jxl_enc_assert!(status, "Set Basic Info");
349
350 let pixel_format = JxlPixelFormat {
351 num_channels: 3,
352 data_type: JxlDataType::Uint8,
353 endianness: JxlEndianness::Native,
354 align: 0,
355 };
356 let mut color_encoding = MaybeUninit::uninit();
357 JxlColorEncodingSetToSRGB(color_encoding.as_mut_ptr(), false.into());
358 status = JxlEncoderSetColorEncoding(enc, color_encoding.as_ptr());
359 jxl_enc_assert!(status, "Set Color Encoding");
360
361 status = JxlEncoderAddImageFrame(
362 JxlEncoderFrameSettingsCreate(enc, std::ptr::null()),
363 &raw const pixel_format,
364 pixels.as_ptr().cast_mut().cast(),
365 pixels.len(),
366 );
367 jxl_enc_assert!(status, "Add Image Frame");
368
369 JxlEncoderCloseInput(enc);
370
371 let chunk_size = 1024 * 512; let mut buffer = vec![0u8; chunk_size];
373 let mut next_out = buffer.as_mut_ptr();
374 let mut avail_out = chunk_size;
375
376 loop {
377 status = JxlEncoderProcessOutput(
378 enc,
379 std::ptr::addr_of_mut!(next_out),
380 &raw mut avail_out,
381 );
382
383 if status != JxlEncoderStatus::NeedMoreOutput {
384 break;
385 }
386
387 let offset = next_out as usize - buffer.as_ptr() as usize;
388 buffer.resize(buffer.len() * 2, 0);
389 next_out = buffer.as_mut_ptr().add(offset);
390 avail_out = buffer.len() - offset;
391 }
392 buffer.truncate(next_out as usize - buffer.as_ptr() as usize);
393 jxl_enc_assert!(status, "Encoding");
394
395 JxlEncoderDestroy(enc);
396 JxlThreadParallelRunnerDestroy(runner);
397
398 buffer
399 }
400 }
401
402 #[test]
403 #[cfg_attr(coverage_nightly, coverage(off))]
404 fn test_bindings_encoding() {
405 let img = image::load_from_memory_with_format(SAMPLE_PNG, image::ImageFormat::Png).unwrap();
406 let image_buffer = img.into_rgb8();
407
408 let output = encode(
409 image_buffer.as_raw(),
410 image_buffer.width(),
411 image_buffer.height(),
412 );
413
414 unsafe {
415 let runner = JxlThreadParallelRunnerCreate(
416 std::ptr::null(),
417 JxlThreadParallelRunnerDefaultNumWorkerThreads(),
418 );
419
420 let dec = JxlDecoderCreate(ptr::null()); assert!(!dec.is_null());
422
423 let status = JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner);
424 jxl_dec_assert!(status, "Set Parallel Runner");
425
426 decode(dec, &output);
427
428 JxlDecoderDestroy(dec);
429 JxlThreadParallelRunnerDestroy(runner);
430 }
431 }
432}