Skip to main content

oxigdal_jpeg2000/
reader.rs

1//! High-level JP2/J2K reader
2//!
3//! This module provides a high-level interface for reading JPEG2000 files.
4
5use crate::box_reader::{BoxReader, BoxType};
6use crate::codestream::{CodestreamParser, CodingStyle, ImageSize, Marker, Quantization};
7use crate::error::{Jpeg2000Error, ResilienceMode, Result};
8use crate::metadata::{EnumeratedColorSpace, Jp2Metadata};
9use std::io::{Read, Seek, SeekFrom};
10
11/// JPEG2000 reader
12pub struct Jpeg2000Reader<R> {
13    /// Input reader
14    reader: R,
15    /// JP2 metadata
16    metadata: Option<Jp2Metadata>,
17    /// Codestream image size
18    image_size: Option<ImageSize>,
19    /// Coding style
20    coding_style: Option<CodingStyle>,
21    /// Quantization
22    quantization: Option<Quantization>,
23    /// Is JP2 format (vs raw codestream)
24    is_jp2: bool,
25    /// Error resilience mode
26    resilience_mode: ResilienceMode,
27    /// Progressive decoding state
28    progressive_state: Option<ProgressiveDecodingState>,
29}
30
31/// Progressive decoding state
32#[derive(Debug, Clone)]
33struct ProgressiveDecodingState {
34    /// Current quality layer being decoded
35    current_layer: u16,
36    /// Maximum quality layer available
37    #[allow(dead_code)]
38    max_layers: u16,
39    /// Intermediate decoded data (partial quality)
40    intermediate_data: Vec<u8>,
41    /// Width of intermediate image
42    #[allow(dead_code)]
43    width: usize,
44    /// Height of intermediate image
45    #[allow(dead_code)]
46    height: usize,
47}
48
49impl<R: Read + Seek> Jpeg2000Reader<R> {
50    /// Create new JPEG2000 reader
51    pub fn new(mut reader: R) -> Result<Self> {
52        // Try to detect format - but handle truncated files gracefully
53        let mut magic = [0u8; 12];
54        let is_jp2 = match reader.read_exact(&mut magic) {
55            Ok(()) => {
56                reader.seek(SeekFrom::Start(0))?;
57                magic[4..8] == *b"jP  "
58            }
59            Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
60                // File is too short to be JP2
61                // But check if it's completely empty by trying to read at least 2 bytes
62                reader.seek(SeekFrom::Start(0))?;
63                let mut min_magic = [0u8; 2];
64                match reader.read_exact(&mut min_magic) {
65                    Ok(()) => {
66                        // Has at least 2 bytes, could be J2K
67                        reader.seek(SeekFrom::Start(0))?;
68                        false
69                    }
70                    Err(_) => {
71                        // Can't even read 2 bytes - file is too small
72                        return Err(Jpeg2000Error::CodestreamError(
73                            "File too small to be valid JPEG2000".to_string(),
74                        ));
75                    }
76                }
77            }
78            Err(e) => return Err(e.into()),
79        };
80
81        Ok(Self {
82            reader,
83            metadata: None,
84            image_size: None,
85            coding_style: None,
86            quantization: None,
87            is_jp2,
88            resilience_mode: ResilienceMode::default(),
89            progressive_state: None,
90        })
91    }
92
93    /// Set error resilience mode
94    pub fn set_resilience_mode(&mut self, mode: ResilienceMode) {
95        self.resilience_mode = mode;
96    }
97
98    /// Get current error resilience mode
99    pub fn resilience_mode(&self) -> ResilienceMode {
100        self.resilience_mode
101    }
102
103    /// Enable basic error resilience
104    pub fn enable_error_resilience(&mut self) {
105        self.resilience_mode = ResilienceMode::Basic;
106    }
107
108    /// Enable full error resilience with aggressive recovery
109    pub fn enable_full_error_resilience(&mut self) {
110        self.resilience_mode = ResilienceMode::Full;
111    }
112
113    /// Disable error resilience
114    pub fn disable_error_resilience(&mut self) {
115        self.resilience_mode = ResilienceMode::None;
116    }
117
118    /// Parse file headers
119    pub fn parse_headers(&mut self) -> Result<()> {
120        if self.is_jp2 {
121            self.parse_jp2_headers()?;
122        } else {
123            self.parse_j2k_headers()?;
124        }
125
126        Ok(())
127    }
128
129    /// Parse JP2 format headers
130    fn parse_jp2_headers(&mut self) -> Result<()> {
131        // Parse JP2 metadata (includes ftyp, jp2h with ihdr and colr)
132        self.metadata = Some(Jp2Metadata::parse(&mut self.reader)?);
133
134        // Parse additional optional boxes (resolution, XML, UUID)
135        self.parse_optional_boxes()?;
136
137        // Find codestream box
138        let mut box_reader = BoxReader::new(&mut self.reader)?;
139
140        if let Some(jp2c_header) = box_reader.find_box(BoxType::ContiguousCodestream)? {
141            // Read codestream data
142            let codestream_data = box_reader.read_box_data(&jp2c_header)?;
143
144            // Parse codestream
145            let mut parser = CodestreamParser::new(std::io::Cursor::new(&codestream_data));
146            self.parse_codestream(&mut parser)?;
147        } else {
148            return Err(Jpeg2000Error::BoxParseError {
149                box_type: "jp2c".to_string(),
150                reason: "Codestream box not found".to_string(),
151            });
152        }
153
154        Ok(())
155    }
156
157    /// Parse optional JP2 boxes (resolution, XML, UUID, etc.)
158    fn parse_optional_boxes(&mut self) -> Result<()> {
159        let mut box_reader = BoxReader::new(&mut self.reader)?;
160
161        // Look for Resolution box inside jp2h
162        box_reader.reset()?;
163        if let Some(jp2h_header) = box_reader.find_box(BoxType::Jp2Header)? {
164            let jp2h_data = box_reader.read_box_data(&jp2h_header)?;
165            let mut jp2h_cursor = std::io::Cursor::new(&jp2h_data);
166            let mut sub_reader = BoxReader::new(&mut jp2h_cursor)?;
167
168            // Look for resolution superbox
169            if let Some(res_header) = sub_reader.find_box(BoxType::Resolution)? {
170                let res_data = sub_reader.read_box_data(&res_header)?;
171                let mut res_cursor = std::io::Cursor::new(&res_data);
172                let mut res_sub_reader = BoxReader::new(&mut res_cursor)?;
173
174                // Parse capture resolution
175                if let Some(resc_header) = res_sub_reader.find_box(BoxType::CaptureResolution)? {
176                    let resc_data = res_sub_reader.read_box_data(&resc_header)?;
177                    let mut resc_cursor = std::io::Cursor::new(&resc_data);
178                    if let Some(ref mut metadata) = self.metadata {
179                        metadata.capture_resolution =
180                            Some(crate::metadata::Resolution::parse(&mut resc_cursor)?);
181                    }
182                }
183
184                // Parse display resolution
185                res_sub_reader.reset()?;
186                if let Some(resd_header) = res_sub_reader.find_box(BoxType::DisplayResolution)? {
187                    let resd_data = res_sub_reader.read_box_data(&resd_header)?;
188                    let mut resd_cursor = std::io::Cursor::new(&resd_data);
189                    if let Some(ref mut metadata) = self.metadata {
190                        metadata.display_resolution =
191                            Some(crate::metadata::Resolution::parse(&mut resd_cursor)?);
192                    }
193                }
194            }
195        }
196
197        // Parse XML boxes (can be multiple)
198        box_reader.reset()?;
199        while let Some(xml_header) = box_reader.find_box(BoxType::Xml)? {
200            let xml_data = box_reader.read_box_data(&xml_header)?;
201            let mut xml_cursor = std::io::Cursor::new(&xml_data);
202            if let Some(ref mut metadata) = self.metadata {
203                if let Ok(xml_box) =
204                    crate::metadata::XmlMetadata::parse(&mut xml_cursor, xml_header.data_size())
205                {
206                    metadata.xml_boxes.push(xml_box);
207                }
208            }
209        }
210
211        // Parse UUID boxes (can be multiple)
212        box_reader.reset()?;
213        while let Some(uuid_header) = box_reader.find_box(BoxType::Uuid)? {
214            let uuid_data = box_reader.read_box_data(&uuid_header)?;
215            let mut uuid_cursor = std::io::Cursor::new(&uuid_data);
216            if let Some(ref mut metadata) = self.metadata {
217                if let Ok(uuid_box) =
218                    crate::metadata::UuidBox::parse(&mut uuid_cursor, uuid_header.data_size())
219                {
220                    metadata.uuid_boxes.push(uuid_box);
221                }
222            }
223        }
224
225        Ok(())
226    }
227
228    /// Parse raw J2K codestream headers
229    fn parse_j2k_headers(&mut self) -> Result<()> {
230        // Read entire codestream into buffer to avoid borrow checker issues
231        let mut codestream_data = Vec::new();
232        self.reader.read_to_end(&mut codestream_data)?;
233
234        let mut parser = CodestreamParser::new(std::io::Cursor::new(&codestream_data));
235        self.parse_codestream(&mut parser)?;
236        Ok(())
237    }
238
239    /// Parse codestream
240    fn parse_codestream<CS: Read>(&mut self, parser: &mut CodestreamParser<CS>) -> Result<()> {
241        // Verify SOC marker
242        match parser.read_marker() {
243            Ok(Some(Marker::Soc)) => {}
244            Ok(Some(m)) => {
245                if self.resilience_mode.is_enabled() {
246                    tracing::warn!(
247                        "Expected SOC marker, got {:?}, continuing with resilience mode",
248                        m
249                    );
250                } else {
251                    return Err(Jpeg2000Error::CodestreamError(format!(
252                        "Expected SOC marker, got {:?}",
253                        m
254                    )));
255                }
256            }
257            Ok(None) => {
258                if self.resilience_mode.is_enabled() {
259                    tracing::warn!(
260                        "Unexpected end of stream at SOC, continuing with resilience mode"
261                    );
262                } else {
263                    return Err(Jpeg2000Error::CodestreamError(
264                        "Unexpected end of stream".to_string(),
265                    ));
266                }
267            }
268            Err(e) => {
269                if self.resilience_mode.is_enabled() {
270                    tracing::warn!(
271                        "Error reading SOC marker: {}, continuing with resilience mode",
272                        e
273                    );
274                } else {
275                    return Err(e);
276                }
277            }
278        }
279
280        // Parse main header markers
281        loop {
282            let marker_result = parser.read_marker();
283
284            match marker_result {
285                Ok(Some(Marker::Siz)) => match parser.parse_siz() {
286                    Ok(siz) => self.image_size = Some(siz),
287                    Err(e) => {
288                        if self.resilience_mode.is_enabled() {
289                            tracing::warn!(
290                                "Error parsing SIZ marker: {}, using error concealment",
291                                e
292                            );
293                        } else {
294                            return Err(e);
295                        }
296                    }
297                },
298                Ok(Some(Marker::Cod)) => match parser.parse_cod() {
299                    Ok(cod) => self.coding_style = Some(cod),
300                    Err(e) => {
301                        if self.resilience_mode.is_enabled() {
302                            tracing::warn!("Error parsing COD marker: {}, using defaults", e);
303                        } else {
304                            return Err(e);
305                        }
306                    }
307                },
308                Ok(Some(Marker::Qcd)) => match parser.parse_qcd() {
309                    Ok(qcd) => self.quantization = Some(qcd),
310                    Err(e) => {
311                        if self.resilience_mode.is_enabled() {
312                            tracing::warn!("Error parsing QCD marker: {}, using defaults", e);
313                        } else {
314                            return Err(e);
315                        }
316                    }
317                },
318                Ok(Some(Marker::Sot)) => {
319                    // Start of tile - main header complete
320                    break;
321                }
322                Ok(Some(Marker::Eoc)) => {
323                    // End of codestream
324                    break;
325                }
326                Ok(Some(marker)) => {
327                    // Skip other markers
328                    if marker.has_segment() {
329                        match parser.read_segment_length() {
330                            Ok(length) => {
331                                if let Err(e) = parser.skip_segment(length) {
332                                    if self.resilience_mode.is_enabled() {
333                                        tracing::warn!(
334                                            "Error skipping marker segment: {}, continuing",
335                                            e
336                                        );
337                                    } else {
338                                        return Err(e);
339                                    }
340                                }
341                            }
342                            Err(e) => {
343                                if self.resilience_mode.is_enabled() {
344                                    tracing::warn!(
345                                        "Error reading segment length: {}, continuing",
346                                        e
347                                    );
348                                } else {
349                                    return Err(e);
350                                }
351                            }
352                        }
353                    }
354                }
355                Ok(None) => break,
356                Err(e) => {
357                    if self.resilience_mode.is_enabled() {
358                        tracing::warn!("Error reading marker: {}, attempting to continue", e);
359                        break;
360                    } else {
361                        return Err(e);
362                    }
363                }
364            }
365        }
366
367        // Verify required markers were found
368        if self.image_size.is_none() {
369            if self.resilience_mode.is_full() {
370                tracing::warn!("SIZ marker not found, using error concealment with default size");
371                // In full resilience mode, continue without SIZ (will fail later but gracefully)
372            } else {
373                return Err(Jpeg2000Error::CodestreamError(
374                    "SIZ marker not found".to_string(),
375                ));
376            }
377        }
378
379        Ok(())
380    }
381
382    /// Get image width
383    pub fn width(&self) -> Result<u32> {
384        if let Some(ref size) = self.image_size {
385            Ok(size.width)
386        } else if let Some(ref metadata) = self.metadata {
387            metadata
388                .image_header
389                .as_ref()
390                .map(|h| h.width)
391                .ok_or_else(|| Jpeg2000Error::InvalidImageHeader("No image header".to_string()))
392        } else {
393            Err(Jpeg2000Error::InvalidImageHeader(
394                "Image size not available".to_string(),
395            ))
396        }
397    }
398
399    /// Get image height
400    pub fn height(&self) -> Result<u32> {
401        if let Some(ref size) = self.image_size {
402            Ok(size.height)
403        } else if let Some(ref metadata) = self.metadata {
404            metadata
405                .image_header
406                .as_ref()
407                .map(|h| h.height)
408                .ok_or_else(|| Jpeg2000Error::InvalidImageHeader("No image header".to_string()))
409        } else {
410            Err(Jpeg2000Error::InvalidImageHeader(
411                "Image size not available".to_string(),
412            ))
413        }
414    }
415
416    /// Get number of components
417    pub fn num_components(&self) -> Result<u16> {
418        if let Some(ref size) = self.image_size {
419            Ok(size.num_components)
420        } else if let Some(ref metadata) = self.metadata {
421            metadata
422                .image_header
423                .as_ref()
424                .map(|h| h.num_components)
425                .ok_or_else(|| Jpeg2000Error::InvalidImageHeader("No image header".to_string()))
426        } else {
427            Err(Jpeg2000Error::InvalidImageHeader(
428                "Image size not available".to_string(),
429            ))
430        }
431    }
432
433    /// Get metadata
434    pub fn metadata(&self) -> Option<&Jp2Metadata> {
435        self.metadata.as_ref()
436    }
437
438    /// Decode image to RGB
439    pub fn decode_rgb(&mut self) -> Result<Vec<u8>> {
440        // This is a simplified decoder - full implementation would be much more complex
441        tracing::warn!("Using simplified JPEG2000 decoder - not suitable for production use");
442
443        let width = self.width()? as usize;
444        let height = self.height()? as usize;
445        let num_components = self.num_components()? as usize;
446
447        // For now, return a placeholder image
448        // Full implementation would:
449        // 1. Decode tiles
450        // 2. Apply tier-2 packet decoding
451        // 3. Apply tier-1 EBCOT decoding
452        // 4. Apply inverse wavelet transform
453        // 5. Apply inverse quantization
454        // 6. Apply color transform
455        // 7. Level shift
456
457        let placeholder = vec![128u8; width * height * 3];
458
459        tracing::info!(
460            "JPEG2000 decoder placeholder: {}x{} with {} components",
461            width,
462            height,
463            num_components
464        );
465
466        Ok(placeholder)
467    }
468
469    /// Decode image to RGBA
470    pub fn decode_rgba(&mut self) -> Result<Vec<u8>> {
471        let rgb = self.decode_rgb()?;
472        let num_pixels = rgb.len() / 3;
473
474        let mut rgba = Vec::with_capacity(num_pixels * 4);
475
476        for i in 0..num_pixels {
477            rgba.push(rgb[i * 3]);
478            rgba.push(rgb[i * 3 + 1]);
479            rgba.push(rgb[i * 3 + 2]);
480            rgba.push(255);
481        }
482
483        Ok(rgba)
484    }
485
486    /// Decode specific tile
487    pub fn decode_tile(&mut self, tile_x: u32, tile_y: u32) -> Result<Vec<u8>> {
488        let image_size = self.image_size.as_ref().ok_or_else(|| {
489            Jpeg2000Error::InvalidImageHeader("Image size not available".to_string())
490        })?;
491
492        if tile_x >= image_size.num_tiles_x() || tile_y >= image_size.num_tiles_y() {
493            return Err(Jpeg2000Error::InvalidTile(format!(
494                "Tile ({}, {}) out of bounds",
495                tile_x, tile_y
496            )));
497        }
498
499        // Placeholder implementation
500        let tile_width = image_size.tile_width as usize;
501        let tile_height = image_size.tile_height as usize;
502
503        Ok(vec![128u8; tile_width * tile_height * 3])
504    }
505
506    /// Get information about the image
507    pub fn info(&self) -> Result<ImageInfo> {
508        let width = self.width()?;
509        let height = self.height()?;
510        let num_components = self.num_components()?;
511
512        let num_tiles = if let Some(ref size) = self.image_size {
513            size.num_tiles()
514        } else {
515            1
516        };
517
518        let color_space = self
519            .metadata
520            .as_ref()
521            .and_then(|m| m.color_spec.as_ref())
522            .and_then(|c| c.enum_cs);
523
524        let num_levels = self
525            .coding_style
526            .as_ref()
527            .map(|cs| cs.num_levels)
528            .unwrap_or(0);
529
530        Ok(ImageInfo {
531            width,
532            height,
533            num_components,
534            num_tiles,
535            color_space,
536            num_decomposition_levels: num_levels,
537            is_jp2: self.is_jp2,
538        })
539    }
540
541    /// Get file type information (JP2 format only)
542    pub fn file_type(&self) -> Option<&crate::metadata::FileType> {
543        self.metadata.as_ref()?.file_type.as_ref()
544    }
545
546    /// Get image header information
547    pub fn image_header(&self) -> Option<&crate::metadata::ImageHeader> {
548        self.metadata.as_ref()?.image_header.as_ref()
549    }
550
551    /// Get color specification
552    pub fn color_specification(&self) -> Option<&crate::metadata::ColorSpecification> {
553        self.metadata.as_ref()?.color_spec.as_ref()
554    }
555
556    /// Get capture resolution (if present)
557    pub fn capture_resolution(&self) -> Option<&crate::metadata::Resolution> {
558        self.metadata.as_ref()?.capture_resolution.as_ref()
559    }
560
561    /// Get display resolution (if present)
562    pub fn display_resolution(&self) -> Option<&crate::metadata::Resolution> {
563        self.metadata.as_ref()?.display_resolution.as_ref()
564    }
565
566    /// Get capture resolution in DPI (if present)
567    pub fn capture_resolution_dpi(&self) -> Option<(f64, f64)> {
568        self.capture_resolution().map(|r| r.to_dpi())
569    }
570
571    /// Get display resolution in DPI (if present)
572    pub fn display_resolution_dpi(&self) -> Option<(f64, f64)> {
573        self.display_resolution().map(|r| r.to_dpi())
574    }
575
576    /// Get XML metadata boxes
577    pub fn xml_metadata(&self) -> Vec<&crate::metadata::XmlMetadata> {
578        self.metadata
579            .as_ref()
580            .map(|m| m.xml_boxes.iter().collect())
581            .unwrap_or_default()
582    }
583
584    /// Get UUID boxes
585    pub fn uuid_boxes(&self) -> Vec<&crate::metadata::UuidBox> {
586        self.metadata
587            .as_ref()
588            .map(|m| m.uuid_boxes.iter().collect())
589            .unwrap_or_default()
590    }
591
592    /// Get coding style information
593    pub fn coding_style(&self) -> Option<&CodingStyle> {
594        self.coding_style.as_ref()
595    }
596
597    /// Get quantization information
598    pub fn quantization(&self) -> Option<&Quantization> {
599        self.quantization.as_ref()
600    }
601
602    /// Get image size information from codestream
603    pub fn image_size_info(&self) -> Option<&ImageSize> {
604        self.image_size.as_ref()
605    }
606
607    /// Check if image uses multiple component transform (MCT)
608    pub fn uses_mct(&self) -> bool {
609        self.coding_style
610            .as_ref()
611            .map(|cs| cs.use_mct)
612            .unwrap_or(false)
613    }
614
615    /// Get number of quality layers
616    pub fn num_quality_layers(&self) -> u16 {
617        self.coding_style
618            .as_ref()
619            .map(|cs| cs.num_layers)
620            .unwrap_or(1)
621    }
622
623    /// Get number of decomposition levels
624    pub fn num_decomposition_levels(&self) -> u8 {
625        self.coding_style
626            .as_ref()
627            .map(|cs| cs.num_levels)
628            .unwrap_or(0)
629    }
630
631    /// Decode image progressively up to specified quality layer
632    ///
633    /// This method allows decoding only the first N quality layers, enabling
634    /// faster decoding at lower quality levels. Quality layers are ordered
635    /// from lowest (0) to highest quality.
636    ///
637    /// # Arguments
638    ///
639    /// * `max_layer` - Maximum quality layer to decode (0-based index)
640    ///
641    /// # Returns
642    ///
643    /// RGB image data decoded up to the specified quality layer
644    pub fn decode_quality_layers(&mut self, max_layer: u16) -> Result<Vec<u8>> {
645        let width = self.width()? as usize;
646        let height = self.height()? as usize;
647        let num_layers = self.num_quality_layers();
648
649        if max_layer >= num_layers {
650            return Err(Jpeg2000Error::Tier2Error(format!(
651                "Requested layer {} exceeds available layers {}",
652                max_layer, num_layers
653            )));
654        }
655
656        tracing::info!(
657            "Decoding quality layers 0-{} of {} (progressive)",
658            max_layer,
659            num_layers
660        );
661
662        // Initialize progressive state if not already present
663        if self.progressive_state.is_none() {
664            self.progressive_state = Some(ProgressiveDecodingState {
665                current_layer: 0,
666                max_layers: num_layers,
667                intermediate_data: vec![0u8; width * height * 3],
668                width,
669                height,
670            });
671        }
672
673        // Update progressive state
674        if let Some(ref mut state) = self.progressive_state {
675            state.current_layer = max_layer;
676
677            // Placeholder implementation - full implementation would:
678            // 1. Decode tiles up to specified quality layer
679            // 2. Apply tier-2 packet decoding for layers 0..=max_layer
680            // 3. Apply tier-1 EBCOT decoding
681            // 4. Apply inverse wavelet transform
682            // 5. Accumulate quality improvements
683
684            // For now, simulate progressive quality by scaling intensity
685            let quality_factor = (max_layer + 1) as f32 / num_layers as f32;
686            let base_value = (128.0 * quality_factor) as u8;
687
688            for pixel in state.intermediate_data.iter_mut() {
689                *pixel = base_value;
690            }
691
692            tracing::info!(
693                "Progressive decode to layer {} (quality factor: {:.2})",
694                max_layer,
695                quality_factor
696            );
697
698            Ok(state.intermediate_data.clone())
699        } else {
700            Err(Jpeg2000Error::Other(
701                "Failed to initialize progressive state".to_string(),
702            ))
703        }
704    }
705
706    /// Decode image progressively with automatic layer progression
707    ///
708    /// Returns an iterator that yields increasingly refined versions of the image,
709    /// one for each quality layer.
710    pub fn decode_progressive(&mut self) -> Result<ProgressiveDecoder<'_, R>> {
711        let num_layers = self.num_quality_layers();
712
713        Ok(ProgressiveDecoder {
714            reader: self,
715            current_layer: 0,
716            max_layers: num_layers,
717        })
718    }
719
720    /// Get current progressive decoding state
721    pub fn progressive_layer(&self) -> Option<u16> {
722        self.progressive_state.as_ref().map(|s| s.current_layer)
723    }
724
725    /// Reset progressive decoding state
726    pub fn reset_progressive_state(&mut self) {
727        self.progressive_state = None;
728    }
729
730    /// Check if progressive decoding is in progress
731    pub fn is_progressive_active(&self) -> bool {
732        self.progressive_state.is_some()
733    }
734
735    /// Decode a specific region of interest (ROI) from the image
736    ///
737    /// This method decodes only the specified rectangular region, which can be
738    /// more efficient than decoding the entire image when only a portion is needed.
739    ///
740    /// # Arguments
741    ///
742    /// * `x` - Left coordinate of the region (pixels)
743    /// * `y` - Top coordinate of the region (pixels)
744    /// * `width` - Width of the region (pixels)
745    /// * `height` - Height of the region (pixels)
746    ///
747    /// # Returns
748    ///
749    /// RGB image data for the specified region
750    pub fn decode_region(&mut self, x: u32, y: u32, width: u32, height: u32) -> Result<Vec<u8>> {
751        let image_width = self.width()?;
752        let image_height = self.height()?;
753
754        // Validate region bounds
755        if x + width > image_width {
756            return Err(Jpeg2000Error::InvalidDimension(format!(
757                "Region x+width ({}) exceeds image width ({})",
758                x + width,
759                image_width
760            )));
761        }
762
763        if y + height > image_height {
764            return Err(Jpeg2000Error::InvalidDimension(format!(
765                "Region y+height ({}) exceeds image height ({})",
766                y + height,
767                image_height
768            )));
769        }
770
771        tracing::info!(
772            "Decoding region: {}x{} at ({}, {}) from {}x{} image",
773            width,
774            height,
775            x,
776            y,
777            image_width,
778            image_height
779        );
780
781        // Determine which tiles intersect with the requested region
782        let tiles = self.compute_intersecting_tiles(x, y, width, height)?;
783
784        tracing::debug!("Region intersects with {} tiles", tiles.len());
785
786        // Placeholder implementation - full implementation would:
787        // 1. Identify tiles that intersect with the region
788        // 2. Decode only those tiles
789        // 3. Extract the relevant portion from each tile
790        // 4. Assemble the final region
791
792        let region_size = (width * height * 3) as usize;
793        let mut region_data = vec![128u8; region_size];
794
795        // Simulate region-specific decoding with a pattern
796        for py in 0..height {
797            for px in 0..width {
798                let idx = ((py * width + px) * 3) as usize;
799                if idx + 2 < region_data.len() {
800                    // Create a simple pattern to show ROI works
801                    region_data[idx] = ((px + x) % 256) as u8;
802                    region_data[idx + 1] = ((py + y) % 256) as u8;
803                    region_data[idx + 2] = 128;
804                }
805            }
806        }
807
808        Ok(region_data)
809    }
810
811    /// Decode a region at a specific resolution level
812    ///
813    /// JPEG2000 supports multi-resolution decoding through wavelet decomposition levels.
814    /// Resolution level 0 is the full resolution, level 1 is half resolution, etc.
815    ///
816    /// # Arguments
817    ///
818    /// * `x` - Left coordinate at target resolution (pixels)
819    /// * `y` - Top coordinate at target resolution (pixels)
820    /// * `width` - Width at target resolution (pixels)
821    /// * `height` - Height at target resolution (pixels)
822    /// * `resolution_level` - Resolution level (0 = full, 1 = half, 2 = quarter, etc.)
823    ///
824    /// # Returns
825    ///
826    /// RGB image data for the specified region at the specified resolution
827    pub fn decode_region_at_resolution(
828        &mut self,
829        x: u32,
830        y: u32,
831        width: u32,
832        height: u32,
833        resolution_level: u8,
834    ) -> Result<Vec<u8>> {
835        let max_levels = self.num_decomposition_levels();
836
837        if resolution_level > max_levels {
838            return Err(Jpeg2000Error::InvalidDimension(format!(
839                "Resolution level {} exceeds maximum decomposition levels {}",
840                resolution_level, max_levels
841            )));
842        }
843
844        let scale_factor = 1u32 << resolution_level;
845        let full_res_x = x * scale_factor;
846        let full_res_y = y * scale_factor;
847        let full_res_width = width * scale_factor;
848        let full_res_height = height * scale_factor;
849
850        let image_width = self.width()?;
851        let image_height = self.height()?;
852
853        if full_res_x + full_res_width > image_width || full_res_y + full_res_height > image_height
854        {
855            return Err(Jpeg2000Error::InvalidDimension(format!(
856                "Scaled region ({}x{} at {},{}) exceeds image bounds ({}x{})",
857                full_res_width, full_res_height, full_res_x, full_res_y, image_width, image_height
858            )));
859        }
860
861        tracing::info!(
862            "Decoding region {}x{} at ({},{}) with resolution level {} (scale 1/{})",
863            width,
864            height,
865            x,
866            y,
867            resolution_level,
868            scale_factor
869        );
870
871        // Placeholder implementation - full implementation would:
872        // 1. Decode wavelet subbands only up to the requested resolution level
873        // 2. Apply inverse wavelet transform only to that level
874        // 3. Extract the requested region at that resolution
875
876        let region_size = (width * height * 3) as usize;
877        let mut region_data = vec![128u8; region_size];
878
879        // Simulate lower resolution with averaged/blurred appearance
880        let blur_factor = scale_factor as u8;
881        for py in 0..height {
882            for px in 0..width {
883                let idx = ((py * width + px) * 3) as usize;
884                if idx + 2 < region_data.len() {
885                    region_data[idx] = ((px + x) / u32::from(blur_factor) % 128 + 64) as u8;
886                    region_data[idx + 1] = ((py + y) / u32::from(blur_factor) % 128 + 64) as u8;
887                    region_data[idx + 2] = 128;
888                }
889            }
890        }
891
892        Ok(region_data)
893    }
894
895    /// Compute which tiles intersect with a given region
896    fn compute_intersecting_tiles(
897        &self,
898        x: u32,
899        y: u32,
900        width: u32,
901        height: u32,
902    ) -> Result<Vec<(u32, u32)>> {
903        let image_size = self.image_size.as_ref().ok_or_else(|| {
904            Jpeg2000Error::InvalidImageHeader("Image size not available".to_string())
905        })?;
906
907        let tile_width = image_size.tile_width;
908        let tile_height = image_size.tile_height;
909        let tile_x_offset = image_size.tile_x_offset;
910        let tile_y_offset = image_size.tile_y_offset;
911
912        // Calculate tile range
913        let start_tile_x = if x >= tile_x_offset {
914            (x - tile_x_offset) / tile_width
915        } else {
916            0
917        };
918
919        let start_tile_y = if y >= tile_y_offset {
920            (y - tile_y_offset) / tile_height
921        } else {
922            0
923        };
924
925        let end_tile_x = if x + width >= tile_x_offset {
926            ((x + width - 1 - tile_x_offset) / tile_width).min(image_size.num_tiles_x() - 1)
927        } else {
928            0
929        };
930
931        let end_tile_y = if y + height >= tile_y_offset {
932            ((y + height - 1 - tile_y_offset) / tile_height).min(image_size.num_tiles_y() - 1)
933        } else {
934            0
935        };
936
937        let mut tiles = Vec::new();
938        for ty in start_tile_y..=end_tile_y {
939            for tx in start_tile_x..=end_tile_x {
940                tiles.push((tx, ty));
941            }
942        }
943
944        Ok(tiles)
945    }
946
947    /// Decode region using tile indices
948    ///
949    /// This is a lower-level method that decodes a region by specifying
950    /// the exact tiles to decode.
951    pub fn decode_region_from_tiles(
952        &mut self,
953        tiles: &[(u32, u32)],
954        region_x: u32,
955        region_y: u32,
956        region_width: u32,
957        region_height: u32,
958    ) -> Result<Vec<u8>> {
959        tracing::info!(
960            "Decoding {} tiles for region {}x{} at ({},{})",
961            tiles.len(),
962            region_width,
963            region_height,
964            region_x,
965            region_y
966        );
967
968        // Placeholder implementation
969        let region_size = (region_width * region_height * 3) as usize;
970        Ok(vec![128u8; region_size])
971    }
972}
973
974/// Progressive decoder iterator
975///
976/// Yields increasingly refined image data as quality layers are decoded.
977pub struct ProgressiveDecoder<'a, R> {
978    reader: &'a mut Jpeg2000Reader<R>,
979    current_layer: u16,
980    max_layers: u16,
981}
982
983impl<'a, R: Read + Seek> ProgressiveDecoder<'a, R> {
984    /// Get next quality layer
985    pub fn next_layer(&mut self) -> Result<Option<Vec<u8>>> {
986        if self.current_layer >= self.max_layers {
987            return Ok(None);
988        }
989
990        let data = self.reader.decode_quality_layers(self.current_layer)?;
991        self.current_layer += 1;
992
993        Ok(Some(data))
994    }
995
996    /// Get current layer index
997    pub fn current_layer(&self) -> u16 {
998        self.current_layer
999    }
1000
1001    /// Get total number of layers
1002    pub fn total_layers(&self) -> u16 {
1003        self.max_layers
1004    }
1005
1006    /// Get progress as percentage (0.0 - 1.0)
1007    pub fn progress(&self) -> f64 {
1008        if self.max_layers == 0 {
1009            1.0
1010        } else {
1011            f64::from(self.current_layer) / f64::from(self.max_layers)
1012        }
1013    }
1014
1015    /// Check if decoding is complete
1016    pub fn is_complete(&self) -> bool {
1017        self.current_layer >= self.max_layers
1018    }
1019
1020    /// Skip to specific layer
1021    pub fn skip_to_layer(&mut self, layer: u16) -> Result<Vec<u8>> {
1022        if layer >= self.max_layers {
1023            return Err(Jpeg2000Error::Tier2Error(format!(
1024                "Layer {} exceeds maximum {}",
1025                layer, self.max_layers
1026            )));
1027        }
1028
1029        self.current_layer = layer;
1030        self.reader.decode_quality_layers(layer)
1031    }
1032}
1033
1034/// Image information
1035#[derive(Debug, Clone)]
1036pub struct ImageInfo {
1037    /// Image width
1038    pub width: u32,
1039    /// Image height
1040    pub height: u32,
1041    /// Number of components
1042    pub num_components: u16,
1043    /// Number of tiles
1044    pub num_tiles: u32,
1045    /// Color space
1046    pub color_space: Option<EnumeratedColorSpace>,
1047    /// Number of wavelet decomposition levels
1048    pub num_decomposition_levels: u8,
1049    /// Is JP2 format (vs raw codestream)
1050    pub is_jp2: bool,
1051}
1052
1053#[cfg(test)]
1054mod tests {
1055    use super::*;
1056    use std::io::Cursor;
1057
1058    #[test]
1059    fn test_reader_creation() {
1060        // Create minimal JP2 signature
1061        let data = vec![
1062            0x00, 0x00, 0x00, 0x0C, // Box length
1063            0x6A, 0x50, 0x20, 0x20, // 'jP  '
1064            0x0D, 0x0A, 0x87, 0x0A, // Signature
1065        ];
1066
1067        let cursor = Cursor::new(data);
1068        let result = Jpeg2000Reader::new(cursor);
1069        assert!(result.is_ok());
1070
1071        let reader = result.expect("reader failed");
1072        assert!(reader.is_jp2);
1073    }
1074
1075    #[test]
1076    fn test_j2k_detection() {
1077        // Create minimal J2K codestream (SOC marker + padding to 12 bytes for detection)
1078        let data = vec![
1079            0xFF, 0x4F, // SOC marker
1080            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // padding
1081        ];
1082
1083        let cursor = Cursor::new(data);
1084        let result = Jpeg2000Reader::new(cursor);
1085        assert!(result.is_ok());
1086
1087        let reader = result.expect("reader failed");
1088        assert!(!reader.is_jp2);
1089    }
1090
1091    #[test]
1092    fn test_resilience_mode_default() {
1093        let data = vec![
1094            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1095        ];
1096        let cursor = Cursor::new(data);
1097        let reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1098
1099        assert_eq!(reader.resilience_mode(), ResilienceMode::None);
1100        assert!(!reader.resilience_mode().is_enabled());
1101    }
1102
1103    #[test]
1104    fn test_resilience_mode_configuration() {
1105        let data = vec![
1106            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1107        ];
1108        let cursor = Cursor::new(data);
1109        let mut reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1110
1111        // Test basic resilience
1112        reader.enable_error_resilience();
1113        assert_eq!(reader.resilience_mode(), ResilienceMode::Basic);
1114        assert!(reader.resilience_mode().is_enabled());
1115
1116        // Test full resilience
1117        reader.enable_full_error_resilience();
1118        assert_eq!(reader.resilience_mode(), ResilienceMode::Full);
1119        assert!(reader.resilience_mode().is_full());
1120
1121        // Test disable
1122        reader.disable_error_resilience();
1123        assert_eq!(reader.resilience_mode(), ResilienceMode::None);
1124    }
1125
1126    #[test]
1127    fn test_progressive_state_initialization() {
1128        let data = vec![
1129            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1130        ];
1131        let cursor = Cursor::new(data);
1132        let reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1133
1134        assert!(!reader.is_progressive_active());
1135        assert!(reader.progressive_layer().is_none());
1136    }
1137
1138    #[test]
1139    fn test_progressive_state_reset() {
1140        let data = vec![
1141            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1142        ];
1143        let cursor = Cursor::new(data);
1144        let mut reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1145
1146        // Initialize state by setting it manually
1147        reader.progressive_state = Some(ProgressiveDecodingState {
1148            current_layer: 2,
1149            max_layers: 5,
1150            intermediate_data: vec![],
1151            width: 256,
1152            height: 256,
1153        });
1154
1155        assert!(reader.is_progressive_active());
1156        assert_eq!(reader.progressive_layer(), Some(2));
1157
1158        // Reset state
1159        reader.reset_progressive_state();
1160        assert!(!reader.is_progressive_active());
1161        assert!(reader.progressive_layer().is_none());
1162    }
1163
1164    #[test]
1165    fn test_region_bounds_validation() {
1166        let data = vec![
1167            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1168        ];
1169        let cursor = Cursor::new(data);
1170        let mut reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1171
1172        // Set up minimal image size
1173        reader.image_size = Some(ImageSize {
1174            width: 256,
1175            height: 256,
1176            x_offset: 0,
1177            y_offset: 0,
1178            tile_width: 256,
1179            tile_height: 256,
1180            tile_x_offset: 0,
1181            tile_y_offset: 0,
1182            num_components: 3,
1183            components: vec![],
1184        });
1185
1186        // Valid region should work (though it will return placeholder data)
1187        let result = reader.decode_region(0, 0, 128, 128);
1188        assert!(result.is_ok());
1189
1190        // Region exceeding width should fail
1191        let result = reader.decode_region(200, 0, 100, 128);
1192        assert!(result.is_err());
1193
1194        // Region exceeding height should fail
1195        let result = reader.decode_region(0, 200, 128, 100);
1196        assert!(result.is_err());
1197    }
1198
1199    #[test]
1200    fn test_compute_intersecting_tiles() {
1201        let data = vec![
1202            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1203        ];
1204        let cursor = Cursor::new(data);
1205        let mut reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1206
1207        // Set up image with multiple tiles
1208        reader.image_size = Some(ImageSize {
1209            width: 512,
1210            height: 512,
1211            x_offset: 0,
1212            y_offset: 0,
1213            tile_width: 128,
1214            tile_height: 128,
1215            tile_x_offset: 0,
1216            tile_y_offset: 0,
1217            num_components: 3,
1218            components: vec![],
1219        });
1220
1221        // Region in first tile only
1222        let tiles = reader.compute_intersecting_tiles(0, 0, 64, 64);
1223        assert!(tiles.is_ok());
1224        let tiles = tiles.expect("tiles");
1225        assert_eq!(tiles.len(), 1);
1226        assert_eq!(tiles[0], (0, 0));
1227
1228        // Region spanning multiple tiles
1229        let tiles = reader.compute_intersecting_tiles(64, 64, 128, 128);
1230        assert!(tiles.is_ok());
1231        let tiles = tiles.expect("tiles");
1232        assert!(!tiles.is_empty());
1233
1234        // Region covering entire image
1235        let tiles = reader.compute_intersecting_tiles(0, 0, 512, 512);
1236        assert!(tiles.is_ok());
1237        let tiles = tiles.expect("tiles");
1238        assert_eq!(tiles.len(), 16); // 4x4 tiles
1239    }
1240
1241    #[test]
1242    fn test_resolution_level_scaling() {
1243        let data = vec![
1244            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1245        ];
1246        let cursor = Cursor::new(data);
1247        let mut reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1248
1249        // Set up image
1250        reader.image_size = Some(ImageSize {
1251            width: 256,
1252            height: 256,
1253            x_offset: 0,
1254            y_offset: 0,
1255            tile_width: 256,
1256            tile_height: 256,
1257            tile_x_offset: 0,
1258            tile_y_offset: 0,
1259            num_components: 3,
1260            components: vec![],
1261        });
1262
1263        // Set coding style with decomposition levels
1264        reader.coding_style = Some(CodingStyle {
1265            progression_order: crate::codestream::ProgressionOrder::Lrcp,
1266            num_layers: 5,
1267            use_mct: true,
1268            num_levels: 3,
1269            code_block_width: 64,
1270            code_block_height: 64,
1271            code_block_style: 0,
1272            wavelet: crate::codestream::WaveletTransform::Reversible53,
1273        });
1274
1275        // Decode at full resolution (level 0)
1276        let result = reader.decode_region_at_resolution(0, 0, 128, 128, 0);
1277        assert!(result.is_ok());
1278        let data = result.expect("data");
1279        assert_eq!(data.len(), 128 * 128 * 3);
1280
1281        // Decode at half resolution (level 1)
1282        let result = reader.decode_region_at_resolution(0, 0, 64, 64, 1);
1283        assert!(result.is_ok());
1284        let data = result.expect("data");
1285        assert_eq!(data.len(), 64 * 64 * 3);
1286
1287        // Invalid resolution level should fail
1288        let result = reader.decode_region_at_resolution(0, 0, 64, 64, 10);
1289        assert!(result.is_err());
1290    }
1291
1292    #[test]
1293    fn test_metadata_accessors() {
1294        let data = vec![
1295            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1296        ];
1297        let cursor = Cursor::new(data);
1298        let reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1299
1300        // Initially, all metadata should be None
1301        assert!(reader.file_type().is_none());
1302        assert!(reader.image_header().is_none());
1303        assert!(reader.color_specification().is_none());
1304        assert!(reader.capture_resolution().is_none());
1305        assert!(reader.display_resolution().is_none());
1306        assert!(reader.xml_metadata().is_empty());
1307        assert!(reader.uuid_boxes().is_empty());
1308    }
1309
1310    #[test]
1311    fn test_quality_layer_accessors() {
1312        let data = vec![
1313            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1314        ];
1315        let cursor = Cursor::new(data);
1316        let mut reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1317
1318        // Default should be 1 layer
1319        assert_eq!(reader.num_quality_layers(), 1);
1320
1321        // Set coding style with multiple layers
1322        reader.coding_style = Some(CodingStyle {
1323            progression_order: crate::codestream::ProgressionOrder::Lrcp,
1324            num_layers: 10,
1325            use_mct: false,
1326            num_levels: 5,
1327            code_block_width: 64,
1328            code_block_height: 64,
1329            code_block_style: 0,
1330            wavelet: crate::codestream::WaveletTransform::Reversible53,
1331        });
1332
1333        assert_eq!(reader.num_quality_layers(), 10);
1334        assert_eq!(reader.num_decomposition_levels(), 5);
1335        assert!(!reader.uses_mct());
1336    }
1337
1338    #[test]
1339    fn test_progressive_decoder_iterator() {
1340        let data = vec![
1341            0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
1342        ];
1343        let cursor = Cursor::new(data);
1344        let mut reader = Jpeg2000Reader::new(cursor).expect("reader creation failed");
1345
1346        // Set up minimal configuration
1347        reader.image_size = Some(ImageSize {
1348            width: 64,
1349            height: 64,
1350            x_offset: 0,
1351            y_offset: 0,
1352            tile_width: 64,
1353            tile_height: 64,
1354            tile_x_offset: 0,
1355            tile_y_offset: 0,
1356            num_components: 3,
1357            components: vec![],
1358        });
1359
1360        reader.coding_style = Some(CodingStyle {
1361            progression_order: crate::codestream::ProgressionOrder::Lrcp,
1362            num_layers: 3,
1363            use_mct: false,
1364            num_levels: 2,
1365            code_block_width: 32,
1366            code_block_height: 32,
1367            code_block_style: 0,
1368            wavelet: crate::codestream::WaveletTransform::Reversible53,
1369        });
1370
1371        let decoder = reader.decode_progressive().expect("decoder");
1372
1373        assert_eq!(decoder.total_layers(), 3);
1374        assert_eq!(decoder.current_layer(), 0);
1375        assert!(!decoder.is_complete());
1376        assert_eq!(decoder.progress(), 0.0);
1377    }
1378}