1#![cfg_attr(not(feature = "std"), no_std)]
70#![forbid(unsafe_code)]
71#![forbid(missing_docs)]
72#![allow(clippy::too_many_arguments)]
73
74extern crate alloc;
75
76use alloc::vec;
77use alloc::vec::Vec;
78
79use crate::error::{bail, err};
80use crate::j2c::{ComponentData, Header};
81use crate::jp2::cdef::{ChannelAssociation, ChannelType};
82use crate::jp2::cmap::ComponentMappingType;
83use crate::jp2::colr::{CieLab, EnumeratedColorspace};
84use crate::jp2::icc::ICCMetadata;
85use crate::jp2::{DecodedImage, ImageBoxes};
86
87pub mod error;
88#[macro_use]
89pub(crate) mod log;
90pub(crate) mod math;
91#[cfg(feature = "openjph-htj2k")]
92mod openjph_htj2k;
93pub(crate) mod writer;
94
95use crate::math::{dispatch, f32x8, Level, Simd, SIMD_WIDTH};
96pub use error::{
97 ColorError, DecodeError, DecodingError, FormatError, MarkerError, Result, TileError,
98 ValidationError,
99};
100pub use j2c::encode::{encode, encode_htj2k, EncodeOptions};
101pub use j2c::DecoderContext;
102
103mod j2c;
104mod jp2;
105pub(crate) mod reader;
106
107pub(crate) const JP2_MAGIC: &[u8] = b"\x00\x00\x00\x0C\x6A\x50\x20\x20";
109pub(crate) const CODESTREAM_MAGIC: &[u8] = b"\xFF\x4F\xFF\x51";
111
112#[derive(Debug, Copy, Clone)]
114pub struct DecodeSettings {
115 pub resolve_palette_indices: bool,
127 pub strict: bool,
132 pub target_resolution: Option<(u32, u32)>,
134}
135
136impl Default for DecodeSettings {
137 fn default() -> Self {
138 Self {
139 resolve_palette_indices: true,
140 strict: false,
141 target_resolution: None,
142 }
143 }
144}
145
146pub struct Image<'a> {
148 pub(crate) codestream: &'a [u8],
150 pub(crate) encoded_codestream: &'a [u8],
152 pub(crate) header: Header<'a>,
154 pub(crate) boxes: ImageBoxes,
157 pub(crate) settings: DecodeSettings,
159 pub(crate) uses_openjph_htj2k: bool,
161 pub(crate) has_alpha: bool,
163 pub(crate) color_space: ColorSpace,
165}
166
167impl<'a> Image<'a> {
168 pub fn new(data: &'a [u8], settings: &DecodeSettings) -> Result<Self> {
170 if data.starts_with(JP2_MAGIC) {
171 jp2::parse(data, *settings)
172 } else if data.starts_with(CODESTREAM_MAGIC) {
173 j2c::parse(data, settings)
174 } else {
175 err!(FormatError::InvalidSignature)
176 }
177 }
178
179 pub fn has_alpha(&self) -> bool {
181 self.has_alpha
182 }
183
184 pub fn color_space(&self) -> &ColorSpace {
186 &self.color_space
187 }
188
189 pub fn width(&self) -> u32 {
191 self.header.size_data.image_width()
192 }
193
194 pub fn height(&self) -> u32 {
196 self.header.size_data.image_height()
197 }
198
199 pub fn original_bit_depth(&self) -> u8 {
202 self.header.component_infos[0].size_info.precision
204 }
205
206 pub fn decode(&self) -> Result<Vec<u8>> {
209 let buffer_size = self.width() as usize
210 * self.height() as usize
211 * (self.color_space.num_channels() as usize + if self.has_alpha { 1 } else { 0 });
212 let mut buf = vec![0; buffer_size];
213 let mut decoder_context = DecoderContext::default();
214 self.decode_into(&mut buf, &mut decoder_context)?;
215
216 Ok(buf)
217 }
218
219 pub fn decode_native(&self) -> Result<RawBitmap> {
228 #[cfg(feature = "openjph-htj2k")]
229 if self.uses_openjph_htj2k {
230 return openjph_htj2k::decode_native(
231 self.encoded_codestream,
232 self.width(),
233 self.height(),
234 self.original_bit_depth(),
235 self.header.component_infos.len() as u8,
236 self.header.skipped_resolution_levels,
237 )
238 .map_err(|_| {
239 DecodeError::Decoding(DecodingError::UnsupportedFeature("OpenJPH HTJ2K decode"))
240 });
241 }
242
243 let mut decoder_context = DecoderContext::default();
244 j2c::decode(self.codestream, &self.header, &mut decoder_context)?;
245
246 let components = &decoder_context.tile_decode_context.channel_data;
247 let bit_depth = self.original_bit_depth();
248 let num_components = components.len() as u8;
249 let width = self.width();
250 let height = self.height();
251 let pixel_count = width as usize * height as usize;
252
253 if bit_depth <= 8 {
254 let max_val = ((1u32 << bit_depth) - 1) as f32;
255 let mut data = Vec::with_capacity(pixel_count * num_components as usize);
256 for i in 0..pixel_count {
257 for component in components.iter() {
258 let v = math::round_f32(component.container.truncated()[i]);
259 let clamped = if v < 0.0 {
260 0.0
261 } else if v > max_val {
262 max_val
263 } else {
264 v
265 };
266 data.push(clamped as u8);
267 }
268 }
269 Ok(RawBitmap {
270 data,
271 width,
272 height,
273 bit_depth,
274 num_components,
275 bytes_per_sample: 1,
276 })
277 } else {
278 let max_val = ((1u32 << bit_depth) - 1) as f32;
279 let mut data = Vec::with_capacity(pixel_count * num_components as usize * 2);
280 for i in 0..pixel_count {
281 for component in components.iter() {
282 let v = math::round_f32(component.container.truncated()[i]);
283 let clamped = if v < 0.0 {
284 0.0
285 } else if v > max_val {
286 max_val
287 } else {
288 v
289 };
290 let val = clamped as u16;
291 data.extend_from_slice(&val.to_le_bytes());
292 }
293 }
294 Ok(RawBitmap {
295 data,
296 width,
297 height,
298 bit_depth,
299 num_components,
300 bytes_per_sample: 2,
301 })
302 }
303 }
304
305 pub fn decode_into(
315 &'a self,
316 buf: &mut [u8],
317 decoder_context: &mut DecoderContext<'a>,
318 ) -> Result<()> {
319 #[cfg(feature = "openjph-htj2k")]
320 if self.uses_openjph_htj2k {
321 if self.has_alpha
322 || self.boxes.palette.is_some()
323 || self.boxes.component_mapping.is_some()
324 || self.boxes.channel_definition.is_some()
325 {
326 return Err(DecodeError::Decoding(DecodingError::UnsupportedFeature(
327 "OpenJPH HTJ2K JP2 palette/alpha handling",
328 )));
329 }
330
331 let raw = self.decode_native()?;
332 copy_raw_bitmap_to_u8(&raw, buf);
333 return Ok(());
334 }
335
336 let settings = &self.settings;
337 j2c::decode(self.codestream, &self.header, decoder_context)?;
338 let mut decoded_image = DecodedImage {
339 decoded_components: &mut decoder_context.tile_decode_context.channel_data,
340 boxes: self.boxes.clone(),
341 };
342
343 if settings.resolve_palette_indices {
345 let components = core::mem::take(decoded_image.decoded_components);
346 *decoded_image.decoded_components =
347 resolve_palette_indices(components, &decoded_image.boxes)?;
348 }
349
350 if let Some(cdef) = &decoded_image.boxes.channel_definition {
351 let mut components = decoded_image
354 .decoded_components
355 .iter()
356 .cloned()
357 .zip(
358 cdef.channel_definitions
359 .iter()
360 .map(|c| match c._association {
361 ChannelAssociation::WholeImage => u16::MAX,
362 ChannelAssociation::Colour(c) => c,
363 }),
364 )
365 .collect::<Vec<_>>();
366 components.sort_by(|c1, c2| c1.1.cmp(&c2.1));
367 *decoded_image.decoded_components = components.into_iter().map(|c| c.0).collect();
368 }
369
370 let bit_depth = decoded_image.decoded_components[0].bit_depth;
372 convert_color_space(&mut decoded_image, bit_depth)?;
373 interleave_and_convert(&mut decoded_image, buf);
374
375 Ok(())
376 }
377}
378
379fn copy_raw_bitmap_to_u8(raw: &RawBitmap, buf: &mut [u8]) {
380 let expected_len = raw.width as usize * raw.height as usize * raw.num_components as usize;
381 debug_assert_eq!(buf.len(), expected_len);
382
383 if raw.bytes_per_sample == 1 {
384 buf.copy_from_slice(&raw.data[..expected_len]);
385 return;
386 }
387
388 let max_value = ((1u32 << raw.bit_depth) - 1).max(1);
389 for (index, out) in buf.iter_mut().enumerate() {
390 let byte_offset = index * 2;
391 let sample = u16::from_le_bytes([raw.data[byte_offset], raw.data[byte_offset + 1]]) as u32;
392 *out = ((sample * 255 + (max_value / 2)) / max_value) as u8;
393 }
394}
395
396pub(crate) fn resolve_alpha_and_color_space(
397 boxes: &ImageBoxes,
398 header: &Header<'_>,
399 settings: &DecodeSettings,
400) -> Result<(ColorSpace, bool)> {
401 let mut num_components = header.component_infos.len();
402
403 if settings.resolve_palette_indices {
406 if let Some(palette_box) = &boxes.palette {
407 num_components = palette_box.columns.len();
408 }
409 }
410
411 let mut has_alpha = false;
412
413 if let Some(cdef) = &boxes.channel_definition {
414 let last = cdef.channel_definitions.last().unwrap();
415 has_alpha = last.channel_type == ChannelType::Opacity;
416 }
417
418 let mut color_space = get_color_space(boxes, num_components)?;
419
420 if !settings.resolve_palette_indices && boxes.palette.is_some() {
422 has_alpha = false;
423 color_space = ColorSpace::Gray;
424 }
425
426 let actual_num_components = header.component_infos.len();
427
428 if boxes.palette.is_none()
430 && actual_num_components
431 != (color_space.num_channels() + if has_alpha { 1 } else { 0 }) as usize
432 {
433 if !settings.strict
434 && actual_num_components == color_space.num_channels() as usize + 1
435 && !has_alpha
436 {
437 has_alpha = true;
440 } else {
441 if actual_num_components == 1 || (actual_num_components == 2 && has_alpha) {
443 color_space = ColorSpace::Gray;
444 } else if actual_num_components == 3 {
445 color_space = ColorSpace::RGB;
446 } else if actual_num_components == 4 {
447 if has_alpha {
448 color_space = ColorSpace::RGB;
449 } else {
450 color_space = ColorSpace::CMYK;
451 }
452 } else {
453 bail!(ValidationError::TooManyChannels);
454 }
455 }
456 }
457
458 Ok((color_space, has_alpha))
459}
460
461#[derive(Debug, Clone)]
463pub enum ColorSpace {
464 Gray,
466 RGB,
468 CMYK,
470 Unknown {
472 num_channels: u8,
474 },
475 Icc {
477 profile: Vec<u8>,
479 num_channels: u8,
481 },
482}
483
484impl ColorSpace {
485 pub fn num_channels(&self) -> u8 {
487 match self {
488 Self::Gray => 1,
489 Self::RGB => 3,
490 Self::CMYK => 4,
491 Self::Unknown { num_channels } => *num_channels,
492 Self::Icc {
493 num_channels: num_components,
494 ..
495 } => *num_components,
496 }
497 }
498}
499
500pub struct Bitmap {
502 pub color_space: ColorSpace,
504 pub data: Vec<u8>,
513 pub has_alpha: bool,
515 pub width: u32,
517 pub height: u32,
519 pub original_bit_depth: u8,
522}
523
524pub struct RawBitmap {
533 pub data: Vec<u8>,
535 pub width: u32,
537 pub height: u32,
539 pub bit_depth: u8,
541 pub num_components: u8,
543 pub bytes_per_sample: u8,
545}
546
547fn interleave_and_convert(image: &mut DecodedImage<'_>, buf: &mut [u8]) {
548 let components = &mut *image.decoded_components;
549 let num_components = components.len();
550
551 let mut all_same_bit_depth = Some(components[0].bit_depth);
552
553 for component in components.iter().skip(1) {
554 if Some(component.bit_depth) != all_same_bit_depth {
555 all_same_bit_depth = None;
556 }
557 }
558
559 let max_len = components[0].container.truncated().len();
560
561 let mut output_iter = buf.iter_mut();
562
563 if all_same_bit_depth == Some(8) && num_components <= 4 {
564 match num_components {
566 1 => {
568 for (output, input) in output_iter.zip(
569 components[0]
570 .container
571 .iter()
572 .map(|v| math::round_f32(*v) as u8),
573 ) {
574 *output = input;
575 }
576 }
577 2 => {
579 let c0 = &components[0];
580 let c1 = &components[1];
581
582 let c0 = &c0.container[..max_len];
583 let c1 = &c1.container[..max_len];
584
585 for i in 0..max_len {
586 *output_iter.next().unwrap() = math::round_f32(c0[i]) as u8;
587 *output_iter.next().unwrap() = math::round_f32(c1[i]) as u8;
588 }
589 }
590 3 => {
592 let c0 = &components[0];
593 let c1 = &components[1];
594 let c2 = &components[2];
595
596 let c0 = &c0.container[..max_len];
597 let c1 = &c1.container[..max_len];
598 let c2 = &c2.container[..max_len];
599
600 for i in 0..max_len {
601 *output_iter.next().unwrap() = math::round_f32(c0[i]) as u8;
602 *output_iter.next().unwrap() = math::round_f32(c1[i]) as u8;
603 *output_iter.next().unwrap() = math::round_f32(c2[i]) as u8;
604 }
605 }
606 4 => {
608 let c0 = &components[0];
609 let c1 = &components[1];
610 let c2 = &components[2];
611 let c3 = &components[3];
612
613 let c0 = &c0.container[..max_len];
614 let c1 = &c1.container[..max_len];
615 let c2 = &c2.container[..max_len];
616 let c3 = &c3.container[..max_len];
617
618 for i in 0..max_len {
619 *output_iter.next().unwrap() = math::round_f32(c0[i]) as u8;
620 *output_iter.next().unwrap() = math::round_f32(c1[i]) as u8;
621 *output_iter.next().unwrap() = math::round_f32(c2[i]) as u8;
622 *output_iter.next().unwrap() = math::round_f32(c3[i]) as u8;
623 }
624 }
625 _ => unreachable!(),
626 }
627 } else {
628 let mul_factor = ((1 << 8) - 1) as f32;
630
631 for sample in 0..max_len {
632 for channel in components.iter() {
633 *output_iter.next().unwrap() = math::round_f32(
634 (channel.container[sample] / ((1_u32 << channel.bit_depth) - 1) as f32)
635 * mul_factor,
636 ) as u8;
637 }
638 }
639 }
640}
641
642fn convert_color_space(image: &mut DecodedImage<'_>, bit_depth: u8) -> Result<()> {
643 if let Some(jp2::colr::ColorSpace::Enumerated(e)) = &image
644 .boxes
645 .color_specification
646 .as_ref()
647 .map(|i| &i.color_space)
648 {
649 match e {
650 EnumeratedColorspace::Sycc => {
651 dispatch!(Level::new(), simd => {
652 sycc_to_rgb(simd, image.decoded_components, bit_depth)
653 })?;
654 }
655 EnumeratedColorspace::CieLab(cielab) => {
656 dispatch!(Level::new(), simd => {
657 cielab_to_rgb(simd, image.decoded_components, bit_depth, cielab)
658 })?;
659 }
660 _ => {}
661 }
662 }
663
664 Ok(())
665}
666
667fn get_color_space(boxes: &ImageBoxes, num_components: usize) -> Result<ColorSpace> {
668 let cs = match boxes
669 .color_specification
670 .as_ref()
671 .map(|c| &c.color_space)
672 .unwrap_or(&jp2::colr::ColorSpace::Unknown)
673 {
674 jp2::colr::ColorSpace::Enumerated(e) => {
675 match e {
676 EnumeratedColorspace::Cmyk => ColorSpace::CMYK,
677 EnumeratedColorspace::Srgb => ColorSpace::RGB,
678 EnumeratedColorspace::RommRgb => {
679 ColorSpace::Icc {
681 profile: include_bytes!("../assets/ProPhoto-v2-micro.icc").to_vec(),
682 num_channels: 3,
683 }
684 }
685 EnumeratedColorspace::EsRgb => ColorSpace::RGB,
686 EnumeratedColorspace::Greyscale => ColorSpace::Gray,
687 EnumeratedColorspace::Sycc => ColorSpace::RGB,
688 EnumeratedColorspace::CieLab(_) => ColorSpace::Icc {
689 profile: include_bytes!("../assets/LAB.icc").to_vec(),
690 num_channels: 3,
691 },
692 _ => bail!(FormatError::Unsupported),
693 }
694 }
695 jp2::colr::ColorSpace::Icc(icc) => {
696 if let Some(metadata) = ICCMetadata::from_data(icc) {
697 ColorSpace::Icc {
698 profile: icc.clone(),
699 num_channels: metadata.color_space.num_components(),
700 }
701 } else {
702 ColorSpace::RGB
707 }
708 }
709 jp2::colr::ColorSpace::Unknown => match num_components {
710 1 => ColorSpace::Gray,
711 3 => ColorSpace::RGB,
712 4 => ColorSpace::CMYK,
713 _ => ColorSpace::Unknown {
714 num_channels: num_components as u8,
715 },
716 },
717 };
718
719 Ok(cs)
720}
721
722fn resolve_palette_indices(
723 components: Vec<ComponentData>,
724 boxes: &ImageBoxes,
725) -> Result<Vec<ComponentData>> {
726 let Some(palette) = boxes.palette.as_ref() else {
727 return Ok(components);
729 };
730
731 let mapping = boxes.component_mapping.as_ref().unwrap();
732 let mut resolved = Vec::with_capacity(mapping.entries.len());
733
734 for entry in &mapping.entries {
735 let component_idx = entry.component_index as usize;
736 let component = components
737 .get(component_idx)
738 .ok_or(ColorError::PaletteResolutionFailed)?;
739
740 match entry.mapping_type {
741 ComponentMappingType::Direct => resolved.push(component.clone()),
742 ComponentMappingType::Palette { column } => {
743 let column_idx = column as usize;
744 let column_info = palette
745 .columns
746 .get(column_idx)
747 .ok_or(ColorError::PaletteResolutionFailed)?;
748
749 let mut mapped =
750 Vec::with_capacity(component.container.truncated().len() + SIMD_WIDTH);
751
752 for &sample in component.container.truncated() {
753 let index = math::round_f32(sample) as i64;
754 let value = palette
755 .map(index as usize, column_idx)
756 .ok_or(ColorError::PaletteResolutionFailed)?;
757 mapped.push(value as f32);
758 }
759
760 resolved.push(ComponentData {
761 container: math::SimdBuffer::new(mapped),
762 bit_depth: column_info.bit_depth,
763 });
764 }
765 }
766 }
767
768 Ok(resolved)
769}
770
771#[inline(always)]
772fn cielab_to_rgb<S: Simd>(
773 simd: S,
774 components: &mut [ComponentData],
775 bit_depth: u8,
776 lab: &CieLab,
777) -> Result<()> {
778 let (head, _) = components
779 .split_at_mut_checked(3)
780 .ok_or(ColorError::LabConversionFailed)?;
781
782 let [l, a, b] = head else {
783 unreachable!();
784 };
785
786 let prec0 = l.bit_depth;
787 let prec1 = a.bit_depth;
788 let prec2 = b.bit_depth;
789
790 if prec0 < 4 || prec1 < 4 || prec2 < 4 {
792 bail!(ColorError::LabConversionFailed);
793 }
794
795 let rl = lab.rl.unwrap_or(100);
796 let ra = lab.ra.unwrap_or(170);
797 let rb = lab.ra.unwrap_or(200);
798 let ol = lab.ol.unwrap_or(0);
799 let oa = lab.oa.unwrap_or(1 << (bit_depth - 1));
800 let ob = lab
801 .ob
802 .unwrap_or((1 << (bit_depth - 2)) + (1 << (bit_depth - 3)));
803
804 let min_l = -(rl as f32 * ol as f32) / ((1 << prec0) - 1) as f32;
806 let max_l = min_l + rl as f32;
807 let min_a = -(ra as f32 * oa as f32) / ((1 << prec1) - 1) as f32;
808 let max_a = min_a + ra as f32;
809 let min_b = -(rb as f32 * ob as f32) / ((1 << prec2) - 1) as f32;
810 let max_b = min_b + rb as f32;
811
812 let bit_max = (1_u32 << bit_depth) - 1;
813
814 let divisor_l = ((1 << prec0) - 1) as f32;
818 let divisor_a = ((1 << prec1) - 1) as f32;
819 let divisor_b = ((1 << prec2) - 1) as f32;
820
821 let scale_l_final = bit_max as f32 / 100.0;
822 let scale_ab_final = bit_max as f32 / 255.0;
823
824 let l_offset = min_l * scale_l_final;
825 let l_scale = (max_l - min_l) / divisor_l * scale_l_final;
826 let a_offset = (min_a + 128.0) * scale_ab_final;
827 let a_scale = (max_a - min_a) / divisor_a * scale_ab_final;
828 let b_offset = (min_b + 128.0) * scale_ab_final;
829 let b_scale = (max_b - min_b) / divisor_b * scale_ab_final;
830
831 let l_offset_v = f32x8::splat(simd, l_offset);
832 let l_scale_v = f32x8::splat(simd, l_scale);
833 let a_offset_v = f32x8::splat(simd, a_offset);
834 let a_scale_v = f32x8::splat(simd, a_scale);
835 let b_offset_v = f32x8::splat(simd, b_offset);
836 let b_scale_v = f32x8::splat(simd, b_scale);
837
838 for ((l_chunk, a_chunk), b_chunk) in l
842 .container
843 .chunks_exact_mut(SIMD_WIDTH)
844 .zip(a.container.chunks_exact_mut(SIMD_WIDTH))
845 .zip(b.container.chunks_exact_mut(SIMD_WIDTH))
846 {
847 let l_v = f32x8::from_slice(simd, l_chunk);
848 let a_v = f32x8::from_slice(simd, a_chunk);
849 let b_v = f32x8::from_slice(simd, b_chunk);
850
851 l_v.mul_add(l_scale_v, l_offset_v).store(l_chunk);
852 a_v.mul_add(a_scale_v, a_offset_v).store(a_chunk);
853 b_v.mul_add(b_scale_v, b_offset_v).store(b_chunk);
854 }
855
856 Ok(())
857}
858
859#[inline(always)]
860fn sycc_to_rgb<S: Simd>(simd: S, components: &mut [ComponentData], bit_depth: u8) -> Result<()> {
861 let offset = (1_u32 << (bit_depth as u32 - 1)) as f32;
862 let max_value = ((1_u32 << bit_depth as u32) - 1) as f32;
863
864 let (head, _) = components
865 .split_at_mut_checked(3)
866 .ok_or(ColorError::SyccConversionFailed)?;
867
868 let [y, cb, cr] = head else {
869 unreachable!();
870 };
871
872 let offset_v = f32x8::splat(simd, offset);
873 let max_v = f32x8::splat(simd, max_value);
874 let zero_v = f32x8::splat(simd, 0.0);
875 let cr_to_r = f32x8::splat(simd, 1.402);
876 let cb_to_g = f32x8::splat(simd, -0.344136);
877 let cr_to_g = f32x8::splat(simd, -0.714136);
878 let cb_to_b = f32x8::splat(simd, 1.772);
879
880 for ((y_chunk, cb_chunk), cr_chunk) in y
881 .container
882 .chunks_exact_mut(SIMD_WIDTH)
883 .zip(cb.container.chunks_exact_mut(SIMD_WIDTH))
884 .zip(cr.container.chunks_exact_mut(SIMD_WIDTH))
885 {
886 let y_v = f32x8::from_slice(simd, y_chunk);
887 let cb_v = f32x8::from_slice(simd, cb_chunk) - offset_v;
888 let cr_v = f32x8::from_slice(simd, cr_chunk) - offset_v;
889
890 let r = cr_v.mul_add(cr_to_r, y_v);
892 let g = cr_v.mul_add(cr_to_g, cb_v.mul_add(cb_to_g, y_v));
894 let b = cb_v.mul_add(cb_to_b, y_v);
896
897 r.min(max_v).max(zero_v).store(y_chunk);
898 g.min(max_v).max(zero_v).store(cb_chunk);
899 b.min(max_v).max(zero_v).store(cr_chunk);
900 }
901
902 Ok(())
903}