use log::{debug, info, warn};
use std::io::SeekFrom;
use image::{ImageBuffer, Rgb};
use crate::io::seekable::SeekableReader;
use crate::tiff::{TiffReader, TiffError};
use crate::tiff::errors::TiffResult;
use crate::tiff::ifd::IFD;
use crate::tiff::constants::{tags, predictor as pred_consts};
use crate::compression::CompressionFactory;
use crate::utils::image_extraction_utils;
use super::region::Region;
pub struct StripReader<'a, R: SeekableReader> {
reader: R,
ifd: &'a IFD,
tiff_reader: &'a TiffReader<'a>,
}
impl<'a, R: SeekableReader> StripReader<'a, R> {
pub fn new(reader: R, ifd: &'a IFD, tiff_reader: &'a TiffReader<'a>) -> Self {
StripReader {
reader,
ifd,
tiff_reader
}
}
fn get_strip_parameters(&self) -> TiffResult<(u32, u32)> {
let (img_width, _) = self.ifd.get_dimensions()
.ok_or_else(|| TiffError::GenericError("Missing image dimensions".to_string()))?;
let rows_per_strip = self.ifd.get_tag_value(tags::ROWS_PER_STRIP)
.unwrap_or(img_width) as u32;
Ok((rows_per_strip, img_width as u32))
}
fn read_strip(
&mut self,
offset: u64,
byte_count: u64,
compression_handler: &dyn crate::compression::CompressionHandler,
predictor: usize,
width: usize,
rows_per_strip: usize
) -> TiffResult<Vec<u8>> {
self.reader.seek(SeekFrom::Start(offset))?;
let mut compressed_data = vec![0u8; byte_count as usize];
self.reader.read_exact(&mut compressed_data)?;
let mut strip_data = compression_handler.decompress(&compressed_data)?;
if predictor == pred_consts::HORIZONTAL_DIFFERENCING as usize {
image_extraction_utils::apply_horizontal_predictor(&mut strip_data, width, rows_per_strip);
}
Ok(strip_data)
}
pub fn extract(
&mut self,
image: &mut ImageBuffer<Rgb<u8>, Vec<u8>>,
region: Region
) -> TiffResult<()> {
let (rows_per_strip, img_width) = self.get_strip_parameters()?;
let compression = self.ifd.get_tag_value(tags::COMPRESSION).unwrap_or(1);
let compression_handler = CompressionFactory::create_handler(compression)?;
info!("Using compression: {}", compression_handler.name());
let predictor = self.ifd.get_tag_value(tags::PREDICTOR).unwrap_or(1) as usize;
let strip_offsets = self.tiff_reader.read_tag_values(&mut self.reader, self.ifd, tags::STRIP_OFFSETS)?;
let strip_byte_counts = self.tiff_reader.read_tag_values(&mut self.reader, self.ifd, tags::STRIP_BYTE_COUNTS)?;
info!("Rows per strip: {}", rows_per_strip);
info!("Total strips: {}", strip_offsets.len());
let start_strip = region.y / rows_per_strip;
let end_strip = (region.end_y() + rows_per_strip - 1) / rows_per_strip;
info!("Processing strips from {} to {}", start_strip, end_strip - 1);
for strip_idx in start_strip..end_strip {
if strip_idx as usize >= strip_offsets.len() {
warn!("Strip index {} out of bounds (max {})",
strip_idx, strip_offsets.len() - 1);
continue;
}
let offset = strip_offsets[strip_idx as usize];
let byte_count = strip_byte_counts[strip_idx as usize];
debug!("Reading strip {} at offset {} with {} bytes",
strip_idx, offset, byte_count);
let strip_data = match self.read_strip(
offset,
byte_count,
&*compression_handler,
predictor,
img_width as usize,
rows_per_strip as usize
) {
Ok(data) => data,
Err(e) => {
warn!("Error reading strip {}: {:?}", strip_idx, e);
continue;
}
};
let strip_start_y = strip_idx * rows_per_strip;
self.copy_strip_to_image(
&strip_data,
image,
img_width as usize,
rows_per_strip as usize,
strip_start_y,
region
);
}
Ok(())
}
fn copy_strip_to_image(
&self,
strip_data: &[u8],
image: &mut ImageBuffer<Rgb<u8>, Vec<u8>>,
width: usize,
rows_in_strip: usize,
strip_start_y: u32,
region: Region
) {
for row in 0..rows_in_strip {
let global_y = strip_start_y + row as u32;
if global_y < region.y || global_y >= region.end_y() {
continue;
}
let row_start = row * width;
for x in region.x..region.end_x() {
if x >= width as u32 {
continue;
}
let strip_idx = row_start + x as usize;
image_extraction_utils::copy_pixel(
strip_data,
image,
x,
global_y,
strip_idx,
region
);
}
}
}
}