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 EnumeratedColorspace::Ycck => {
492 dispatch!(Level::new(), simd => {
498 sycc_to_rgb(simd, &mut image.decoded.components, bit_depth)
499 })?;
500 let max_val = ((1_u32 << bit_depth) - 1) as f32;
502 for comp in image.decoded.components.iter_mut().take(3) {
503 for v in comp.container.iter_mut() {
504 *v = max_val - *v;
505 }
506 }
507 }
508 _ => {}
509 }
510 }
511
512 Ok(())
513}
514
515fn get_color_space(boxes: &ImageBoxes, num_components: usize) -> Result<ColorSpace> {
516 let cs = match boxes
517 .color_specification
518 .as_ref()
519 .map(|c| &c.color_space)
520 .unwrap_or(&jp2::colr::ColorSpace::Unknown)
521 {
522 jp2::colr::ColorSpace::Enumerated(e) => {
523 match e {
524 EnumeratedColorspace::Cmyk => ColorSpace::CMYK,
525 EnumeratedColorspace::Ycck => ColorSpace::CMYK,
529 EnumeratedColorspace::Srgb => ColorSpace::RGB,
530 EnumeratedColorspace::RommRgb => {
531 ColorSpace::Icc {
533 profile: include_bytes!("../assets/ProPhoto-v2-micro.icc").to_vec(),
534 num_channels: 3,
535 }
536 }
537 EnumeratedColorspace::EsRgb => ColorSpace::RGB,
538 EnumeratedColorspace::Greyscale => ColorSpace::Gray,
539 EnumeratedColorspace::Sycc => ColorSpace::RGB,
540 EnumeratedColorspace::CieLab(_) => ColorSpace::Icc {
541 profile: include_bytes!("../assets/LAB.icc").to_vec(),
542 num_channels: 3,
543 },
544 _ => bail!(FormatError::Unsupported),
545 }
546 }
547 jp2::colr::ColorSpace::Icc(icc) => {
548 if let Some(metadata) = ICCMetadata::from_data(icc) {
549 ColorSpace::Icc {
550 profile: icc.clone(),
551 num_channels: metadata.color_space.num_components(),
552 }
553 } else {
554 ColorSpace::RGB
559 }
560 }
561 jp2::colr::ColorSpace::Unknown => match num_components {
562 1 => ColorSpace::Gray,
563 3 => ColorSpace::RGB,
564 4 => ColorSpace::CMYK,
565 _ => ColorSpace::Unknown {
566 num_channels: num_components as u8,
567 },
568 },
569 };
570
571 Ok(cs)
572}
573
574fn resolve_palette_indices(
575 components: Vec<ComponentData>,
576 boxes: &ImageBoxes,
577) -> Result<Vec<ComponentData>> {
578 let Some(palette) = boxes.palette.as_ref() else {
579 return Ok(components);
581 };
582
583 let mapping = boxes.component_mapping.as_ref().unwrap();
584 let mut resolved = Vec::with_capacity(mapping.entries.len());
585
586 for entry in &mapping.entries {
587 let component_idx = entry.component_index as usize;
588 let component = components
589 .get(component_idx)
590 .ok_or(ColorError::PaletteResolutionFailed)?;
591
592 match entry.mapping_type {
593 ComponentMappingType::Direct => resolved.push(component.clone()),
594 ComponentMappingType::Palette { column } => {
595 let column_idx = column as usize;
596 let column_info = palette
597 .columns
598 .get(column_idx)
599 .ok_or(ColorError::PaletteResolutionFailed)?;
600
601 let mut mapped =
602 Vec::with_capacity(component.container.truncated().len() + SIMD_WIDTH);
603
604 for &sample in component.container.truncated() {
605 let index = math::round_f32(sample) as i64;
606 let value = palette
607 .map(index as usize, column_idx)
608 .ok_or(ColorError::PaletteResolutionFailed)?;
609 mapped.push(value as f32);
610 }
611
612 resolved.push(ComponentData {
613 container: math::SimdBuffer::new(mapped),
614 bit_depth: column_info.bit_depth,
615 });
616 }
617 }
618 }
619
620 Ok(resolved)
621}
622
623#[inline(always)]
624fn cielab_to_rgb<S: Simd>(
625 simd: S,
626 components: &mut [ComponentData],
627 bit_depth: u8,
628 lab: &CieLab,
629) -> Result<()> {
630 let (head, _) = components
631 .split_at_mut_checked(3)
632 .ok_or(ColorError::LabConversionFailed)?;
633
634 let [l, a, b] = head else {
635 unreachable!();
636 };
637
638 let prec0 = l.bit_depth;
639 let prec1 = a.bit_depth;
640 let prec2 = b.bit_depth;
641
642 if prec0 < 4 || prec1 < 4 || prec2 < 4 {
644 bail!(ColorError::LabConversionFailed);
645 }
646
647 let rl = lab.rl.unwrap_or(100);
648 let ra = lab.ra.unwrap_or(170);
649 let rb = lab.ra.unwrap_or(200);
650 let ol = lab.ol.unwrap_or(0);
651 let oa = lab.oa.unwrap_or(1 << (bit_depth - 1));
652 let ob = lab
653 .ob
654 .unwrap_or((1 << (bit_depth - 2)) + (1 << (bit_depth - 3)));
655
656 let min_l = -(rl as f32 * ol as f32) / ((1 << prec0) - 1) as f32;
658 let max_l = min_l + rl as f32;
659 let min_a = -(ra as f32 * oa as f32) / ((1 << prec1) - 1) as f32;
660 let max_a = min_a + ra as f32;
661 let min_b = -(rb as f32 * ob as f32) / ((1 << prec2) - 1) as f32;
662 let max_b = min_b + rb as f32;
663
664 let bit_max = (1_u32 << bit_depth) - 1;
665
666 let divisor_l = ((1 << prec0) - 1) as f32;
670 let divisor_a = ((1 << prec1) - 1) as f32;
671 let divisor_b = ((1 << prec2) - 1) as f32;
672
673 let scale_l_final = bit_max as f32 / 100.0;
674 let scale_ab_final = bit_max as f32 / 255.0;
675
676 let l_offset = min_l * scale_l_final;
677 let l_scale = (max_l - min_l) / divisor_l * scale_l_final;
678 let a_offset = (min_a + 128.0) * scale_ab_final;
679 let a_scale = (max_a - min_a) / divisor_a * scale_ab_final;
680 let b_offset = (min_b + 128.0) * scale_ab_final;
681 let b_scale = (max_b - min_b) / divisor_b * scale_ab_final;
682
683 let l_offset_v = f32x8::splat(simd, l_offset);
684 let l_scale_v = f32x8::splat(simd, l_scale);
685 let a_offset_v = f32x8::splat(simd, a_offset);
686 let a_scale_v = f32x8::splat(simd, a_scale);
687 let b_offset_v = f32x8::splat(simd, b_offset);
688 let b_scale_v = f32x8::splat(simd, b_scale);
689
690 for ((l_chunk, a_chunk), b_chunk) in l
694 .container
695 .chunks_exact_mut(SIMD_WIDTH)
696 .zip(a.container.chunks_exact_mut(SIMD_WIDTH))
697 .zip(b.container.chunks_exact_mut(SIMD_WIDTH))
698 {
699 let l_v = f32x8::from_slice(simd, l_chunk);
700 let a_v = f32x8::from_slice(simd, a_chunk);
701 let b_v = f32x8::from_slice(simd, b_chunk);
702
703 l_v.mul_add(l_scale_v, l_offset_v).store(l_chunk);
704 a_v.mul_add(a_scale_v, a_offset_v).store(a_chunk);
705 b_v.mul_add(b_scale_v, b_offset_v).store(b_chunk);
706 }
707
708 Ok(())
709}
710
711#[cfg(test)]
712mod tests {
713 use super::*;
714
715 #[rustfmt::skip]
727 const MINIMAL_J2C: &[u8] = &[
728 0xFF, 0x4F,
730
731 0xFF, 0x51,
733 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,
748 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x5C,
761 0x00, 0x04, 0x00, 0x80, 0xFF, 0x90,
767 ];
768
769 #[test]
770 fn new_minimal_j2c_succeeds() {
771 assert!(Image::new(MINIMAL_J2C, &DecodeSettings::default()).is_ok());
772 }
773
774 #[test]
775 fn new_minimal_j2c_dimensions() {
776 let image = Image::new(MINIMAL_J2C, &DecodeSettings::default()).expect("J2C should parse");
777 assert_eq!(image.width(), 2);
778 assert_eq!(image.height(), 2);
779 }
780
781 #[test]
782 fn new_minimal_j2c_is_greyscale() {
783 let image = Image::new(MINIMAL_J2C, &DecodeSettings::default()).expect("J2C should parse");
784 assert_eq!(image.color_space().num_channels(), 1);
785 }
786
787 #[test]
788 fn new_minimal_j2c_no_alpha() {
789 let image = Image::new(MINIMAL_J2C, &DecodeSettings::default()).expect("J2C should parse");
790 assert!(!image.has_alpha());
791 }
792
793 #[test]
794 fn new_invalid_signature_returns_error() {
795 assert!(Image::new(b"\x00\x00\x00\x00", &DecodeSettings::default()).is_err());
796 }
797}
798
799#[inline(always)]
800fn sycc_to_rgb<S: Simd>(simd: S, components: &mut [ComponentData], bit_depth: u8) -> Result<()> {
801 let offset = (1_u32 << (bit_depth as u32 - 1)) as f32;
802 let max_value = ((1_u32 << bit_depth as u32) - 1) as f32;
803
804 let (head, _) = components
805 .split_at_mut_checked(3)
806 .ok_or(ColorError::SyccConversionFailed)?;
807
808 let [y, cb, cr] = head else {
809 unreachable!();
810 };
811
812 let offset_v = f32x8::splat(simd, offset);
813 let max_v = f32x8::splat(simd, max_value);
814 let zero_v = f32x8::splat(simd, 0.0);
815 let cr_to_r = f32x8::splat(simd, 1.402);
816 let cb_to_g = f32x8::splat(simd, -0.344136);
817 let cr_to_g = f32x8::splat(simd, -0.714136);
818 let cb_to_b = f32x8::splat(simd, 1.772);
819
820 for ((y_chunk, cb_chunk), cr_chunk) in y
821 .container
822 .chunks_exact_mut(SIMD_WIDTH)
823 .zip(cb.container.chunks_exact_mut(SIMD_WIDTH))
824 .zip(cr.container.chunks_exact_mut(SIMD_WIDTH))
825 {
826 let y_v = f32x8::from_slice(simd, y_chunk);
827 let cb_v = f32x8::from_slice(simd, cb_chunk) - offset_v;
828 let cr_v = f32x8::from_slice(simd, cr_chunk) - offset_v;
829
830 let r = cr_v.mul_add(cr_to_r, y_v);
832 let g = cr_v.mul_add(cr_to_g, cb_v.mul_add(cb_to_g, y_v));
834 let b = cb_v.mul_add(cb_to_b, y_v);
836
837 r.min(max_v).max(zero_v).store(y_chunk);
838 g.min(max_v).max(zero_v).store(cb_chunk);
839 b.min(max_v).max(zero_v).store(cr_chunk);
840 }
841
842 Ok(())
843}