1use std::mem::MaybeUninit;
21
22use image::{DynamicImage, ImageBuffer};
23use jpegxl_sys::common::types::{JxlDataType, JxlPixelFormat};
24
25use crate::{
26 common::PixelType,
27 decode::{JxlDecoder, Metadata},
28 DecodeError,
29};
30
31pub trait ToDynamic {
33 fn decode_to_image(&self, data: &[u8]) -> Result<Option<DynamicImage>, DecodeError>;
39
40 fn decode_to_image_with<T: PixelType>(
46 &self,
47 data: &[u8],
48 ) -> Result<Option<DynamicImage>, DecodeError>;
49}
50
51impl<'pr, 'mm> ToDynamic for JxlDecoder<'pr, 'mm> {
52 fn decode_to_image(&self, data: &[u8]) -> Result<Option<DynamicImage>, DecodeError> {
53 let mut buffer = vec![];
54 let mut pixel_format = MaybeUninit::uninit();
55 let metadata = self.decode_internal(
56 data,
57 None,
58 false,
59 None,
60 pixel_format.as_mut_ptr(),
61 &mut buffer,
62 )?;
63
64 let pixel_format = unsafe { pixel_format.assume_init() };
65 Ok(to_image(metadata, &pixel_format, buffer))
66 }
67
68 fn decode_to_image_with<T: PixelType>(
69 &self,
70 data: &[u8],
71 ) -> Result<Option<DynamicImage>, DecodeError> {
72 let mut buffer = vec![];
73 let mut pixel_format = MaybeUninit::uninit();
74 let metadata = self.decode_internal(
75 data,
76 Some(T::pixel_type()),
77 false,
78 None,
79 pixel_format.as_mut_ptr(),
80 &mut buffer,
81 )?;
82
83 let pixel_format = unsafe { pixel_format.assume_init() };
84 Ok(to_image(metadata, &pixel_format, buffer))
85 }
86}
87
88fn to_image(
89 Metadata { width, height, .. }: Metadata,
90 pixel_format: &JxlPixelFormat,
91 buffer: Vec<u8>,
92) -> Option<DynamicImage> {
93 match (pixel_format.data_type, pixel_format.num_channels) {
94 (JxlDataType::Float, 3) => {
95 ImageBuffer::from_raw(width, height, f32::convert(&buffer, pixel_format))
96 .map(DynamicImage::ImageRgb32F)
97 }
98 (JxlDataType::Float, 4) => {
99 ImageBuffer::from_raw(width, height, f32::convert(&buffer, pixel_format))
100 .map(DynamicImage::ImageRgba32F)
101 }
102 (JxlDataType::Uint8, 1) => {
103 ImageBuffer::from_raw(width, height, buffer).map(DynamicImage::ImageLuma8)
104 }
105 (JxlDataType::Uint8, 2) => {
106 ImageBuffer::from_raw(width, height, buffer).map(DynamicImage::ImageLumaA8)
107 }
108 (JxlDataType::Uint8, 3) => {
109 ImageBuffer::from_raw(width, height, buffer).map(DynamicImage::ImageRgb8)
110 }
111 (JxlDataType::Uint8, 4) => {
112 ImageBuffer::from_raw(width, height, buffer).map(DynamicImage::ImageRgba8)
113 }
114 (JxlDataType::Uint16, 1) => {
115 ImageBuffer::from_raw(width, height, u16::convert(&buffer, pixel_format))
116 .map(DynamicImage::ImageLuma16)
117 }
118 (JxlDataType::Uint16, 2) => {
119 ImageBuffer::from_raw(width, height, u16::convert(&buffer, pixel_format))
120 .map(DynamicImage::ImageLumaA16)
121 }
122 (JxlDataType::Uint16, 3) => {
123 ImageBuffer::from_raw(width, height, u16::convert(&buffer, pixel_format))
124 .map(DynamicImage::ImageRgb16)
125 }
126 (JxlDataType::Uint16, 4) => {
127 ImageBuffer::from_raw(width, height, u16::convert(&buffer, pixel_format))
128 .map(DynamicImage::ImageRgba16)
129 }
130 _ => None,
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use crate::{
138 decode::PixelFormat,
139 decoder_builder,
140 tests::{SAMPLE_JXL, SAMPLE_JXL_GRAY, SAMPLE_PNG},
141 ThreadsRunner,
142 };
143
144 use half::f16;
145 use pretty_assertions::assert_eq;
146 use testresult::TestResult;
147
148 #[test]
149 #[cfg_attr(coverage_nightly, coverage(off))]
150 fn invalid() -> TestResult {
151 let decoder = decoder_builder().build()?;
152 assert!(decoder.decode_to_image(&[]).is_err());
153 assert!(decoder.decode_to_image_with::<f32>(&[]).is_err());
154 Ok(())
155 }
156
157 #[test]
158 #[cfg_attr(coverage_nightly, coverage(off))]
159 fn simple() -> TestResult {
160 let parallel_runner = ThreadsRunner::default();
161 let decoder = decoder_builder()
162 .parallel_runner(¶llel_runner)
163 .build()?;
164
165 let img = decoder
166 .decode_to_image(SAMPLE_JXL)?
167 .expect("Failed to create DynamicImage");
168 let sample_png = image::load_from_memory_with_format(SAMPLE_PNG, image::ImageFormat::Png)?;
169 assert_eq!(img.to_rgba16(), sample_png.to_rgba16());
170
171 Ok(())
172 }
173
174 #[test]
175 #[cfg_attr(coverage_nightly, coverage(off))]
176 fn pixel_type() -> TestResult {
177 let parallel_runner = ThreadsRunner::default();
178 let mut decoder = decoder_builder()
179 .parallel_runner(¶llel_runner)
180 .build()?;
181 assert!(decoder.decode_to_image_with::<f16>(SAMPLE_JXL)?.is_none());
182
183 decoder.pixel_format = Some(PixelFormat {
184 num_channels: 1,
185 ..PixelFormat::default()
186 });
187 decoder
188 .decode_to_image_with::<u8>(SAMPLE_JXL_GRAY)?
189 .unwrap();
190 decoder
191 .decode_to_image_with::<u16>(SAMPLE_JXL_GRAY)?
192 .unwrap();
193 assert!(decoder
194 .decode_to_image_with::<f32>(SAMPLE_JXL_GRAY)?
195 .is_none());
196
197 decoder.pixel_format = Some(PixelFormat {
198 num_channels: 2,
199 ..PixelFormat::default()
200 });
201 decoder
202 .decode_to_image_with::<u8>(SAMPLE_JXL_GRAY)?
203 .unwrap();
204 decoder
205 .decode_to_image_with::<u16>(SAMPLE_JXL_GRAY)?
206 .unwrap();
207 assert!(decoder
208 .decode_to_image_with::<f32>(SAMPLE_JXL_GRAY)?
209 .is_none());
210
211 decoder.pixel_format = Some(PixelFormat {
212 num_channels: 3,
213 ..PixelFormat::default()
214 });
215 decoder.decode_to_image_with::<u8>(SAMPLE_JXL)?.unwrap();
216 decoder.decode_to_image_with::<u16>(SAMPLE_JXL)?.unwrap();
217 decoder.decode_to_image_with::<f32>(SAMPLE_JXL)?.unwrap();
218
219 decoder.pixel_format = Some(PixelFormat {
220 num_channels: 4,
221 ..PixelFormat::default()
222 });
223 decoder.decode_to_image_with::<u8>(SAMPLE_JXL)?.unwrap();
224 decoder.decode_to_image_with::<u16>(SAMPLE_JXL)?.unwrap();
225 decoder.decode_to_image_with::<f32>(SAMPLE_JXL)?.unwrap();
226
227 Ok(())
228 }
229}