Skip to content

Commit fddeed5

Browse files
committed
incorporated feedback
1 parent 6c9a8cf commit fddeed5

File tree

7 files changed

+393
-582
lines changed

7 files changed

+393
-582
lines changed

python/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ primary_ifd.geo_key_directory.projected_type
3131
# 26918
3232
# (EPSG code)
3333

34-
primary_ifd.sample_format
34+
primary_ifd.sample_format[0]
3535
# [<SampleFormat.Uint: 1>, <SampleFormat.Uint: 1>, <SampleFormat.Uint: 1>]
3636

37-
primary_ifd.bits_per_sample
37+
primary_ifd.bits_per_sample[0]
3838
# [8, 8, 8]
3939

4040
tile = await tiff.fetch_tile(0, 0, 4)
@@ -75,9 +75,9 @@ primary_ifd.geo_key_directory.citation
7575
# EPSG code
7676
primary_ifd.geo_key_directory.projected_type
7777

78-
primary_ifd.sample_format[0]
78+
primary_ifd.sample_format
7979
# <SampleFormat.Uint: 1>
80-
primary_ifd.bits_per_sample[0]
80+
primary_ifd.bits_per_sample
8181
# 16
8282

8383
tile = await tiff.fetch_tile(0, 0, 0)

src/cog.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ mod test {
2525

2626
use crate::decoder::DecoderRegistry;
2727
use crate::metadata::{PrefetchBuffer, TiffMetadataReader};
28-
use crate::predictor::RevPredictorRegistry;
2928
use crate::reader::{AsyncFileReader, ObjectReader};
3029

3130
use super::*;
@@ -53,9 +52,8 @@ mod test {
5352

5453
let ifd = &tiff.ifds[1];
5554
let decoder_registry = DecoderRegistry::default();
56-
let predictor_registry = RevPredictorRegistry::default();
5755
let tile = ifd.fetch_tile(0, 0, reader.as_ref()).await.unwrap();
58-
let tile = tile.decode(&decoder_registry, &predictor_registry).unwrap();
56+
let tile = tile.decode(&decoder_registry, &ifd).unwrap();
5957
std::fs::write("img.buf", tile).unwrap();
6058
}
6159

src/ifd.rs

Lines changed: 141 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::tiff::tags::{
1212
SampleFormat, Tag,
1313
};
1414
use crate::tiff::{TiffError, Value};
15-
use crate::tile::{PredictorInfo, Tile};
15+
use crate::tile::Tile;
1616

1717
const DOCUMENT_NAME: u16 = 269;
1818

@@ -190,15 +190,18 @@ impl ImageFileDirectory {
190190

191191
let mut other_tags = HashMap::new();
192192

193-
// for x in tag_data.into_iter() {
194-
195-
// }
196193
tag_data.into_iter().try_for_each(|(tag, value)| {
197194
match tag {
198195
Tag::NewSubfileType => new_subfile_type = Some(value.into_u32()?),
199196
Tag::ImageWidth => image_width = Some(value.into_u32()?),
200197
Tag::ImageLength => image_height = Some(value.into_u32()?),
201-
Tag::BitsPerSample => bits_per_sample = Some(value.into_u16_vec()?),
198+
Tag::BitsPerSample => bits_per_sample = Some({
199+
let values = value.into_u16_vec()?;
200+
if values.iter().any(|v| *v != values[0]) {
201+
return Err(TiffError::UnsupportedError(crate::tiff::TiffUnsupportedError::InconsistentBitsPerSample(values)));
202+
}
203+
values
204+
}),
202205
Tag::Compression => {
203206
compression = Some(CompressionMethod::from_u16_exhaustive(value.into_u16()?))
204207
}
@@ -241,12 +244,11 @@ impl ImageFileDirectory {
241244
Tag::ExtraSamples => extra_samples = Some(value.into_u16_vec()?),
242245
Tag::SampleFormat => {
243246
let values = value.into_u16_vec()?;
244-
sample_format = Some(
245-
values
246-
.into_iter()
247-
.map(SampleFormat::from_u16_exhaustive)
248-
.collect(),
249-
);
247+
// we don't support mixed sample formats
248+
if values.iter().any(|v| *v!= values[0]) {
249+
return Err(TiffError::UnsupportedError(crate::tiff::TiffUnsupportedError::UnsupportedSampleFormat(values.iter().map(|v| SampleFormat::from_u16_exhaustive(*v)).collect())));
250+
}
251+
sample_format = Some(values.iter().map(|v| SampleFormat::from_u16_exhaustive(*v)).collect());
250252
}
251253
Tag::JPEGTables => jpeg_tables = Some(value.into_u8_vec()?.into()),
252254
Tag::Copyright => copyright = Some(value.into_string()?),
@@ -681,30 +683,6 @@ impl ImageFileDirectory {
681683
Some(offset as _..(offset + byte_count) as _)
682684
}
683685

684-
fn get_predictor_info(&self) -> PredictorInfo {
685-
PredictorInfo {
686-
endianness: self.endianness,
687-
image_width: self.image_width,
688-
image_height: self.image_height,
689-
chunk_width: if self.tile_width.is_none() {
690-
// we are stripped => image_width
691-
self.image_width
692-
} else {
693-
self.tile_width.unwrap()
694-
},
695-
chunk_height: if self.tile_height.is_none() {
696-
self.rows_per_strip
697-
.expect("no tile height and no rows_per_strip")
698-
} else {
699-
self.tile_height.unwrap()
700-
},
701-
bits_per_sample: &self.bits_per_sample,
702-
samples_per_pixel: self.samples_per_pixel,
703-
sample_format: &self.sample_format,
704-
planar_configuration: self.planar_configuration,
705-
}
706-
}
707-
708686
/// Fetch the tile located at `x` column and `y` row using the provided reader.
709687
pub async fn fetch_tile(
710688
&self,
@@ -719,12 +697,7 @@ impl ImageFileDirectory {
719697
Ok(Tile {
720698
x,
721699
y,
722-
predictor: self.predictor.unwrap_or(Predictor::None),
723-
predictor_info: self.get_predictor_info(),
724700
compressed_bytes,
725-
compression_method: self.compression,
726-
photometric_interpretation: self.photometric_interpretation,
727-
jpeg_tables: self.jpeg_tables.clone(),
728701
})
729702
}
730703

@@ -756,12 +729,7 @@ impl ImageFileDirectory {
756729
let tile = Tile {
757730
x,
758731
y,
759-
predictor: self.predictor.unwrap_or(Predictor::None),
760-
predictor_info: self.get_predictor_info(),
761732
compressed_bytes,
762-
compression_method: self.compression,
763-
photometric_interpretation: self.photometric_interpretation,
764-
jpeg_tables: self.jpeg_tables.clone(),
765733
};
766734
tiles.push(tile);
767735
}
@@ -775,4 +743,132 @@ impl ImageFileDirectory {
775743
let y_count = (self.image_height as f64 / self.tile_height? as f64).ceil();
776744
Some((x_count as usize, y_count as usize))
777745
}
746+
747+
748+
/// width of a chunk (strip or tile)
749+
///
750+
/// In case of tile, this is [`Tag::TileWidth`], otherwise [`Tag::ImageWidth`]
751+
pub fn chunk_width(&self) -> u32 {
752+
if self.tile_width.is_none() {
753+
// we are stripped => image_width
754+
self.image_width
755+
} else {
756+
self.tile_width.unwrap()
757+
}
758+
}
759+
760+
/// Height of a chunk (strip or tile)
761+
///
762+
/// in case of tile, this is [`Tag::TileLength`], otherwise [`Tag::RowsPerStrip`]
763+
///
764+
/// # Panics
765+
///
766+
/// if neither `tile_height` or `rows_per_strip` is found
767+
pub fn chunk_height(&self) -> u32 {
768+
if self.tile_height.is_none() {
769+
self.rows_per_strip
770+
.expect("no tile height and no rows_per_strip")
771+
} else {
772+
self.tile_height.unwrap()
773+
}
774+
}
775+
776+
/// chunk width in pixels, taking padding into account
777+
///
778+
/// strips are considered image-width chunks
779+
pub fn chunk_width_pixels(&self, x: u32) -> AsyncTiffResult<u32> {
780+
if x >= self.chunks_across() {
781+
Err(crate::error::AsyncTiffError::TileIndexError(
782+
x,
783+
self.chunks_across(),
784+
))
785+
} else if x == self.chunks_across() - 1 {
786+
// last chunk
787+
Ok(self.image_width - self.chunk_width() * x)
788+
} else {
789+
Ok(self.chunk_width())
790+
}
791+
}
792+
793+
/// chunk height in pixels, taking padding into account
794+
pub fn chunk_height_pixels(&self, y: u32) -> AsyncTiffResult<u32> {
795+
if y >= self.chunks_down() {
796+
Err(crate::error::AsyncTiffError::TileIndexError(
797+
y,
798+
self.chunks_down(),
799+
))
800+
} else if y == self.chunks_down() - 1 {
801+
// last chunk
802+
Ok(self.image_height - self.chunk_height() * y)
803+
} else {
804+
Ok(self.chunk_height())
805+
}
806+
}
807+
808+
/// get the output row stride in bytes, taking padding into account
809+
pub fn output_row_stride(&self, x: u32) -> AsyncTiffResult<usize> {
810+
Ok((self.chunk_width_pixels(x)? as usize).saturating_mul(self.bits_per_pixel()) / 8)
811+
}
812+
813+
/// The total number of bits per pixel.
814+
///
815+
/// Technically bits_per_sample.len() should be *equal* to samples, but libtiff also allows
816+
/// it to be a single value that applies to all samples.
817+
pub fn bits_per_pixel(&self) -> usize {
818+
match self.planar_configuration {
819+
PlanarConfiguration::Chunky => {
820+
self.samples_per_pixel as usize * self.bits_per_sample[0] as usize
821+
}
822+
PlanarConfiguration::Planar => self.bits_per_sample[0] as usize,
823+
}
824+
}
825+
826+
/// The number of chunks in the horizontal (x) direction
827+
pub fn chunks_across(&self) -> u32 {
828+
self.image_width.div_ceil(self.chunk_width())
829+
}
830+
831+
/// The number of chunks in the vertical (y) direction
832+
pub fn chunks_down(&self) -> u32 {
833+
self.image_height.div_ceil(self.chunk_height())
834+
}
835+
}
836+
837+
838+
#[cfg(test)]
839+
mod test {
840+
use std::collections::HashMap;
841+
842+
use crate::{
843+
reader::Endianness,
844+
tiff::{tags::{PlanarConfiguration, Tag, PhotometricInterpretation}, Value}, ImageFileDirectory,
845+
};
846+
847+
// use super::PredictorInfo;
848+
849+
#[test]
850+
fn test_chunk_width_pixels() {
851+
let mut hmap = HashMap::new();
852+
hmap.insert(Tag::ImageWidth, Value::Unsigned(15));
853+
hmap.insert(Tag::ImageLength, Value::Unsigned(17));
854+
hmap.insert(Tag::TileWidth, Value::Unsigned(8));
855+
hmap.insert(Tag::TileLength, Value::Unsigned(8));
856+
hmap.insert(Tag::BitsPerSample, Value::Short(8));
857+
hmap.insert(Tag::SamplesPerPixel, Value::Short(1));
858+
hmap.insert(Tag::PlanarConfiguration, Value::Short(PlanarConfiguration::Chunky.to_u16()));
859+
hmap.insert(Tag::PhotometricInterpretation, Value::Short(PhotometricInterpretation::BlackIsZero.to_u16()));
860+
let info = ImageFileDirectory::from_tags(hmap, Endianness::LittleEndian).unwrap();
861+
assert_eq!(info.bits_per_pixel(), 8);
862+
assert_eq!(info.chunks_across(), 2);
863+
assert_eq!(info.chunks_down(), 3);
864+
assert_eq!(info.chunk_width_pixels(0).unwrap(), info.chunk_width());
865+
assert_eq!(info.chunk_width_pixels(1).unwrap(), 7);
866+
info.chunk_width_pixels(2).unwrap_err();
867+
assert_eq!(info.chunk_height_pixels(0).unwrap(), info.chunk_height());
868+
assert_eq!(info.chunk_height_pixels(2).unwrap(), 1);
869+
info.chunk_height_pixels(3).unwrap_err();
870+
assert_eq!(info.output_row_stride(0).unwrap(), info.chunk_width() as _); //single-byte samples
871+
assert_eq!(info.output_row_stride(1).unwrap(), 7);
872+
info.output_row_stride(2).unwrap_err();
873+
}
778874
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ mod tile;
1515

1616
pub use cog::TIFF;
1717
pub use ifd::ImageFileDirectory;
18-
pub use tile::{PredictorInfo, Tile};
18+
pub use tile::Tile;

0 commit comments

Comments
 (0)