Merge pull request #319 from image-rs/ycbcr-subsampling

Recognize Ycbcr/Chroma subsampling tags
This commit is contained in:
Aurelia Molzer authored and GitHub committed 2026-01-05 20:57:17 +00:00
commit a3ab252168
5 files changed
+60 -1

No files matched your search

+38
View File
@@ -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,23 @@ 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) {
// The JPEG library does upsampling for us and defines its buffers correctly
// (presumably). All other compression schemes are not supported..
//
// NOTE: as explained in <fa225e820b96bef35f01bf4685654beeb4a8df0c> we may be better
// off supporting this tag by consistently upsampling, not by adjusting the buffer
// size. At least as a default this makes more sense and is much more permissive in
// case the compression stream disagrees with the tags (we would not have enough / or
// the wrong buffer layout if we only asked for subsampled planes in a planar layout).
if !matches!(self.compression_method, CompressionMethod::ModernJPEG) {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::ChromaSubsampling,
));
}
}
Ok(ReadoutLayout {
planar_config: self.planar_config,
color,
+1
View File
@@ -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()?;