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 match (cs, has_alpha) {
213 (ColorSpace::Gray, false) => {
214 image.decode_into(buf)?;
215 }
216 (ColorSpace::Gray, true) => {
217 image.decode_into(buf)?;
218 }
219 (ColorSpace::RGB, false) => {
220 image.decode_into(buf)?;
221 }
222 (ColorSpace::RGB, true) => {
223 image.decode_into(buf)?;
224 }
225 (ColorSpace::CMYK, false) => {
226 let decoded = image.decode()?;
227 let transformed = from_icc(CMYK_PROFILE, 4, has_alpha, width, height, &decoded)
228 .map_err(icc_err_to_image)?;
229 buf.copy_from_slice(&transformed);
230 }
231 (ColorSpace::CMYK, true) => {
232 let decoded = image.decode()?;
235 let mut cmyk = vec![];
236 let mut alpha = vec![];
237
238 for sample in decoded.chunks_exact(5) {
239 cmyk.extend_from_slice(&sample[..4]);
240 alpha.push(sample[4]);
241 }
242
243 let rgb = from_icc(CMYK_PROFILE, 4, false, width, height, &cmyk)
244 .map_err(icc_err_to_image)?;
245 for (out, pixel) in buf.chunks_exact_mut(4).zip(
246 rgb.chunks_exact(3)
247 .zip(alpha)
248 .map(|(rgb, alpha)| [rgb[0], rgb[1], rgb[2], alpha]),
249 ) {
250 out.copy_from_slice(&pixel);
251 }
252 }
253 (
254 ColorSpace::Icc {
255 profile,
256 num_channels: num_components,
257 },
258 has_alpha,
259 ) => {
260 let decoded = image.decode()?;
261
262 let transformed =
263 from_icc(&profile, num_components, has_alpha, width, height, &decoded);
264
265 if let Ok(transformed) = transformed {
266 buf.copy_from_slice(&transformed);
267 } else {
268 match num_components {
269 1 => process(image, buf, width, height, has_alpha, ColorSpace::Gray)?,
270 3 => process(image, buf, width, height, has_alpha, ColorSpace::RGB)?,
271 4 => process(image, buf, width, height, has_alpha, ColorSpace::CMYK)?,
272 _ => {
273 return Err(unsupported_color_error(image.original_color_type()));
274 }
275 }
276 };
277 }
278 (ColorSpace::Unknown { .. }, _) => {
279 return Err(unsupported_color_error(image.original_color_type()));
280 }
281 };
282
283 Ok(())
284 }
285
286 process(image, buf, width, height, has_alpha, color_space)
287}
288
289impl From<crate::DecodeError> for DecodingError {
290 fn from(value: crate::DecodeError) -> Self {
291 let format = ImageFormatHint::Name("JPEG2000".to_owned());
292 Self::new(format, value)
293 }
294}
295
296impl From<crate::DecodeError> for ImageError {
297 fn from(value: crate::DecodeError) -> Self {
298 Self::Decoding(value.into())
299 }
300}
301
302fn icc_err_to_image(err: CmsError) -> ImageError {
303 let format = ImageFormatHint::Name("JPEG2000".to_owned());
304 ImageError::Decoding(DecodingError::new(format, err))
305}
306
307fn unsupported_color_error(color: ExtendedColorType) -> ImageError {
308 ImageError::Unsupported(image::error::UnsupportedError::from_format_and_kind(
309 ImageFormatHint::Name("JPEG2000".to_owned()),
310 image::error::UnsupportedErrorKind::Color(color),
311 ))
312}
313
314pub fn register_decoding_hook() -> bool {
319 if decoding_hook_registered(OsStr::new("jp2")) {
320 return false;
321 }
322
323 for extension in ["jp2", "jpg2", "j2k", "jpf"] {
324 image::hooks::register_decoding_hook(
325 extension.into(),
326 Box::new(|r| Ok(Box::new(Jp2Decoder::new(r)?))),
327 );
328 register_format_detection_hook(extension.into(), crate::JP2_MAGIC, None);
329 }
330
331 for extension in ["j2c", "jpc"] {
332 image::hooks::register_decoding_hook(
333 extension.into(),
334 Box::new(|r| Ok(Box::new(Jp2Decoder::new(r)?))),
335 );
336 register_format_detection_hook(extension.into(), crate::CODESTREAM_MAGIC, None);
337 }
338
339 true
340}