Revert "feat: add YCbCr chroma subsampling support for non-JPEG compression"
This reverts commit 83409cbdf3.
This commit is contained in:
1 parent
617dc6eed8
commit
85215e81e3
3 files changed
+14
-107
No files matched your search
+10
-19
@@ -952,10 +952,15 @@ impl Image {
|
|||||||
|
|
||||||
// Only this color type interprets the tag, which is defined with a default of (2, 2)
|
// 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) {
|
if matches!(color, ColorType::YCbCr(_)) && self.chroma_subsampling != (1, 1) {
|
||||||
// The JPEG library does upsampling for us. For other compression methods,
|
// The JPEG library does upsampling for us and defines its buffers correctly
|
||||||
// the expand_chunk pipeline wraps the decompressor in a YCbCrUpsamplingReader
|
// (presumably). All other compression schemes are not supported..
|
||||||
// that converts the subsampled block layout to full-resolution pixel rows.
|
//
|
||||||
if matches!(self.planar_config, PlanarConfiguration::Planar) {
|
// 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(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::ChromaSubsampling,
|
TiffUnsupportedError::ChromaSubsampling,
|
||||||
));
|
));
|
||||||
@@ -1155,7 +1160,7 @@ impl Image {
|
|||||||
let is_all_bits = samples == data_samples;
|
let is_all_bits = samples == data_samples;
|
||||||
let is_output_chunk_rows = layout.row_stride == chunk_row_bytes;
|
let is_output_chunk_rows = layout.row_stride == chunk_row_bytes;
|
||||||
|
|
||||||
let mut reader: Box<dyn Read> = Self::create_reader(
|
let mut reader = Self::create_reader(
|
||||||
reader.inner(),
|
reader.inner(),
|
||||||
compression_method,
|
compression_method,
|
||||||
*compressed_bytes,
|
*compressed_bytes,
|
||||||
@@ -1165,20 +1170,6 @@ impl Image {
|
|||||||
self.fill_order,
|
self.fill_order,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// For non-JPEG YCbCr with chroma subsampling, wrap the decompressor in an
|
|
||||||
// upsampling reader that converts the subsampled block layout to full-resolution
|
|
||||||
// pixel rows. JPEG handles upsampling internally.
|
|
||||||
if matches!(color_type, ColorType::YCbCr(_))
|
|
||||||
&& self.chroma_subsampling != (1, 1)
|
|
||||||
&& !matches!(compression_method, CompressionMethod::ModernJPEG)
|
|
||||||
{
|
|
||||||
reader = Box::new(super::stream::YCbCrUpsamplingReader::new(
|
|
||||||
reader,
|
|
||||||
data_dims,
|
|
||||||
self.chroma_subsampling,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extended bit depths (9-15, 17-31): packed N-bit samples must be unpacked to
|
// Extended bit depths (9-15, 17-31): packed N-bit samples must be unpacked to
|
||||||
// standard-width output samples before endianness fix and predictor.
|
// standard-width output samples before endianness fix and predictor.
|
||||||
let needs_unpacking = layout.tiff_bits_per_sample != layout.output_bits_per_sample
|
let needs_unpacking = layout.tiff_bits_per_sample != layout.output_bits_per_sample
|
||||||
|
|||||||
@@ -507,90 +507,6 @@ impl Read for Group3Reader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts YCbCr data from chroma-subsampled block layout to full-resolution
|
|
||||||
/// 3-bytes-per-pixel YCbCr output. Each block of h×v pixels stores h*v Y samples
|
|
||||||
/// plus one shared Cb and one shared Cr sample. This reader expands those into
|
|
||||||
/// per-pixel (Y, Cb, Cr) triples.
|
|
||||||
pub struct YCbCrUpsamplingReader<R: Read> {
|
|
||||||
reader: R,
|
|
||||||
width: u32,
|
|
||||||
horiz_sub: u32,
|
|
||||||
vert_sub: u32,
|
|
||||||
height: u32,
|
|
||||||
current_row: u32,
|
|
||||||
output_buf: io::Cursor<Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Read> YCbCrUpsamplingReader<R> {
|
|
||||||
pub fn new(reader: R, dimensions: (u32, u32), chroma: (u16, u16)) -> Self {
|
|
||||||
Self {
|
|
||||||
reader,
|
|
||||||
width: dimensions.0,
|
|
||||||
horiz_sub: u32::from(chroma.0),
|
|
||||||
vert_sub: u32::from(chroma.1),
|
|
||||||
height: dimensions.1,
|
|
||||||
current_row: 0,
|
|
||||||
output_buf: io::Cursor::new(Vec::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Read> Read for YCbCrUpsamplingReader<R> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
// If output buffer is exhausted, decode next block row
|
|
||||||
if self.output_buf.position() as usize >= self.output_buf.get_ref().len() {
|
|
||||||
if self.current_row >= self.height {
|
|
||||||
return Ok(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let h = self.horiz_sub;
|
|
||||||
let v = self.vert_sub;
|
|
||||||
let blocks_across = self.width.div_ceil(h);
|
|
||||||
let block_size = (h * v + 2) as usize;
|
|
||||||
let block_row_bytes = blocks_across as usize * block_size;
|
|
||||||
|
|
||||||
let rows_in_block = v.min(self.height - self.current_row) as usize;
|
|
||||||
|
|
||||||
let mut subsampled = vec![0u8; block_row_bytes];
|
|
||||||
self.reader.read_exact(&mut subsampled)?;
|
|
||||||
|
|
||||||
let output_row_bytes = self.width as usize * 3;
|
|
||||||
let output = self.output_buf.get_mut();
|
|
||||||
output.clear();
|
|
||||||
output.resize(rows_in_block * output_row_bytes, 0);
|
|
||||||
|
|
||||||
let mut src_offset = 0;
|
|
||||||
for bx in 0..blocks_across {
|
|
||||||
let x0 = (bx * h) as usize;
|
|
||||||
|
|
||||||
let y_samples = &subsampled[src_offset..src_offset + (h * v) as usize];
|
|
||||||
let cb = subsampled[src_offset + (h * v) as usize];
|
|
||||||
let cr = subsampled[src_offset + (h * v) as usize + 1];
|
|
||||||
src_offset += block_size;
|
|
||||||
|
|
||||||
for dy in 0..rows_in_block {
|
|
||||||
for dx in 0..h as usize {
|
|
||||||
let x = x0 + dx;
|
|
||||||
if x >= self.width as usize {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let y_val = y_samples[dy * h as usize + dx];
|
|
||||||
let pixel_offset = dy * output_row_bytes + x * 3;
|
|
||||||
output[pixel_offset] = y_val;
|
|
||||||
output[pixel_offset + 1] = cb;
|
|
||||||
output[pixel_offset + 2] = cr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.current_row += v;
|
|
||||||
self.output_buf.set_position(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.output_buf.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "webp")]
|
#[cfg(feature = "webp")]
|
||||||
pub struct WebPReader {
|
pub struct WebPReader {
|
||||||
inner: Cursor<Vec<u8>>,
|
inner: Cursor<Vec<u8>>,
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ const FILES: &[(&str, Option<u32>)] = &[
|
|||||||
("tests/libtiffpic/depth/genimages", None),
|
("tests/libtiffpic/depth/genimages", None),
|
||||||
("tests/libtiffpic/depth/README.txt", None),
|
("tests/libtiffpic/depth/README.txt", None),
|
||||||
("tests/libtiffpic/depth/summary.txt", None),
|
("tests/libtiffpic/depth/summary.txt", None),
|
||||||
("tests/libtiffpic/dscf0013.tif", Some(0x6896e83a)),
|
("tests/libtiffpic/dscf0013.tif", Some(0)), // unsupported YCbCr chroma subsampling
|
||||||
("tests/libtiffpic/fax2d.tif", Some(0xc2f30c6f)),
|
("tests/libtiffpic/fax2d.tif", Some(0xc2f30c6f)),
|
||||||
// No support for direct CCITT Group 3 1D format
|
// No support for direct CCITT Group 3 1D format
|
||||||
("tests/libtiffpic/g3test.g3", None),
|
("tests/libtiffpic/g3test.g3", None),
|
||||||
@@ -193,11 +193,11 @@ const FILES: &[(&str, Option<u32>)] = &[
|
|||||||
("tests/libtiffpic/quad-lzw.tif", Some(0)), // invalid LZW stream
|
("tests/libtiffpic/quad-lzw.tif", Some(0)), // invalid LZW stream
|
||||||
("tests/libtiffpic/quad-tile.tif", Some(0x6055f9ee)),
|
("tests/libtiffpic/quad-tile.tif", Some(0x6055f9ee)),
|
||||||
("tests/libtiffpic/README", None),
|
("tests/libtiffpic/README", None),
|
||||||
("tests/libtiffpic/smallliz.tif", Some(0)), // unsupported Old JPEG compression (type 6)
|
("tests/libtiffpic/smallliz.tif", Some(0)), // unsupported YCbCr chroma subsampling
|
||||||
("tests/libtiffpic/strike.tif", Some(0x016b0f6a)),
|
("tests/libtiffpic/strike.tif", Some(0x016b0f6a)),
|
||||||
("tests/libtiffpic/text.tif", Some(0)), // unsupported compression
|
("tests/libtiffpic/text.tif", Some(0)), // unsupported compression
|
||||||
("tests/libtiffpic/ycbcr-cat.tif", Some(0x388a9d8f)),
|
("tests/libtiffpic/ycbcr-cat.tif", Some(0)), // unsupported YCbCr chroma subsampling
|
||||||
("tests/libtiffpic/zackthecat.tif", Some(0)), // unsupported Old JPEG compression (type 6)
|
("tests/libtiffpic/zackthecat.tif", Some(0)), // unsupported YCbCr chroma subsampling
|
||||||
];
|
];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
Reference in new issue
Block a user