1use std::{
4 ffi::OsStr,
5 io::{BufRead, Seek},
6};
7
8use crate::{ColorSpace, DecodeSettings, Image};
9use ::image::error::{DecodingError, ImageFormatHint};
10use ::image::{ColorType, ExtendedColorType, ImageDecoder, ImageError, ImageResult};
11use image::hooks::{decoding_hook_registered, register_format_detection_hook};
12use moxcms::{CmsError, ColorProfile, Layout, TransformOptions};
13
14const CMYK_PROFILE: &[u8] = include_bytes!("../assets/CGATS001Compat-v2-micro.icc");
15
16impl ImageDecoder for Image<'_> {
17 fn dimensions(&self) -> (u32, u32) {
18 (self.width(), self.height())
19 }
20
21 fn color_type(&self) -> ColorType {
22 let channel_count = self.color_space.num_channels();
23 let has_alpha = self.has_alpha;
24
25 match (channel_count, has_alpha) {
26 (1, false) => ColorType::L8,
27 (1, true) => ColorType::La8,
28 (3, false) => ColorType::Rgb8,
29 (3, true) => ColorType::Rgba8,
30 (4, false) => ColorType::Rgb8,
32 (4, true) => ColorType::Rgba8,
33 _ => ColorType::Rgb8,
35 }
36 }
37
38 fn original_color_type(&self) -> ExtendedColorType {
39 let channel_count = self.color_space.num_channels();
40 let has_alpha = self.has_alpha;
41 let depth = self.original_bit_depth();
42 match (channel_count, depth, has_alpha) {
44 (1, 1, false) => ExtendedColorType::L1,
46 (1, 1, true) => ExtendedColorType::La1,
47 (1, 2, false) => ExtendedColorType::L2,
48 (1, 2, true) => ExtendedColorType::La2,
49 (1, 4, false) => ExtendedColorType::L4,
50 (1, 4, true) => ExtendedColorType::La4,
51 (1, 8, false) => ExtendedColorType::L8,
52 (1, 8, true) => ExtendedColorType::La8,
53 (1, 16, false) => ExtendedColorType::L8,
54 (1, 16, true) => ExtendedColorType::La8,
55 (3, 1, false) => ExtendedColorType::Rgb1,
57 (3, 1, true) => ExtendedColorType::Rgba1,
58 (3, 2, false) => ExtendedColorType::Rgb2,
59 (3, 2, true) => ExtendedColorType::Rgba2,
60 (3, 4, false) => ExtendedColorType::Rgb4,
61 (3, 4, true) => ExtendedColorType::Rgba4,
62 (3, 8, false) => ExtendedColorType::Rgb8,
63 (3, 8, true) => ExtendedColorType::Rgba8,
64 (3, 16, false) => ExtendedColorType::Rgb8,
65 (3, 16, true) => ExtendedColorType::Rgba8,
66 (4, 8, false) => ExtendedColorType::Cmyk8,
68 (4, 16, false) => ExtendedColorType::Cmyk16,
69 _ => ExtendedColorType::Unknown(orig_bits_per_pixel(self)),
71 }
72 }
73
74 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
75 where
76 Self: Sized,
77 {
78 convert_inner(&self, buf)
79 }
80
81 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
82 convert_inner(&self, buf)
83 }
84}
85
86#[doc(hidden)]
87pub struct Jp2Decoder {
89 input: Vec<u8>,
100 width: u32,
101 height: u32,
102 color_type: ColorType,
103 orig_color_type: ExtendedColorType,
104}
105
106impl Jp2Decoder {
107 pub fn new<R: BufRead + Seek>(r: R) -> ImageResult<Self> {
109 let mut input = Vec::new();
110 let mut r = r;
111 r.read_to_end(&mut input)?;
112 let headers = Image::new(&input, &DecodeSettings::default())?;
113 Ok(Self {
114 width: headers.width(),
115 height: headers.height(),
116 color_type: headers.color_type(),
117 orig_color_type: headers.original_color_type(),
118 input,
119 })
120 }
121}
122
123impl ImageDecoder for Jp2Decoder {
124 fn dimensions(&self) -> (u32, u32) {
125 (self.width, self.height)
126 }
127
128 fn color_type(&self) -> ColorType {
129 self.color_type
130 }
131
132 fn original_color_type(&self) -> ExtendedColorType {
133 self.orig_color_type
134 }
135
136 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
137 where
138 Self: Sized,
139 {
140 let decoder = Image::new(&self.input, &DecodeSettings::default()).unwrap();
142 decoder.read_image(buf)
143 }
144
145 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
146 let decoder = Image::new(&self.input, &DecodeSettings::default()).unwrap();
148 decoder.read_image(buf)
149 }
150}
151
152fn orig_bits_per_pixel(img: &Image<'_>) -> u8 {
154 let mut channel_count = img.color_space().num_channels();
155 if img.has_alpha {
156 channel_count += 1;
157 }
158 channel_count * img.original_bit_depth()
159}
160
161fn convert_inner(image: &Image<'_>, buf: &mut [u8]) -> ImageResult<()> {
162 let width = image.width();
163 let height = image.height();
164 let color_space = image.color_space().clone();
165 let has_alpha = image.has_alpha();
166
167 fn from_icc(
168 icc: &[u8],
169 num_channels: u8,
170 has_alpha: bool,
171 width: u32,
172 height: u32,
173 input_data: &[u8],
174 ) -> Result<Vec<u8>, CmsError> {
175 let src_profile = ColorProfile::new_from_slice(icc)?;
176 let dest_profile = ColorProfile::new_srgb();
177
178 let (src_layout, dest_layout, out_channels) = match (num_channels, has_alpha) {
179 (1, false) => (Layout::Gray, Layout::Gray, 1),
180 (1, true) => (Layout::GrayAlpha, Layout::GrayAlpha, 2),
181 (3, false) => (Layout::Rgb, Layout::Rgb, 3),
182 (3, true) => (Layout::Rgba, Layout::Rgba, 4),
183 (4, false) => (Layout::Rgba, Layout::Rgb, 3),
185 _ => {
186 return Err(CmsError::UnsupportedChannelConfiguration);
187 }
188 };
189
190 let transform = src_profile.create_transform_8bit(
191 src_layout,
192 &dest_profile,
193 dest_layout,
194 TransformOptions::default(),
195 )?;
196
197 let mut transformed = vec![0; (width * height * out_channels) as usize];
198
199 transform.transform(input_data, &mut transformed)?;
200
201 Ok(transformed)
202 }
203
204 fn process(
205 image: &Image<'_>,
206 buf: &mut [u8],
207 width: u32,
208 height: u32,
209 has_alpha: bool,
210 cs: ColorSpace,
211 ) -> Result<(), ImageError> {
212 let mut decoder_context = crate::DecoderContext::default();
213
214 match (cs, has_alpha) {
215 (ColorSpace::Gray, false) => {
216 image.decode_into(buf, &mut decoder_context)?;
217 }
218 (ColorSpace::Gray, true) => {
219 image.decode_into(buf, &mut decoder_context)?;
220 }
221 (ColorSpace::RGB, false) => {
222 image.decode_into(buf, &mut decoder_context)?;
223 }
224 (ColorSpace::RGB, true) => {
225 image.decode_into(buf, &mut decoder_context)?;
226 }
227 (ColorSpace::CMYK, false) => {
228 let decoded = image.decode()?;
229 let transformed = from_icc(CMYK_PROFILE, 4, has_alpha, width, height, &decoded)
230 .map_err(icc_err_to_image)?;
231 buf.copy_from_slice(&transformed);
232 }
233 (ColorSpace::CMYK, true) => {
234 let decoded = image.decode()?;
237 let mut cmyk = vec![];
238 let mut alpha = vec![];
239
240 for sample in decoded.chunks_exact(5) {
241 cmyk.extend_from_slice(&sample[..4]);
242 alpha.push(sample[4]);
243 }
244
245 let rgb = from_icc(CMYK_PROFILE, 4, false, width, height, &cmyk)
246 .map_err(icc_err_to_image)?;
247 for (out, pixel) in buf.chunks_exact_mut(4).zip(
248 rgb.chunks_exact(3)
249 .zip(alpha)
250 .map(|(rgb, alpha)| [rgb[0], rgb[1], rgb[2], alpha]),
251 ) {
252 out.copy_from_slice(&pixel);
253 }
254 }
255 (
256 ColorSpace::Icc {
257 profile,
258 num_channels: num_components,
259 },
260 has_alpha,
261 ) => {
262 let decoded = image.decode()?;
263
264 let transformed =
265 from_icc(&profile, num_components, has_alpha, width, height, &decoded);
266
267 if let Ok(transformed) = transformed {
268 buf.copy_from_slice(&transformed);
269 } else {
270 match num_components {
271 1 => process(image, buf, width, height, has_alpha, ColorSpace::Gray)?,
272 3 => process(image, buf, width, height, has_alpha, ColorSpace::RGB)?,
273 4 => process(image, buf, width, height, has_alpha, ColorSpace::CMYK)?,
274 _ => {
275 return Err(unsupported_color_error(image.original_color_type()));
276 }
277 }
278 };
279 }
280 (ColorSpace::Unknown { .. }, _) => {
281 return Err(unsupported_color_error(image.original_color_type()));
282 }
283 };
284
285 Ok(())
286 }
287
288 process(image, buf, width, height, has_alpha, color_space)
289}
290
291impl From<crate::DecodeError> for DecodingError {
292 fn from(value: crate::DecodeError) -> Self {
293 let format = ImageFormatHint::Name("JPEG2000".to_owned());
294 Self::new(format, value)
295 }
296}
297
298impl From<crate::DecodeError> for ImageError {
299 fn from(value: crate::DecodeError) -> Self {
300 Self::Decoding(value.into())
301 }
302}
303
304fn icc_err_to_image(err: CmsError) -> ImageError {
305 let format = ImageFormatHint::Name("JPEG2000".to_owned());
306 ImageError::Decoding(DecodingError::new(format, err))
307}
308
309fn unsupported_color_error(color: ExtendedColorType) -> ImageError {
310 ImageError::Unsupported(image::error::UnsupportedError::from_format_and_kind(
311 ImageFormatHint::Name("JPEG2000".to_owned()),
312 image::error::UnsupportedErrorKind::Color(color),
313 ))
314}
315
316pub fn register_decoding_hook() -> bool {
321 if decoding_hook_registered(OsStr::new("jp2")) {
322 return false;
323 }
324
325 for extension in ["jp2", "jpg2", "j2k", "jpf"] {
326 image::hooks::register_decoding_hook(
327 extension.into(),
328 Box::new(|r| Ok(Box::new(Jp2Decoder::new(r)?))),
329 );
330 register_format_detection_hook(extension.into(), crate::JP2_MAGIC, None);
331 }
332
333 for extension in ["j2c", "jpc"] {
334 image::hooks::register_decoding_hook(
335 extension.into(),
336 Box::new(|r| Ok(Box::new(Jp2Decoder::new(r)?))),
337 );
338 register_format_detection_hook(extension.into(), crate::CODESTREAM_MAGIC, None);
339 }
340
341 true
342}