Recognize YCbCr/Chomra subsampling tags
In particular we avoid mistaking the absence of this tag for an absence of subsampling. The specification instead requires it to mean half the samples in both directions—a mode that we do not support yet. Instead of returning erroneous samples or an error in the data stream (which would appear too short) we return an error when asked to layout the read buffer for these images.
This commit is contained in:
1 parent
d38fba4268
commit
23d1867f75
4 files changed
+40
No files matched your search
@@ -85,6 +85,7 @@ pub(crate) struct Image {
|
||||
pub tile_attributes: Option<TileAttributes>,
|
||||
pub chunk_offsets: Vec<u64>,
|
||||
pub chunk_bytes: Vec<u64>,
|
||||
pub chroma_subsampling: (u16, u16),
|
||||
}
|
||||
|
||||
/// Describes how to read a tile-aligned portion of the image.
|
||||
@@ -275,6 +276,25 @@ impl Image {
|
||||
.transpose()?
|
||||
.unwrap_or(PlanarConfiguration::Chunky);
|
||||
|
||||
let ycbcr_subsampling = tag_reader.find_tag_uint_vec::<u16>(Tag::ChromaSubsampling)?;
|
||||
|
||||
let chroma_subsampling = if let Some(subsamples) = &ycbcr_subsampling {
|
||||
let [a, b] = subsamples.as_slice() else {
|
||||
return Err(TiffError::FormatError(TiffFormatError::InvalidCountForTag(
|
||||
Tag::ChromaSubsampling,
|
||||
subsamples.len(),
|
||||
)));
|
||||
};
|
||||
|
||||
// ImageWidth and ImageLength are constrained to be integer multiples of
|
||||
// YCbCrSubsampleHoriz and YCbCrSubsampleVert respectively. TileWidth and TileLength
|
||||
// have the same constraints. RowsPerStrip must be an integer multiple of
|
||||
// YCbCrSubsampleVert.
|
||||
(*a, *b)
|
||||
} else {
|
||||
(2, 2)
|
||||
};
|
||||
|
||||
let planes = match planar_config {
|
||||
PlanarConfiguration::Chunky => 1,
|
||||
PlanarConfiguration::Planar => samples,
|
||||
@@ -386,6 +406,7 @@ impl Image {
|
||||
tile_attributes,
|
||||
chunk_offsets,
|
||||
chunk_bytes,
|
||||
chroma_subsampling,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -715,6 +736,13 @@ impl Image {
|
||||
));
|
||||
}
|
||||
|
||||
// Only this color type interprets the tag, which is defined with a default of (2, 2)
|
||||
if matches!(color, ColorType::YCbCr(_)) && self.chroma_subsampling != (1, 1) {
|
||||
return Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::ChromaSubsampling,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(ReadoutLayout {
|
||||
planar_config: self.planar_config,
|
||||
color,
|
||||
|
||||
@@ -860,6 +860,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
tile_attributes: None,
|
||||
chunk_offsets: Vec::new(),
|
||||
chunk_bytes: Vec::new(),
|
||||
chroma_subsampling: (2, 2),
|
||||
},
|
||||
};
|
||||
decoder.next_image()?;
|
||||
|
||||
@@ -90,6 +90,9 @@ quick_error! {
|
||||
InvalidTypeForTag {
|
||||
display("tag has invalid type")
|
||||
}
|
||||
InvalidCountForTag(tag: Tag, len: usize) {
|
||||
display("tag `{tag:?}` with incorrect number of elements ({len}) encountered")
|
||||
}
|
||||
StripTileTagConflict {
|
||||
display("file should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found")
|
||||
}
|
||||
@@ -156,6 +159,9 @@ quick_error! {
|
||||
UnsupportedInterpretation(interpretation: PhotometricInterpretation) {
|
||||
display("unsupported photometric interpretation \"{interpretation:?}\"")
|
||||
}
|
||||
ChromaSubsampling {
|
||||
display("chroma subsampling of YCbCr color is unsupported")
|
||||
}
|
||||
MisalignedTileBoundaries {
|
||||
display("tile rows are not aligned to byte boundaries")
|
||||
}
|
||||
|
||||
@@ -122,6 +122,11 @@ pub enum Tag(u16) unknown(
|
||||
SMaxSampleValue = 341, // TODO add support
|
||||
// JPEG
|
||||
JPEGTables = 347,
|
||||
// Subsampling
|
||||
#[doc(alias = "YCbCrSubsampling")]
|
||||
ChromaSubsampling = 530, // TODO add support
|
||||
#[doc(alias = "YCbCrPositioning")]
|
||||
ChromaPositioning = 531, // TODO add support
|
||||
// GeoTIFF
|
||||
ModelPixelScaleTag = 33550, // (SoftDesk)
|
||||
ModelTransformationTag = 34264, // (JPL Carto Group)
|
||||
|
||||
Reference in new issue
Block a user