1#![cfg_attr(not(feature = "std"), no_std)]
65#![forbid(unsafe_code)]
66#![forbid(missing_docs)]
67
68extern crate alloc;
69
70use alloc::vec;
71use alloc::vec::Vec;
72
73use crate::error::{bail, err};
74use crate::j2c::{ComponentData, DecodedCodestream, Header};
75use crate::jp2::cdef::{ChannelAssociation, ChannelType};
76use crate::jp2::cmap::ComponentMappingType;
77use crate::jp2::colr::{CieLab, EnumeratedColorspace};
78use crate::jp2::icc::ICCMetadata;
79use crate::jp2::{DecodedImage, ImageBoxes};
80
81pub mod error;
82#[macro_use]
83pub(crate) mod log;
84pub(crate) mod math;
85
86use crate::math::{Level, SIMD_WIDTH, Simd, dispatch, f32x8};
87pub use error::{
88 ColorError, DecodeError, DecodingError, FormatError, MarkerError, Result, TileError,
89 ValidationError,
90};
91
92#[cfg(feature = "image")]
93pub mod integration;
94mod j2c;
95mod jp2;
96pub(crate) mod reader;
97
98pub(crate) const JP2_MAGIC: &[u8] = b"\x00\x00\x00\x0C\x6A\x50\x20\x20";
100pub(crate) const CODESTREAM_MAGIC: &[u8] = b"\xFF\x4F\xFF\x51";
102
103#[derive(Debug, Copy, Clone)]
105pub struct DecodeSettings {
106 pub resolve_palette_indices: bool,
118 pub strict: bool,
123 pub target_resolution: Option<(u32, u32)>,
125}
126
127impl Default for DecodeSettings {
128 fn default() -> Self {
129 Self {
130 resolve_palette_indices: true,
131 strict: false,
132 target_resolution: None,
133 }
134 }
135}
136
137pub struct Image<'a> {
139 pub(crate) codestream: &'a [u8],
141 pub(crate) header: Header<'a>,
143 pub(crate) boxes: ImageBoxes,
146 pub(crate) settings: DecodeSettings,
148 pub(crate) has_alpha: bool,
150 pub(crate) color_space: ColorSpace,
152}
153
154impl<'a> Image<'a> {
155 pub fn new(data: &'a [u8], settings: &DecodeSettings) -> Result<Self> {
157 if data.starts_with(JP2_MAGIC) {
158 jp2::parse(data, *settings)
159 } else if data.starts_with(CODESTREAM_MAGIC) {
160 j2c::parse(data, settings)
161 } else {
162 err!(FormatError::InvalidSignature)
163 }
164 }
165
166 pub fn has_alpha(&self) -> bool {
168 self.has_alpha
169 }
170
171 pub fn color_space(&self) -> &ColorSpace {
173 &self.color_space
174 }
175
176 pub fn width(&self) -> u32 {
178 self.header.size_data.image_width()
179 }
180
181 pub fn height(&self) -> u32 {
183 self.header.size_data.image_height()
184 }
185
186 pub fn original_bit_depth(&self) -> u8 {
189 self.header.component_infos[0].size_info.precision
191 }
192
193 pub fn decode(&self) -> Result<Vec<u8>> {
195 let buffer_size = self.width() as usize
196 * self.height() as usize
197 * (self.color_space.num_channels() as usize + if self.has_alpha { 1 } else { 0 });
198 let mut buf = vec![0; buffer_size];
199 self.decode_into(&mut buf)?;
200
201 Ok(buf)
202 }
203
204 pub(crate) fn decode_into(&self, buf: &mut [u8]) -> Result<()> {
207 let settings = &self.settings;
208 let mut decoded_image =
209 j2c::decode(self.codestream, &self.header).map(move |data| DecodedImage {
210 decoded: DecodedCodestream { components: data },
211 boxes: self.boxes.clone(),
212 })?;
213
214 if settings.resolve_palette_indices {
216 decoded_image.decoded.components =
217 resolve_palette_indices(decoded_image.decoded.components, &decoded_image.boxes)?;
218 }
219
220 if let Some(cdef) = &decoded_image.boxes.channel_definition {
221 let mut components = decoded_image
224 .decoded
225 .components
226 .into_iter()
227 .zip(
228 cdef.channel_definitions
229 .iter()
230 .map(|c| match c._association {
231 ChannelAssociation::WholeImage => u16::MAX,
232 ChannelAssociation::Colour(c) => c,
233 }),
234 )
235 .collect::<Vec<_>>();
236 components.sort_by(|c1, c2| c1.1.cmp(&c2.1));
237 decoded_image.decoded.components = components.into_iter().map(|c| c.0).collect();
238 }
239
240 let bit_depth = decoded_image.decoded.components[0].bit_depth;
242 convert_color_space(&mut decoded_image, bit_depth)?;
243
244 interleave_and_convert(decoded_image, buf);
245
246 Ok(())
247 }
248}
249
250pub(crate) fn resolve_alpha_and_color_space(
251 boxes: &ImageBoxes,
252 header: &Header<'_>,
253 settings: &DecodeSettings,
254) -> Result<(ColorSpace, bool)> {
255 let mut num_components = header.component_infos.len();
256
257 if settings.resolve_palette_indices
260 && let Some(palette_box) = &boxes.palette
261 {
262 num_components = palette_box.columns.len();
263 }
264
265 let mut has_alpha = false;
266
267 if let Some(cdef) = &boxes.channel_definition {
268 let last = cdef.channel_definitions.last().unwrap();
269 has_alpha = last.channel_type == ChannelType::Opacity;
270 }
271
272 let mut color_space = get_color_space(boxes, num_components)?;
273
274 if !settings.resolve_palette_indices && boxes.palette.is_some() {
276 has_alpha = false;
277 color_space = ColorSpace::Gray;
278 }
279
280 let actual_num_components = header.component_infos.len();
281
282 if boxes.palette.is_none()
284 && actual_num_components
285 != (color_space.num_channels() + if has_alpha { 1 } else { 0 }) as usize
286 {
287 if !settings.strict
288 && actual_num_components == color_space.num_channels() as usize + 1
289 && !has_alpha
290 {
291 has_alpha = true;
294 } else {
295 if actual_num_components == 1 || (actual_num_components == 2 && has_alpha) {
297 color_space = ColorSpace::Gray;
298 } else if actual_num_components == 3 {
299 color_space = ColorSpace::RGB;
300 } else if actual_num_components == 4 {
301 if has_alpha {
302 color_space = ColorSpace::RGB;
303 } else {
304 color_space = ColorSpace::CMYK;
305 }
306 } else {
307 bail!(ValidationError::TooManyChannels);
308 }
309 }
310 }
311
312 Ok((color_space, has_alpha))
313}
314
315#[derive(Debug, Clone)]
317pub enum ColorSpace {
318 Gray,
320 RGB,
322 CMYK,
324 Unknown {
326 num_channels: u8,
328 },
329 Icc {
331 profile: Vec<u8>,
333 num_channels: u8,
335 },
336}
337
338impl ColorSpace {
339 pub fn num_channels(&self) -> u8 {
341 match self {
342 Self::Gray => 1,
343 Self::RGB => 3,
344 Self::CMYK => 4,
345 Self::Unknown { num_channels } => *num_channels,
346 Self::Icc {
347 num_channels: num_components,
348 ..
349 } => *num_components,
350 }
351 }
352}
353
354pub struct Bitmap {
356 pub color_space: ColorSpace,
358 pub data: Vec<u8>,
367 pub has_alpha: bool,
369 pub width: u32,
371 pub height: u32,
373 pub original_bit_depth: u8,
376}
377
378fn interleave_and_convert(image: DecodedImage, buf: &mut [u8]) {
379 let mut components = image.decoded.components;
380 let num_components = components.len();
381
382 let mut all_same_bit_depth = Some(components[0].bit_depth);
383
384 for component in components.iter().skip(1) {
385 if Some(component.bit_depth) != all_same_bit_depth {
386 all_same_bit_depth = None;
387 }
388 }
389
390 let max_len = components[0].container.truncated().len();
391
392 let mut output_iter = buf.iter_mut();
393
394 if all_same_bit_depth == Some(8) && num_components <= 4 {
395 match num_components {
397 1 => {
399 for (output, input) in output_iter.zip(
400 components[0]
401 .container
402 .iter()
403 .map(|v| math::round_f32(*v) as u8),
404 ) {
405 *output = input;
406 }
407 }
408 2 => {
410 let c1 = components.pop().unwrap();
411 let c0 = components.pop().unwrap();
412
413 let c0 = &c0.container[..max_len];
414 let c1 = &c1.container[..max_len];
415
416 for i in 0..max_len {
417 *output_iter.next().unwrap() = math::round_f32(c0[i]) as u8;
418 *output_iter.next().unwrap() = math::round_f32(c1[i]) as u8;
419 }
420 }
421 3 => {
423 let c2 = components.pop().unwrap();
424 let c1 = components.pop().unwrap();
425 let c0 = components.pop().unwrap();
426
427 let c0 = &c0.container[..max_len];
428 let c1 = &c1.container[..max_len];
429 let c2 = &c2.container[..max_len];
430
431 for i in 0..max_len {
432 *output_iter.next().unwrap() = math::round_f32(c0[i]) as u8;
433 *output_iter.next().unwrap() = math::round_f32(c1[i]) as u8;
434 *output_iter.next().unwrap() = math::round_f32(c2[i]) as u8;
435 }
436 }
437 4 => {
439 let c3 = components.pop().unwrap();
440 let c2 = components.pop().unwrap();
441 let c1 = components.pop().unwrap();
442 let c0 = components.pop().unwrap();
443
444 let c0 = &c0.container[..max_len];
445 let c1 = &c1.container[..max_len];
446 let c2 = &c2.container[..max_len];
447 let c3 = &c3.container[..max_len];
448
449 for i in 0..max_len {
450 *output_iter.next().unwrap() = math::round_f32(c0[i]) as u8;
451 *output_iter.next().unwrap() = math::round_f32(c1[i]) as u8;
452 *output_iter.next().unwrap() = math::round_f32(c2[i]) as u8;
453 *output_iter.next().unwrap() = math::round_f32(c3[i]) as u8;
454 }
455 }
456 _ => unreachable!(),
457 }
458 } else {
459 let mul_factor = ((1 << 8) - 1) as f32;
461
462 for sample in 0..max_len {
463 for channel in components.iter() {
464 *output_iter.next().unwrap() = math::round_f32(
465 (channel.container[sample] / ((1_u32 << channel.bit_depth) - 1) as f32)
466 * mul_factor,
467 ) as u8;
468 }
469 }
470 }
471}
472
473fn convert_color_space(image: &mut DecodedImage, bit_depth: u8) -> Result<()> {
474 if let Some(jp2::colr::ColorSpace::Enumerated(e)) = &image
475 .boxes
476 .color_specification
477 .as_ref()
478 .map(|i| &i.color_space)
479 {
480 match e {
481 EnumeratedColorspace::Sycc => {
482 dispatch!(Level::new(), simd => {
483 sycc_to_rgb(simd, &mut image.decoded.components, bit_depth)
484 })?;
485 }
486 EnumeratedColorspace::CieLab(cielab) => {
487 dispatch!(Level::new(), simd => {
488 cielab_to_rgb(simd, &mut image.decoded.components, bit_depth, cielab)
489 })?;
490 }
491 _ => {}
492 }
493 }
494
495 Ok(())
496}
497
498fn get_color_space(boxes: &ImageBoxes, num_components: usize) -> Result<ColorSpace> {
499 let cs = match boxes
500 .color_specification
501 .as_ref()
502 .map(|c| &c.color_space)
503 .unwrap_or(&jp2::colr::ColorSpace::Unknown)
504 {
505 jp2::colr::ColorSpace::Enumerated(e) => {
506 match e {
507 EnumeratedColorspace::Cmyk => ColorSpace::CMYK,
508 EnumeratedColorspace::Srgb => ColorSpace::RGB,
509 EnumeratedColorspace::RommRgb => {
510 ColorSpace::Icc {
512 profile: include_bytes!("../assets/ISO22028-2_ROMM-RGB.icc").to_vec(),
513 num_channels: 3,
514 }
515 }
516 EnumeratedColorspace::EsRgb => ColorSpace::RGB,
517 EnumeratedColorspace::Greyscale => ColorSpace::Gray,
518 EnumeratedColorspace::Sycc => ColorSpace::RGB,
519 EnumeratedColorspace::CieLab(_) => ColorSpace::Icc {
520 profile: include_bytes!("../assets/LAB.icc").to_vec(),
521 num_channels: 3,
522 },
523 _ => bail!(FormatError::Unsupported),
524 }
525 }
526 jp2::colr::ColorSpace::Icc(icc) => {
527 if let Some(metadata) = ICCMetadata::from_data(icc) {
528 ColorSpace::Icc {
529 profile: icc.clone(),
530 num_channels: metadata.color_space.num_components(),
531 }
532 } else {
533 ColorSpace::RGB
538 }
539 }
540 jp2::colr::ColorSpace::Unknown => match num_components {
541 1 => ColorSpace::Gray,
542 3 => ColorSpace::RGB,
543 4 => ColorSpace::CMYK,
544 _ => ColorSpace::Unknown {
545 num_channels: num_components as u8,
546 },
547 },
548 };
549
550 Ok(cs)
551}
552
553fn resolve_palette_indices(
554 components: Vec<ComponentData>,
555 boxes: &ImageBoxes,
556) -> Result<Vec<ComponentData>> {
557 let Some(palette) = boxes.palette.as_ref() else {
558 return Ok(components);
560 };
561
562 let mapping = boxes.component_mapping.as_ref().unwrap();
563 let mut resolved = Vec::with_capacity(mapping.entries.len());
564
565 for entry in &mapping.entries {
566 let component_idx = entry.component_index as usize;
567 let component = components
568 .get(component_idx)
569 .ok_or(ColorError::PaletteResolutionFailed)?;
570
571 match entry.mapping_type {
572 ComponentMappingType::Direct => resolved.push(component.clone()),
573 ComponentMappingType::Palette { column } => {
574 let column_idx = column as usize;
575 let column_info = palette
576 .columns
577 .get(column_idx)
578 .ok_or(ColorError::PaletteResolutionFailed)?;
579
580 let mut mapped =
581 Vec::with_capacity(component.container.truncated().len() + SIMD_WIDTH);
582
583 for &sample in component.container.truncated() {
584 let index = math::round_f32(sample) as i64;
585 let value = palette
586 .map(index as usize, column_idx)
587 .ok_or(ColorError::PaletteResolutionFailed)?;
588 mapped.push(value as f32);
589 }
590
591 resolved.push(ComponentData {
592 container: math::SimdBuffer::new(mapped),
593 bit_depth: column_info.bit_depth,
594 });
595 }
596 }
597 }
598
599 Ok(resolved)
600}
601
602#[inline(always)]
603fn cielab_to_rgb<S: Simd>(
604 simd: S,
605 components: &mut [ComponentData],
606 bit_depth: u8,
607 lab: &CieLab,
608) -> Result<()> {
609 let (head, _) = components
610 .split_at_mut_checked(3)
611 .ok_or(ColorError::LabConversionFailed)?;
612
613 let [l, a, b] = head else {
614 unreachable!();
615 };
616
617 let prec0 = l.bit_depth;
618 let prec1 = a.bit_depth;
619 let prec2 = b.bit_depth;
620
621 if prec0 < 4 || prec1 < 4 || prec2 < 4 {
623 bail!(ColorError::LabConversionFailed);
624 }
625
626 let rl = lab.rl.unwrap_or(100);
627 let ra = lab.ra.unwrap_or(170);
628 let rb = lab.ra.unwrap_or(200);
629 let ol = lab.ol.unwrap_or(0);
630 let oa = lab.oa.unwrap_or(1 << (bit_depth - 1));
631 let ob = lab
632 .ob
633 .unwrap_or((1 << (bit_depth - 2)) + (1 << (bit_depth - 3)));
634
635 let min_l = -(rl as f32 * ol as f32) / ((1 << prec0) - 1) as f32;
637 let max_l = min_l + rl as f32;
638 let min_a = -(ra as f32 * oa as f32) / ((1 << prec1) - 1) as f32;
639 let max_a = min_a + ra as f32;
640 let min_b = -(rb as f32 * ob as f32) / ((1 << prec2) - 1) as f32;
641 let max_b = min_b + rb as f32;
642
643 let bit_max = (1_u32 << bit_depth) - 1;
644
645 let divisor_l = ((1 << prec0) - 1) as f32;
649 let divisor_a = ((1 << prec1) - 1) as f32;
650 let divisor_b = ((1 << prec2) - 1) as f32;
651
652 let scale_l_final = bit_max as f32 / 100.0;
653 let scale_ab_final = bit_max as f32 / 255.0;
654
655 let l_offset = min_l * scale_l_final;
656 let l_scale = (max_l - min_l) / divisor_l * scale_l_final;
657 let a_offset = (min_a + 128.0) * scale_ab_final;
658 let a_scale = (max_a - min_a) / divisor_a * scale_ab_final;
659 let b_offset = (min_b + 128.0) * scale_ab_final;
660 let b_scale = (max_b - min_b) / divisor_b * scale_ab_final;
661
662 let l_offset_v = f32x8::splat(simd, l_offset);
663 let l_scale_v = f32x8::splat(simd, l_scale);
664 let a_offset_v = f32x8::splat(simd, a_offset);
665 let a_scale_v = f32x8::splat(simd, a_scale);
666 let b_offset_v = f32x8::splat(simd, b_offset);
667 let b_scale_v = f32x8::splat(simd, b_scale);
668
669 for ((l_chunk, a_chunk), b_chunk) in l
673 .container
674 .chunks_exact_mut(SIMD_WIDTH)
675 .zip(a.container.chunks_exact_mut(SIMD_WIDTH))
676 .zip(b.container.chunks_exact_mut(SIMD_WIDTH))
677 {
678 let l_v = f32x8::from_slice(simd, l_chunk);
679 let a_v = f32x8::from_slice(simd, a_chunk);
680 let b_v = f32x8::from_slice(simd, b_chunk);
681
682 l_v.mul_add(l_scale_v, l_offset_v).store(l_chunk);
683 a_v.mul_add(a_scale_v, a_offset_v).store(a_chunk);
684 b_v.mul_add(b_scale_v, b_offset_v).store(b_chunk);
685 }
686
687 Ok(())
688}
689
690#[inline(always)]
691fn sycc_to_rgb<S: Simd>(simd: S, components: &mut [ComponentData], bit_depth: u8) -> Result<()> {
692 let offset = (1_u32 << (bit_depth as u32 - 1)) as f32;
693 let max_value = ((1_u32 << bit_depth as u32) - 1) as f32;
694
695 let (head, _) = components
696 .split_at_mut_checked(3)
697 .ok_or(ColorError::SyccConversionFailed)?;
698
699 let [y, cb, cr] = head else {
700 unreachable!();
701 };
702
703 let offset_v = f32x8::splat(simd, offset);
704 let max_v = f32x8::splat(simd, max_value);
705 let zero_v = f32x8::splat(simd, 0.0);
706 let cr_to_r = f32x8::splat(simd, 1.402);
707 let cb_to_g = f32x8::splat(simd, -0.344136);
708 let cr_to_g = f32x8::splat(simd, -0.714136);
709 let cb_to_b = f32x8::splat(simd, 1.772);
710
711 for ((y_chunk, cb_chunk), cr_chunk) in y
712 .container
713 .chunks_exact_mut(SIMD_WIDTH)
714 .zip(cb.container.chunks_exact_mut(SIMD_WIDTH))
715 .zip(cr.container.chunks_exact_mut(SIMD_WIDTH))
716 {
717 let y_v = f32x8::from_slice(simd, y_chunk);
718 let cb_v = f32x8::from_slice(simd, cb_chunk) - offset_v;
719 let cr_v = f32x8::from_slice(simd, cr_chunk) - offset_v;
720
721 let r = cr_v.mul_add(cr_to_r, y_v);
723 let g = cr_v.mul_add(cr_to_g, cb_v.mul_add(cb_to_g, y_v));
725 let b = cb_v.mul_add(cb_to_b, y_v);
727
728 r.min(max_v).max(zero_v).store(y_chunk);
729 g.min(max_v).max(zero_v).store(cb_chunk);
730 b.min(max_v).max(zero_v).store(cr_chunk);
731 }
732
733 Ok(())
734}