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 total_channels =
196 self.color_space.num_channels() as usize + if self.has_alpha { 1 } else { 0 };
197 let buffer_size = (self.width() as usize)
200 .checked_mul(self.height() as usize)
201 .and_then(|n| n.checked_mul(total_channels))
202 .ok_or(DecodeError::Validation(ValidationError::ImageTooLarge))?;
203 let mut buf = vec![0; buffer_size];
204 self.decode_into(&mut buf)?;
205
206 Ok(buf)
207 }
208
209 pub(crate) fn decode_into(&self, buf: &mut [u8]) -> Result<()> {
212 let settings = &self.settings;
213 let mut decoded_image =
214 j2c::decode(self.codestream, &self.header).map(move |data| DecodedImage {
215 decoded: DecodedCodestream { components: data },
216 boxes: self.boxes.clone(),
217 })?;
218
219 if settings.resolve_palette_indices {
221 decoded_image.decoded.components =
222 resolve_palette_indices(decoded_image.decoded.components, &decoded_image.boxes)?;
223 }
224
225 if let Some(cdef) = &decoded_image.boxes.channel_definition {
226 let mut components = decoded_image
229 .decoded
230 .components
231 .into_iter()
232 .zip(
233 cdef.channel_definitions
234 .iter()
235 .map(|c| match c._association {
236 ChannelAssociation::WholeImage => u16::MAX,
237 ChannelAssociation::Colour(c) => c,
238 }),
239 )
240 .collect::<Vec<_>>();
241 components.sort_by_key(|c1| c1.1);
242 decoded_image.decoded.components = components.into_iter().map(|c| c.0).collect();
243 }
244
245 let bit_depth = decoded_image.decoded.components[0].bit_depth;
247 convert_color_space(&mut decoded_image, bit_depth)?;
248
249 interleave_and_convert(decoded_image, buf);
250
251 Ok(())
252 }
253}
254
255pub(crate) fn resolve_alpha_and_color_space(
256 boxes: &ImageBoxes,
257 header: &Header<'_>,
258 settings: &DecodeSettings,
259) -> Result<(ColorSpace, bool)> {
260 let mut num_components = header.component_infos.len();
261
262 if settings.resolve_palette_indices
265 && let Some(palette_box) = &boxes.palette
266 {
267 num_components = palette_box.columns.len();
268 }
269
270 let mut has_alpha = false;
271
272 if let Some(cdef) = &boxes.channel_definition {
273 let last = cdef.channel_definitions.last().unwrap();
274 has_alpha = last.channel_type == ChannelType::Opacity;
275 }
276
277 let mut color_space = get_color_space(boxes, num_components)?;
278
279 if !settings.resolve_palette_indices && boxes.palette.is_some() {
281 has_alpha = false;
282 color_space = ColorSpace::Gray;
283 }
284
285 let actual_num_components = header.component_infos.len();
286
287 if boxes.palette.is_none()
289 && actual_num_components
290 != (color_space.num_channels() + if has_alpha { 1 } else { 0 }) as usize
291 {
292 if !settings.strict
293 && actual_num_components == color_space.num_channels() as usize + 1
294 && !has_alpha
295 {
296 has_alpha = true;
299 } else {
300 if actual_num_components == 1 || (actual_num_components == 2 && has_alpha) {
302 color_space = ColorSpace::Gray;
303 } else if actual_num_components == 3 {
304 color_space = ColorSpace::RGB;
305 } else if actual_num_components == 4 {
306 if has_alpha {
307 color_space = ColorSpace::RGB;
308 } else {
309 color_space = ColorSpace::CMYK;
310 }
311 } else {
312 bail!(ValidationError::TooManyChannels);
313 }
314 }
315 }
316
317 Ok((color_space, has_alpha))
318}
319
320#[derive(Debug, Clone)]
322pub enum ColorSpace {
323 Gray,
325 RGB,
327 CMYK,
329 Unknown {
331 num_channels: u8,
333 },
334 Icc {
336 profile: Vec<u8>,
338 num_channels: u8,
340 },
341}
342
343impl ColorSpace {
344 pub fn num_channels(&self) -> u8 {
346 match self {
347 Self::Gray => 1,
348 Self::RGB => 3,
349 Self::CMYK => 4,
350 Self::Unknown { num_channels } => *num_channels,
351 Self::Icc {
352 num_channels: num_components,
353 ..
354 } => *num_components,
355 }
356 }
357}
358
359pub struct Bitmap {
361 pub color_space: ColorSpace,
363 pub data: Vec<u8>,
372 pub has_alpha: bool,
374 pub width: u32,
376 pub height: u32,
378 pub original_bit_depth: u8,
381}
382
383fn interleave_and_convert(image: DecodedImage, buf: &mut [u8]) {
384 let mut components = image.decoded.components;
385 let num_components = components.len();
386
387 let mut all_same_bit_depth = Some(components[0].bit_depth);
388
389 for component in components.iter().skip(1) {
390 if Some(component.bit_depth) != all_same_bit_depth {
391 all_same_bit_depth = None;
392 }
393 }
394
395 let max_len = components[0].container.truncated().len();
396
397 let mut output_iter = buf.iter_mut();
398
399 if all_same_bit_depth == Some(8) && num_components <= 4 {
400 match num_components {
402 1 => {
404 for (output, input) in output_iter.zip(
405 components[0]
406 .container
407 .iter()
408 .map(|v| math::round_f32(*v) as u8),
409 ) {
410 *output = input;
411 }
412 }
413 2 => {
415 let c1 = components.pop().unwrap();
416 let c0 = components.pop().unwrap();
417
418 let c0 = &c0.container[..max_len];
419 let c1 = &c1.container[..max_len];
420
421 for i in 0..max_len {
422 *output_iter.next().unwrap() = math::round_f32(c0[i]) as u8;
423 *output_iter.next().unwrap() = math::round_f32(c1[i]) as u8;
424 }
425 }
426 3 => {
428 let c2 = components.pop().unwrap();
429 let c1 = components.pop().unwrap();
430 let c0 = components.pop().unwrap();
431
432 let c0 = &c0.container[..max_len];
433 let c1 = &c1.container[..max_len];
434 let c2 = &c2.container[..max_len];
435
436 for i in 0..max_len {
437 *output_iter.next().unwrap() = math::round_f32(c0[i]) as u8;
438 *output_iter.next().unwrap() = math::round_f32(c1[i]) as u8;
439 *output_iter.next().unwrap() = math::round_f32(c2[i]) as u8;
440 }
441 }
442 4 => {
444 let c3 = components.pop().unwrap();
445 let c2 = components.pop().unwrap();
446 let c1 = components.pop().unwrap();
447 let c0 = components.pop().unwrap();
448
449 let c0 = &c0.container[..max_len];
450 let c1 = &c1.container[..max_len];
451 let c2 = &c2.container[..max_len];
452 let c3 = &c3.container[..max_len];
453
454 for i in 0..max_len {
455 *output_iter.next().unwrap() = math::round_f32(c0[i]) as u8;
456 *output_iter.next().unwrap() = math::round_f32(c1[i]) as u8;
457 *output_iter.next().unwrap() = math::round_f32(c2[i]) as u8;
458 *output_iter.next().unwrap() = math::round_f32(c3[i]) as u8;
459 }
460 }
461 _ => unreachable!(),
462 }
463 } else {
464 let mul_factor = ((1 << 8) - 1) as f32;
466
467 for sample in 0..max_len {
468 for channel in components.iter() {
469 *output_iter.next().unwrap() = math::round_f32(
470 (channel.container[sample] / ((1_u32 << channel.bit_depth) - 1) as f32)
471 * mul_factor,
472 ) as u8;
473 }
474 }
475 }
476}
477
478fn convert_color_space(image: &mut DecodedImage, bit_depth: u8) -> Result<()> {
479 if let Some(jp2::colr::ColorSpace::Enumerated(e)) = &image
480 .boxes
481 .color_specification
482 .as_ref()
483 .map(|i| &i.color_space)
484 {
485 match e {
486 EnumeratedColorspace::Sycc => {
487 dispatch!(Level::new(), simd => {
488 sycc_to_rgb(simd, &mut image.decoded.components, bit_depth)
489 })?;
490 }
491 EnumeratedColorspace::CieLab(cielab) => {
492 dispatch!(Level::new(), simd => {
493 cielab_to_rgb(simd, &mut image.decoded.components, bit_depth, cielab)
494 })?;
495 }
496 EnumeratedColorspace::Ycck => {
497 dispatch!(Level::new(), simd => {
503 sycc_to_rgb(simd, &mut image.decoded.components, bit_depth)
504 })?;
505 let max_val = ((1_u32 << bit_depth) - 1) as f32;
507 for comp in image.decoded.components.iter_mut().take(3) {
508 for v in comp.container.iter_mut() {
509 *v = max_val - *v;
510 }
511 }
512 }
513 _ => {}
514 }
515 }
516
517 Ok(())
518}
519
520fn get_color_space(boxes: &ImageBoxes, num_components: usize) -> Result<ColorSpace> {
521 let cs = match boxes
522 .color_specification
523 .as_ref()
524 .map(|c| &c.color_space)
525 .unwrap_or(&jp2::colr::ColorSpace::Unknown)
526 {
527 jp2::colr::ColorSpace::Enumerated(e) => {
528 match e {
529 EnumeratedColorspace::Cmyk => ColorSpace::CMYK,
530 EnumeratedColorspace::Ycck => ColorSpace::CMYK,
534 EnumeratedColorspace::Srgb => ColorSpace::RGB,
535 EnumeratedColorspace::RommRgb => {
536 ColorSpace::Icc {
538 profile: include_bytes!("../assets/ProPhoto-v2-micro.icc").to_vec(),
539 num_channels: 3,
540 }
541 }
542 EnumeratedColorspace::EsRgb => ColorSpace::RGB,
543 EnumeratedColorspace::Greyscale => ColorSpace::Gray,
544 EnumeratedColorspace::Sycc => ColorSpace::RGB,
545 EnumeratedColorspace::CieLab(_) => ColorSpace::Icc {
546 profile: include_bytes!("../assets/LAB.icc").to_vec(),
547 num_channels: 3,
548 },
549 _ => bail!(FormatError::Unsupported),
550 }
551 }
552 jp2::colr::ColorSpace::Icc(icc) => {
553 if let Some(metadata) = ICCMetadata::from_data(icc) {
554 ColorSpace::Icc {
555 profile: icc.clone(),
556 num_channels: metadata.color_space.num_components(),
557 }
558 } else {
559 ColorSpace::RGB
564 }
565 }
566 jp2::colr::ColorSpace::Unknown => match num_components {
567 1 => ColorSpace::Gray,
568 3 => ColorSpace::RGB,
569 4 => ColorSpace::CMYK,
570 _ => ColorSpace::Unknown {
571 num_channels: num_components as u8,
572 },
573 },
574 };
575
576 Ok(cs)
577}
578
579fn resolve_palette_indices(
580 components: Vec<ComponentData>,
581 boxes: &ImageBoxes,
582) -> Result<Vec<ComponentData>> {
583 let Some(palette) = boxes.palette.as_ref() else {
584 return Ok(components);
586 };
587
588 let mapping = boxes.component_mapping.as_ref().unwrap();
589 let mut resolved = Vec::with_capacity(mapping.entries.len());
590
591 for entry in &mapping.entries {
592 let component_idx = entry.component_index as usize;
593 let component = components
594 .get(component_idx)
595 .ok_or(ColorError::PaletteResolutionFailed)?;
596
597 match entry.mapping_type {
598 ComponentMappingType::Direct => resolved.push(component.clone()),
599 ComponentMappingType::Palette { column } => {
600 let column_idx = column as usize;
601 let column_info = palette
602 .columns
603 .get(column_idx)
604 .ok_or(ColorError::PaletteResolutionFailed)?;
605
606 let mut mapped =
607 Vec::with_capacity(component.container.truncated().len() + SIMD_WIDTH);
608
609 for &sample in component.container.truncated() {
610 let index = math::round_f32(sample) as i64;
611 let value = palette
612 .map(index as usize, column_idx)
613 .ok_or(ColorError::PaletteResolutionFailed)?;
614 mapped.push(value as f32);
615 }
616
617 resolved.push(ComponentData {
618 container: math::SimdBuffer::new(mapped),
619 bit_depth: column_info.bit_depth,
620 });
621 }
622 }
623 }
624
625 Ok(resolved)
626}
627
628#[inline(always)]
629fn cielab_to_rgb<S: Simd>(
630 simd: S,
631 components: &mut [ComponentData],
632 bit_depth: u8,
633 lab: &CieLab,
634) -> Result<()> {
635 let (head, _) = components
636 .split_at_mut_checked(3)
637 .ok_or(ColorError::LabConversionFailed)?;
638
639 let [l, a, b] = head else {
640 unreachable!();
641 };
642
643 let prec0 = l.bit_depth;
644 let prec1 = a.bit_depth;
645 let prec2 = b.bit_depth;
646
647 if prec0 < 4 || prec1 < 4 || prec2 < 4 {
649 bail!(ColorError::LabConversionFailed);
650 }
651
652 let rl = lab.rl.unwrap_or(100);
653 let ra = lab.ra.unwrap_or(170);
654 let rb = lab.ra.unwrap_or(200);
655 let ol = lab.ol.unwrap_or(0);
656 let oa = lab.oa.unwrap_or(1 << (bit_depth - 1));
657 let ob = lab
658 .ob
659 .unwrap_or((1 << (bit_depth - 2)) + (1 << (bit_depth - 3)));
660
661 let min_l = -(rl as f32 * ol as f32) / ((1 << prec0) - 1) as f32;
663 let max_l = min_l + rl as f32;
664 let min_a = -(ra as f32 * oa as f32) / ((1 << prec1) - 1) as f32;
665 let max_a = min_a + ra as f32;
666 let min_b = -(rb as f32 * ob as f32) / ((1 << prec2) - 1) as f32;
667 let max_b = min_b + rb as f32;
668
669 let bit_max = (1_u32 << bit_depth) - 1;
670
671 let divisor_l = ((1 << prec0) - 1) as f32;
675 let divisor_a = ((1 << prec1) - 1) as f32;
676 let divisor_b = ((1 << prec2) - 1) as f32;
677
678 let scale_l_final = bit_max as f32 / 100.0;
679 let scale_ab_final = bit_max as f32 / 255.0;
680
681 let l_offset = min_l * scale_l_final;
682 let l_scale = (max_l - min_l) / divisor_l * scale_l_final;
683 let a_offset = (min_a + 128.0) * scale_ab_final;
684 let a_scale = (max_a - min_a) / divisor_a * scale_ab_final;
685 let b_offset = (min_b + 128.0) * scale_ab_final;
686 let b_scale = (max_b - min_b) / divisor_b * scale_ab_final;
687
688 let l_offset_v = f32x8::splat(simd, l_offset);
689 let l_scale_v = f32x8::splat(simd, l_scale);
690 let a_offset_v = f32x8::splat(simd, a_offset);
691 let a_scale_v = f32x8::splat(simd, a_scale);
692 let b_offset_v = f32x8::splat(simd, b_offset);
693 let b_scale_v = f32x8::splat(simd, b_scale);
694
695 for ((l_chunk, a_chunk), b_chunk) in l
699 .container
700 .chunks_exact_mut(SIMD_WIDTH)
701 .zip(a.container.chunks_exact_mut(SIMD_WIDTH))
702 .zip(b.container.chunks_exact_mut(SIMD_WIDTH))
703 {
704 let l_v = f32x8::from_slice(simd, l_chunk);
705 let a_v = f32x8::from_slice(simd, a_chunk);
706 let b_v = f32x8::from_slice(simd, b_chunk);
707
708 l_v.mul_add(l_scale_v, l_offset_v).store(l_chunk);
709 a_v.mul_add(a_scale_v, a_offset_v).store(a_chunk);
710 b_v.mul_add(b_scale_v, b_offset_v).store(b_chunk);
711 }
712
713 Ok(())
714}
715
716#[cfg(test)]
717mod tests {
718 use super::*;
719
720 #[rustfmt::skip]
732 const MINIMAL_J2C: &[u8] = &[
733 0xFF, 0x4F,
735
736 0xFF, 0x51,
738 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x01, 0xFF, 0x52,
753 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x5C,
766 0x00, 0x04, 0x00, 0x80, 0xFF, 0x90,
772 ];
773
774 #[test]
775 fn new_minimal_j2c_succeeds() {
776 assert!(Image::new(MINIMAL_J2C, &DecodeSettings::default()).is_ok());
777 }
778
779 #[test]
780 fn new_minimal_j2c_dimensions() {
781 let image = Image::new(MINIMAL_J2C, &DecodeSettings::default()).expect("J2C should parse");
782 assert_eq!(image.width(), 2);
783 assert_eq!(image.height(), 2);
784 }
785
786 #[test]
787 fn new_minimal_j2c_is_greyscale() {
788 let image = Image::new(MINIMAL_J2C, &DecodeSettings::default()).expect("J2C should parse");
789 assert_eq!(image.color_space().num_channels(), 1);
790 }
791
792 #[test]
793 fn new_minimal_j2c_no_alpha() {
794 let image = Image::new(MINIMAL_J2C, &DecodeSettings::default()).expect("J2C should parse");
795 assert!(!image.has_alpha());
796 }
797
798 #[test]
799 fn new_invalid_signature_returns_error() {
800 assert!(Image::new(b"\x00\x00\x00\x00", &DecodeSettings::default()).is_err());
801 }
802
803 #[test]
809 fn decode_buffer_size_overflow_is_guarded() {
810 let overflow = (usize::MAX / 2 + 1)
811 .checked_mul(2)
812 .and_then(|n| n.checked_mul(1));
813 assert!(
814 overflow.is_none(),
815 "overflow must be detected by checked_mul"
816 );
817 let err: DecodeError = DecodeError::Validation(ValidationError::ImageTooLarge);
819 assert!(matches!(
820 err,
821 DecodeError::Validation(ValidationError::ImageTooLarge)
822 ));
823 }
824}
825
826#[inline(always)]
827fn sycc_to_rgb<S: Simd>(simd: S, components: &mut [ComponentData], bit_depth: u8) -> Result<()> {
828 let offset = (1_u32 << (bit_depth as u32 - 1)) as f32;
829 let max_value = ((1_u32 << bit_depth as u32) - 1) as f32;
830
831 let (head, _) = components
832 .split_at_mut_checked(3)
833 .ok_or(ColorError::SyccConversionFailed)?;
834
835 let [y, cb, cr] = head else {
836 unreachable!();
837 };
838
839 let offset_v = f32x8::splat(simd, offset);
840 let max_v = f32x8::splat(simd, max_value);
841 let zero_v = f32x8::splat(simd, 0.0);
842 let cr_to_r = f32x8::splat(simd, 1.402);
843 let cb_to_g = f32x8::splat(simd, -0.344136);
844 let cr_to_g = f32x8::splat(simd, -0.714136);
845 let cb_to_b = f32x8::splat(simd, 1.772);
846
847 for ((y_chunk, cb_chunk), cr_chunk) in y
848 .container
849 .chunks_exact_mut(SIMD_WIDTH)
850 .zip(cb.container.chunks_exact_mut(SIMD_WIDTH))
851 .zip(cr.container.chunks_exact_mut(SIMD_WIDTH))
852 {
853 let y_v = f32x8::from_slice(simd, y_chunk);
854 let cb_v = f32x8::from_slice(simd, cb_chunk) - offset_v;
855 let cr_v = f32x8::from_slice(simd, cr_chunk) - offset_v;
856
857 let r = cr_v.mul_add(cr_to_r, y_v);
859 let g = cr_v.mul_add(cr_to_g, cb_v.mul_add(cb_to_g, y_v));
861 let b = cb_v.mul_add(cb_to_b, y_v);
863
864 r.min(max_v).max(zero_v).store(y_chunk);
865 g.min(max_v).max(zero_v).store(cb_chunk);
866 b.min(max_v).max(zero_v).store(cr_chunk);
867 }
868
869 Ok(())
870}