From 44eebff62f07940bd6e51f805a9e62ca08698302 Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Thu, 12 May 2022 23:01:12 +0200 Subject: [PATCH] #678, #679: TIFF read support for YCbCr Planar with or without subsampling --- .../imageio/metadata/tiff/TIFFEntry.java | 4 + .../imageio/plugins/tiff/TIFFImageReader.java | 163 ++++++++++++-- .../tiff/YCbCrPlanarUpsamplerStream.java | 198 ++++++++++++++++++ .../plugins/tiff/YCbCrUpsamplerStream.java | 4 - .../plugins/tiff/TIFFImageReaderTest.java | 14 +- .../resources/tiff/planar-yuv410-jpeg-lzw.tif | Bin 0 -> 2648 bytes .../tiff/planar-yuv410-jpeg-uncompressed.tif | Bin 0 -> 18788 bytes .../resources/tiff/planar-yuv420-jpeg-lzw.tif | Bin 0 -> 3264 bytes .../tiff/planar-yuv420-jpeg-uncompressed.tif | Bin 0 -> 24932 bytes .../tiff/planar-yuv422-bt601-lzw.tif | Bin 0 -> 3830 bytes .../tiff/planar-yuv422-bt601-uncompressed.tif | Bin 0 -> 33124 bytes .../resources/tiff/planar-yuv422-jpeg-lzw.tif | Bin 0 -> 3942 bytes .../tiff/planar-yuv422-jpeg-uncompressed.tif | Bin 0 -> 33124 bytes .../resources/tiff/planar-yuv444-jpeg-lzw.tif | Bin 0 -> 5418 bytes .../tiff/planar-yuv444-jpeg-uncompressed.tif | Bin 0 -> 49508 bytes 15 files changed, 357 insertions(+), 26 deletions(-) create mode 100644 imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/YCbCrPlanarUpsamplerStream.java create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv410-jpeg-lzw.tif create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv410-jpeg-uncompressed.tif create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv420-jpeg-lzw.tif create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv420-jpeg-uncompressed.tif create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv422-bt601-lzw.tif create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv422-bt601-uncompressed.tif create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv422-jpeg-lzw.tif create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv422-jpeg-uncompressed.tif create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv444-jpeg-lzw.tif create mode 100644 imageio/imageio-tiff/src/test/resources/tiff/planar-yuv444-jpeg-uncompressed.tif diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/tiff/TIFFEntry.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/tiff/TIFFEntry.java index e54ddfb4..fe9fe794 100644 --- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/tiff/TIFFEntry.java +++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/tiff/TIFFEntry.java @@ -173,10 +173,14 @@ public final class TIFFEntry extends AbstractEntry { return "TileByteCounts"; case TIFF.TAG_COPYRIGHT: return "Copyright"; + case TIFF.TAG_YCBCR_COEFFICIENTS: + return "YCbCrCoefficients"; case TIFF.TAG_YCBCR_SUB_SAMPLING: return "YCbCrSubSampling"; case TIFF.TAG_YCBCR_POSITIONING: return "YCbCrPositioning"; + case TIFF.TAG_REFERENCE_BLACK_WHITE: + return "ReferenceBlackWhite"; case TIFF.TAG_COLOR_MAP: return "ColorMap"; case TIFF.TAG_INK_SET: diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java index d9785a8c..3a41a3a9 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java @@ -1092,19 +1092,14 @@ public final class TIFFImageReader extends ImageReaderBase { : createStreamAdapter(imageInput); adapter = createFillOrderStream(fillOrder, adapter); - adapter = createDecompressorStream(compression, stripTileWidth, numBands, adapter); - adapter = createUnpredictorStream(predictor, stripTileWidth, numBands, bitsPerSample, adapter, imageInput.getByteOrder()); - if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR && rowRaster.getTransferType() == DataBuffer.TYPE_BYTE) { - adapter = new YCbCrUpsamplerStream(adapter, yCbCrSubsampling, yCbCrPos, colsInTile); - } - else if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR && rowRaster.getTransferType() == DataBuffer.TYPE_USHORT) { - adapter = new YCbCr16UpsamplerStream(adapter, yCbCrSubsampling, yCbCrPos, colsInTile, imageInput.getByteOrder()); - } - else if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) { - // Handled in getRawImageType - throw new AssertionError(); - } + // For subsampled planar, the compressed data will not be full width + int compressedStripTileWidth = planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR && b > 0 && yCbCrSubsampling != null + ? ((stripTileWidth + yCbCrSubsampling[0] - 1) / yCbCrSubsampling[0]) + : stripTileWidth; + adapter = createDecompressorStream(compression, compressedStripTileWidth, numBands, adapter); + adapter = createUnpredictorStream(predictor, compressedStripTileWidth, numBands, bitsPerSample, adapter, imageInput.getByteOrder()); + adapter = createYCbCrUpsamplerStream(interpretation, planarConfiguration, b, rowRaster.getTransferType(), yCbCrSubsampling, yCbCrPos, colsInTile, adapter, imageInput.getByteOrder()); if (needsBitPadding) { // We'll pad "odd" bitsPerSample streams to the smallest data type (byte/short/int) larger than the input @@ -1129,6 +1124,11 @@ public final class TIFFImageReader extends ImageReaderBase { readStripTileData(clippedRow, srcRegion, xSub, ySub, b, numBands, interpretation, destRaster, col, srcRow, colsInTile, rowsInTile, input); } + // Need to do color normalization after reading all bands for planar + if (planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR) { + normalizeColorPlanar(interpretation, destRaster); + } + col += colsInTile; if (abortRequested()) { @@ -1567,7 +1567,7 @@ public final class TIFFImageReader extends ImageReaderBase { break; - // Known, but unsupported compression types + // Known, but unsupported compression types case TIFFCustom.COMPRESSION_NEXT: case TIFFCustom.COMPRESSION_CCITTRLEW: case TIFFCustom.COMPRESSION_THUNDERSCAN: @@ -1582,8 +1582,8 @@ public final class TIFFImageReader extends ImageReaderBase { case TIFFCustom.COMPRESSION_SGILOG: case TIFFCustom.COMPRESSION_SGILOG24: case TIFFCustom.COMPRESSION_JPEG2000: // Doable with JPEG2000 plugin? - throw new IIOException("Unsupported TIFF Compression value: " + compression); + default: throw new IIOException("Unknown TIFF Compression value: " + compression); } @@ -1595,6 +1595,28 @@ public final class TIFFImageReader extends ImageReaderBase { return destination; } + private InputStream createYCbCrUpsamplerStream(int photometricInterpretation, int planarConfiguration, int plane, int transferType, + int[] yCbCrSubsampling, int yCbCrPos, int colsInTile, InputStream stream, ByteOrder byteOrder) { + if (photometricInterpretation == TIFFExtension.PHOTOMETRIC_YCBCR) { + if (planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR && transferType == DataBuffer.TYPE_BYTE) { + // For planar YCbCr, only the chroma planes are subsampled + return plane > 0 && (yCbCrSubsampling[0] != 1 || yCbCrSubsampling[1] != 1) + ? new YCbCrPlanarUpsamplerStream(stream, yCbCrSubsampling, yCbCrPos, colsInTile) : stream; + } + else if (transferType == DataBuffer.TYPE_BYTE) { + return new YCbCrUpsamplerStream(stream, yCbCrSubsampling, yCbCrPos, colsInTile); + } + else if (transferType == DataBuffer.TYPE_USHORT) { + return new YCbCr16UpsamplerStream(stream, yCbCrSubsampling, yCbCrPos, colsInTile, byteOrder); + } + + // Handled in getRawImageType + throw new AssertionError(); + } + + return stream; + } + private boolean containsZero(long[] byteCounts) { for (long byteCount : byteCounts) { if (byteCount <= 0) { @@ -1919,12 +1941,8 @@ public final class TIFFImageReader extends ImageReaderBase { } } -// if (banded) { -// // TODO: Normalize colors for tile (need to know tile region and sample model) -// // Unfortunately, this will disable acceleration... -// } - break; + case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: /*for (int band = 0; band < bands; band++)*/ { @@ -1962,6 +1980,7 @@ public final class TIFFImageReader extends ImageReaderBase { } break; + case DataBuffer.TYPE_INT: /*for (int band = 0; band < bands; band++)*/ { int[] rowDataInt = ((DataBufferInt) dataBuffer).getData(band); @@ -2101,6 +2120,102 @@ public final class TIFFImageReader extends ImageReaderBase { } } + private void normalizeColorPlanar(int photometricInterpretation, WritableRaster raster) throws IIOException { + // TODO: Other transfer types? + if (raster.getTransferType() != DataBuffer.TYPE_BYTE) { + return; + } + + byte[] pixel = null; + + switch (photometricInterpretation) { + case TIFFExtension.PHOTOMETRIC_YCBCR: + + // Default: CCIR Recommendation 601-1: 299/1000, 587/1000 and 114/1000 + double[] coefficients = getValueAsDoubleArray(TIFF.TAG_YCBCR_COEFFICIENTS, "YCbCrCoefficients", false, 3); + + // "Default" [0, 255, 128, 255, 128, 255] for YCbCr (real default is [0, 255, 0, 255, 0, 255] for RGB) + double[] referenceBW = getValueAsDoubleArray(TIFF.TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite", false, 6); + + if ((coefficients == null || Arrays.equals(coefficients, CCIR_601_1_COEFFICIENTS)) + && (referenceBW == null || Arrays.equals(referenceBW, REFERENCE_BLACK_WHITE_YCC_DEFAULT))) { + + // Fast, default conversion + for (int y = 0; y < raster.getHeight(); y++) { + for (int x = 0; x < raster.getWidth(); x++) { + pixel = (byte[]) raster.getDataElements(x, y, pixel); + YCbCrConverter.convertJPEGYCbCr2RGB(pixel, pixel, 0); + raster.setDataElements(x, y, pixel); + } + } + } + else { + // If one of the values are null, we'll need the other here... + if (coefficients == null) { + coefficients = CCIR_601_1_COEFFICIENTS; + } + + if (referenceBW != null && Arrays.equals(referenceBW, REFERENCE_BLACK_WHITE_YCC_DEFAULT)) { + referenceBW = null; + } + + for (int y = 0; y < raster.getHeight(); y++) { + for (int x = 0; x < raster.getWidth(); x++) { + pixel = (byte[]) raster.getDataElements(x, y, pixel); + YCbCrConverter.convertYCbCr2RGB(pixel, pixel, coefficients, referenceBW, 0); + raster.setDataElements(x, y, pixel); + } + } + } + + break; + + case TIFFExtension.PHOTOMETRIC_CIELAB: + case TIFFExtension.PHOTOMETRIC_ICCLAB: + case TIFFExtension.PHOTOMETRIC_ITULAB: + // TODO: White point may be encoded in separate tag + CIELabColorConverter converter = new CIELabColorConverter( + photometricInterpretation == TIFFExtension.PHOTOMETRIC_CIELAB + ? Illuminant.D65 + : Illuminant.D50 + ); + + float[] temp = new float[3]; + + for (int y = 0; y < raster.getHeight(); y++) { + for (int x = 0; x < raster.getWidth(); x++) { + pixel = (byte[]) raster.getDataElements(x, y, pixel); + + float LStar = (pixel[0] & 0xff) * 100f / 255.0f; + float aStar; + float bStar; + + if (photometricInterpretation == TIFFExtension.PHOTOMETRIC_CIELAB) { + // -128...127 + aStar = pixel[1]; + bStar = pixel[2]; + } + else { + // Assumes same data for ICC and ITU (unsigned) + // 0...255 + aStar = (pixel[1] & 0xff) - 128; + bStar = (pixel[2] & 0xff) - 128; + } + + converter.toRGB(LStar, aStar, bStar, temp); + + pixel[0] = (byte) temp[0]; + pixel[1] = (byte) temp[1]; + pixel[2] = (byte) temp[2]; + + raster.setDataElements(x, y, pixel); + } + } + + break; + } + } + private void normalizeColor(int photometricInterpretation, byte[] data) throws IOException { switch (photometricInterpretation) { case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO: @@ -2705,8 +2820,14 @@ public final class TIFFImageReader extends ImageReaderBase { try { long start = System.currentTimeMillis(); - int width = reader.getWidth(imageNo); - int height = reader.getHeight(imageNo); +// int width = reader.getWidth(imageNo); +// int height = reader.getHeight(imageNo); + if (param.canSetSourceRenderSize()) { + int thumbSize = 512; + float aspectRatio = reader.getAspectRatio(imageNo); + param.setSourceRenderSize(aspectRatio > 1f ? new Dimension(thumbSize, (int) Math.ceil(thumbSize / aspectRatio)) + : new Dimension((int) Math.ceil(thumbSize * aspectRatio), thumbSize)); + } // param.setSourceRegion(new Rectangle(width / 4, height / 4, width / 2, height / 2)); // param.setSourceRegion(new Rectangle(100, 300, 400, 400)); // param.setSourceRegion(new Rectangle(95, 105, 100, 100)); diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/YCbCrPlanarUpsamplerStream.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/YCbCrPlanarUpsamplerStream.java new file mode 100644 index 00000000..e32a3c53 --- /dev/null +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/YCbCrPlanarUpsamplerStream.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2022, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.imageio.plugins.tiff; + +import com.twelvemonkeys.lang.Validate; + +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Input stream that provides on-the-fly upsampling of TIFF subsampled YCbCr samples. + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: YCbCrUpsamplerStream.java,v 1.0 31.01.13 09:25 haraldk Exp$ + */ +final class YCbCrPlanarUpsamplerStream extends FilterInputStream { + + private final int horizChromaSub; + private final int vertChromaSub; + private final int yCbCrPos; + private final int columns; + + private final int units; + private final byte[] decodedRows; + int decodedLength; + int decodedPos; + + private final byte[] buffer; + int bufferLength; + int bufferPos; + + public YCbCrPlanarUpsamplerStream(final InputStream stream, final int[] chromaSub, final int yCbCrPos, final int columns) { + super(Validate.notNull(stream, "stream")); + + Validate.notNull(chromaSub, "chromaSub"); + Validate.isTrue(chromaSub.length == 2, "chromaSub.length != 2"); + + this.horizChromaSub = chromaSub[0]; + this.vertChromaSub = chromaSub[1]; + this.yCbCrPos = yCbCrPos; + this.columns = columns; + + units = (columns + horizChromaSub - 1) / horizChromaSub; // If columns % horizChromasSub != 0... + // ...each coded row will be padded to fill unit + decodedRows = new byte[columns * vertChromaSub]; + buffer = new byte[units]; + } + + private void fetch() throws IOException { + if (bufferPos >= bufferLength) { + int pos = 0; + int read; + + // This *SHOULD* read an entire row of units into the buffer, otherwise decodeRows will throw EOFException + while (pos < buffer.length && (read = in.read(buffer, pos, buffer.length - pos)) > 0) { + pos += read; + } + + bufferLength = pos; + bufferPos = 0; + } + + if (bufferLength > 0) { + decodeRows(); + } + else { + decodedLength = -1; + } + } + + private void decodeRows() throws EOFException { + decodedLength = decodedRows.length; + + for (int u = 0; u < units; u++) { + if (u >= bufferLength) { + throw new EOFException("Unexpected end of stream"); + } + + // Decode one unit + byte c = buffer[u]; + + for (int y = 0; y < vertChromaSub; y++) { + for (int x = 0; x < horizChromaSub; x++) { + // Skip padding at end of row + int column = horizChromaSub * u + x; + if (column >= columns) { + break; + } + + int pixelOff = column + columns * y; + decodedRows[pixelOff] = c; + } + } + } + + bufferPos = bufferLength; + decodedPos = 0; + } + + @Override + public int read() throws IOException { + if (decodedLength < 0) { + return -1; + } + + if (decodedPos >= decodedLength) { + fetch(); + + if (decodedLength < 0) { + return -1; + } + } + + return decodedRows[decodedPos++] & 0xff; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (decodedLength < 0) { + return -1; + } + + if (decodedPos >= decodedLength) { + fetch(); + + if (decodedLength < 0) { + return -1; + } + } + + int read = Math.min(decodedLength - decodedPos, len); + System.arraycopy(decodedRows, decodedPos, b, off, read); + decodedPos += read; + + return read; + } + + @Override + public long skip(long n) throws IOException { + if (decodedLength < 0) { + return -1; + } + + if (decodedPos >= decodedLength) { + fetch(); + + if (decodedLength < 0) { + return -1; + } + } + + int skipped = (int) Math.min(decodedLength - decodedPos, n); + decodedPos += skipped; + + return skipped; + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public synchronized void reset() throws IOException { + throw new IOException("mark/reset not supported"); + } +} diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/YCbCrUpsamplerStream.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/YCbCrUpsamplerStream.java index f4c07fba..989cf5c8 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/YCbCrUpsamplerStream.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/YCbCrUpsamplerStream.java @@ -213,8 +213,4 @@ final class YCbCrUpsamplerStream extends FilterInputStream { public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } - - private static byte clamp(int val) { - return (byte) Math.max(0, Math.min(255, val)); - } } diff --git a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java index 642954c4..e452cfe8 100644 --- a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java +++ b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java @@ -173,7 +173,19 @@ public class TIFFImageReaderTest extends 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 AL-*BpDol*abv0Ffgz{F@qRnG*J)Ieez nF#tsvM1bnem;}M%3DcE5Z7y6^jY&w0*sp69#=g#t(e0Kj+A0Km(uGHgstw_~1c7%IZg zoQTr{^z?&8`Ph;=oMFX!%0i&Tm?xG3;LEx!NgZOY(}74`h6GdbGy%|<&WSh*q%t&i z6ADl47ayIbeb7>mXa11UypRUG%&HRB%VmF=g=10y%LwdE696%Sa15`&pLTgu7LPS z4MwN~YTS2Toz|`x@BOGb;sE7qpr$`P>@;`(%A@V`E}f$tcAzBKf0EgVxr@!^_&1#q z&5WV)QLvLZg3>KH((|?13*4nZ3p-`7nW2A1Kc%ooh6vZ4hmeI4RK9-8@R?a%c;S0K zV@hNdrxKjjs9Lp2FaY-Ph{qo@l83OBqXN7@?*_R)xv8PQLQMpQFyl7o(CJ(6jm#!M zZ+Ue-Z*y*TSbCmO?v*XPAw@|yO%_5c*2x>_$tV4?G^{0bAoTIz?DG}@5gG>8c%_S? z*4x`^c1m>x^YVjrffZ95OZ-@!j4|27;&#T~r9TW1?rhFU_41No*a0f-S-PZP) z+2Fs~-AC#^`-RzpT;?UI4avz-rlCsvNiVX<2o!erx@5l%9yC(xlsUPUaTW2mjhn`< z#MRZ;!>kZ9$I!z7kLGL$I^8~pC9mja=-)|+%X>(M(ghn1joW=cSb0)y_HsYQ>hkH` zDOyOq&{)1GXf{t=$@U(4KF5zszB@x>|D65ahLX@|@V$N|+hhurQnk`N8&`WpZG9?l z`msz$nIC5gZ~513v{0sv`kdduM_ViV_Ky?ZB1B)>(wFXaLV5diKm^r9Y)3FQ;RPz^ zIZJ8C84iyWZ`xtX$g)PfNUN@fid4HxOFNEq6PFV72zrQh`ryfTETuZI|L{lds%C92 zIcUGVuHjqcuKS#2-l<$dWQ}}8+fS_?h{ZzMpBRt!{% zXP!oOq>s%Ro08lV-qc1v;=FBQIu#}I{@}4!CDwMBl&??(ex2o?BRf3(NFHNV7HTOC zH)}XPJfATgyp`$R*^g&mA`?@&n=~j!br)}RyJgmtSW?51*b6e|(@orOqomojo?&Q~ zm1kWv-4awF6LWv1D*KMwhQW%x1QKgjWh}GibzMzj@SdA7R5cC#l|ZXJe;qrEsUCx+ zGLhUZwaKx|UaD_R4~ls%M^dtPbygh?v+?Z3CM)?cDb4Y0#kSm$raYwd()tsgjs+vq zYi#lMr~J*t2j&AWtX$bK#31fcq(A}It;eU#l43BqYJa6TC^BywnMyfXp_+;hD-B!? z#Ll#vITiXwPEty!g6@YjaAgVadcf3-9hc{9Oiev|hjRFeYl%`;s$VAZRRgCdEU z#ZKH{2@^^d6x)Kk)S&%>GU!L^&RYfVAD9&`IT z5wedR3auzl=aQ_od}hnr>pOof5PHWepv%o5%k7d<-bnAA^^Qj;KVk*rVsTg=uMqb_ zJ0gRh6z;Gqa|m=5S{7TO6(uf^;~`JtjTEvMpIt_t=z!6ZZDbn52TxH zgL9qtQ!M1n;8pyWf4Y#FLT1Y~qrQ{QE0gn`sx!q~Z3)LNYdD5NgJqhsE(zO50<(Ne zPWO_M5La79oj2t$E1GfuneS+inybk0yB)u$v#@^C z6ba=?>O`hWf-Pmw-8U2ygN-nv&Sx0Aa!P@8Pb2PjhI&+S)dK_)mUYSY&jJbKH(WGKwiIfv3$-=6?5M} z-fi%mbHliqe!5fqynkp!WK?Ns!P=(=@Zo%bR%4b!IPGj-iety}e3RdmpJEZ~4+3uO zT!<%7zNnZIYQJfs7dtmws5xodBp>_!4;nE4iSK0j%)u{!TKv-G&x@NwO5EpB>V5Zq7JcS^mtgwz)_ z)`y_O6kRtC0H)V5YK0UQ2);{HVMXySPCVDl8Pi|5r>0$C?#P{Wz+n&0Lb+p%TBXrK z567-;u0}~Ga6cPe%aHVf=i%;Hxd%6uxmTSdS;{D_4pT^~$z#N^X1%tlzv^epwrISA z`x;I2zOM|E*HxnG96vOGH6_}GJEy-WYNFEl2-SmC-%KAEd*zhAlM^8HK9|iMxv?|R z3bGM-_QF*m=jRouMa~c{WcPmX8>5DSjdcQT7pg{{-l|m=Y8OM7EqF@r%TX_p#(F=y z(Qefa1wEN`QkhNaJ7$L+_KOT>mlvbf?1mqnotyaeGPcdgxqmbIRCCjgT7~l?W_6gQ z`B!ap;qk1O)Ww!xI%L7b-7+fh1w%_Ek&nIId@YPsV}q<&)6G$<_pyhwYnO}21!du_ zNcH&iE1$aB!>jjIoJXKz;{bA^XJf)cqrkRY3=v3dYfDFMs8CM6Z z7Plmjn{17sA#-LCm>vQYnL0cc`Bqpt4PL%S;;to=kz`^n{~-7Fln@$`%%C=mIH*!I z$5i7q6~`xe5xth4eRAX);&Y8oUs-njvD#sIBnJNTgyHyh5t9XKt(6C3obI_|F@e}| zv!+hQsfk-7mImytivz=O7Tcd8_M@P>+v}`=+*2}MCB%-4M$A4b{Jpzsi0{|yD>6sq@VAucv7!3gcfD!#M{3pXQ2%t|m+71}Z zaONL;ivj#c&q!}d{tt!&=nMcv*WYE(`Y+GO_1k;&Zw@2(Z=R8^WMl;W=INUOtLZ+# FzW`@zyzl@3 literal 0 HcmV?d00001 diff --git a/imageio/imageio-tiff/src/test/resources/tiff/planar-yuv420-jpeg-uncompressed.tif b/imageio/imageio-tiff/src/test/resources/tiff/planar-yuv420-jpeg-uncompressed.tif new file mode 100644 index 0000000000000000000000000000000000000000..af658e8210399e6c0003db03c164da44f903a1e8 GIT binary patch literal 24932 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 literal 0 HcmV?d00001 diff --git a/imageio/imageio-tiff/src/test/resources/tiff/planar-yuv422-bt601-uncompressed.tif b/imageio/imageio-tiff/src/test/resources/tiff/planar-yuv422-bt601-uncompressed.tif new file mode 100644 index 0000000000000000000000000000000000000000..00ec56cfa869c5a0f769b029f85065c6cf9b6f6f GIT binary patch literal 33124 zcmeHO?@Lor7(REF_$f5BLW_x%mc^PYCmSscS7@7M4Kil_;F^}VMlD)yvFci@S&MFJ z%(A-uoMf?(QW|9xtq?SjB#NX03Bu@$ANm72_wKgthkf(wvvGIMJ?Gtf&hG9#=Y5~& zeXFZUF(G_+W=^hl`Kscz>rERgDk`gY*6yii_Z@T}Yxj4Y3tjFCU%wrBxU8UPO^LB= z!=^2^njO398uzwwhmQJAo(Y`4)N}Qwc<({<$+Lt8Yt=T#?)rx2{jMYJY7LJDho8TC zo7B+M!W}s5Iesdj))0I7X7v3BwFd8r)4_{f-Mu&H91x>e**sn{tf&e{0I0SnL$&tS85Lu z{1^B?@SjN!GRfY8|NYOPgZ~EqkN5}m4vP2>#J?c^2k}pc|3ds5;{PNM3h|GK|3v&N z;(rnU{4atW@!yDlNBlqXACUin{1@c^NC+zOpOF8B{5RzPA^(x)bRqv!O2|e2EAoGl z|BU?a6#4IbbjmG`v%-jjk8XC0#%94AndHLpy3QfHuiwJ;>gqb(;#fk!N(ES<$icUT z}xL*6z);aD1v4#>e%?{fFld z>krmn>_6ClVSm8>g8d2m8~z9UFZiGEzkxphe*yjk{7nKVfjm`7ATK*CbO!t$H^(;A*4V5TQ%P~OU+n7%1$m#x z)yg^@+bhfCBg2COcg3&}3i!QluBD-NYI1z^b!;fwf2XfU2=ac9YxdXlw@(ve@sZfz zKtv38hXSeSe;(uG`s4n?^M~~Z>o4{n?7y%-V1L2>g#8Ww1O6BMPx#-!AAr9Ae**pn z{1Nyo@Mqxf;2*%hfPVu22L2KJEBNO*^Y0`>eiQmh%IVQ0m4uLdc}tjpKy%_U`Ky;163O-R}dAsSnvBqrMShjwHt-~FwWyi(4lJkO%Tq)9!}aP&j2 zOOs(Va-E*@8BChePKB6srJhdfS!5plnk2J;$x+(r8M8>KOL`V3WpbHZGG7{o$`>+u v$-0hNs>o|-=So%GKxhlert(HjmMkxgwNC1u~Y zvCdc`Yb0w~@6_%7?)~eY^Eu~vzR&l0&N-ihKmf!50M=)Z0DzBo#F^>n9!I?1)Rl!^ zJr|_~Xzc*=gP7x*9i-oD$#Vl^BVL0mZFe`j@SJ^ajRJiIqcS;3!3i6)( z2SHYSy3E13?StaNn-<9^(W(R7EgbUWoqI2p;Fp)8Q;i(=>x5Q+*_By;3v}ysMVY5g z@z;vu+M1tb-xvE?A1Gh&E^=nms_p^X*X#tO-xYjyN<;omd`v`_wp&}_zr#f3J-hI(sSVV020j~&^_oEMA>-S zls9q|y36C)|F*iKdXuT1Tn};U2B@>r-8tpdI?mJU(SK%kJiDhc zppx887zEo3!unc72U>r8@Bn|7A%|Sh*-F;Fs*TNU6~{w0XM}oqVf~;Elh8?uCN%de z=vE)5?4lf$TzkE2D^rK5on0{cjGmMbGXde}0sPX%_RU2R`2(WQ*=;(E;z1^Df7LS_ z0nTw~tnXahYBq7dD&HoN>wK**$snE^DO)9_qa_u0YNA`2*$mUhfzw5d{lb)W%y4oy z_|0~96fNbd-p)vMRr!}7wdI&mOoNhOijg4)I!D(uP&>fM@;Qf$m-!I4k9t3l+tdyr z-%Vnq?5+K}+c}J9K&tIaWU85$vk3T@5L6<=QWK>0 zByToRt(Gv)WjKRhN%!o2H+=;i|8$bfvY!4G)fZE%^Rs#-U4IOLEnE3OiK?_!+!)Iq ze~j&zLHLo5!+Vn#zPD^-5}L;S!us_EcDW*B3J@9Z z7>a_fvbteC$tT!>W#wqT22Ev!YfX;zO=#kMG&Ud=a$;`3=nguAE%)y@`IWV*Qkm5g zaM)B;vxnJtoi@q7kd=w4kP2%&R&~aAlDk_GghWT8!s2^7JgozEuCFN{yfd>#3im?C ztLL|z@ai&#^`JL3oZA-cx;9j?IWvuUD-2-4f6zwu7JN0V4_^OUGf{>z`gzWJ6@D2mxAWNTU$0r0p(pMCdOXo0pf-^MV7T#R}`_~|`q z%XI2mx_3=of%OQ{bJcDjPyl{Ekp``DAwbexL^JsH9)Axg&ikI6q{Rg1-Qk6Z(il3e z4nJ=D$;jZus>Al0I39Dyhgj+GW!>u^&MwH_@n#c>Zhz`>e!&kOwRHD1X!@1`(M4ve@=@x=$NC0i;e20@UU`(7Tl5lhWb(ihX}%c}z2m7;rnifOO~jyv zHD`NglE(wLQ(QlF;8+%V@QG}7%6)oOH=J8sQYs2f`a|Ma-X_l^>AOBgh_R^Vo03gR z|8Z1tiBS+5nSPik?>%S{BK_WjGqz$?+$3#A(@8%9=e`xuuc)lO;(t5aSHseHtW{^B zm@jKvku+rGaedC<=smj?rZ0V8W7Xy)9mn!se^Q<0m z!uG|PvsI2FpJCHkbmM{8IdijL#T zWj~?23h59Mh{y`b*gVUwbWQw`M)@V$PW3f?`fEbbxx+)r7U#`So=oH#-z--%onL52 zN_YOQ@BsOc>u6=SI2UBzg$^_JEmV#9fP36#xtitfhIm8VX?-yn+O>9HVy!Nq&29Qn zBTV9zP44Z!H`8%us$LXAQ}w6Q+03@#63Frgpvg{Q5eLR|ccVG={E|6WR3zG*-6O~~ z*Ah?Ta}9m+Ii(wT2T~kaq8eBPf>xRyhWjAGRZPaB>hEy1+Di-bzmm&lns@sO$0dl> zSp;S|9Ab?n4WVUMtd1Rf=z=H&mGGZL5&51R`|=dQ28+xyR?2q4kU;Uew41!v7^XDu z!pm*MIM|)~L5Hnso6oEkY#umea*+K+Vgrr_FpnI*vkJ6cRdS%WCkG0jk;U z3+07^`IgsHZ}JUIu<*O6#Uc4Q&igN>bL0%V-411Lyc?4~;F@yn_*SMy1!)0SE{i>j$V1&RP5wgQt>m!3|%0uaNyS zX#_egJZo)8Ix?|C^DyGk)97pIwG;lom?fA{@X?{tAPGW0B6P$(kh5pw!41N+%XQP| zI#MoOUmR+N4YiZ(qi1}B!!Y4RMQ_)>)qqcC{8VexY(mNZv}5g>muKpHY>x%PHWK_E z?5ag)_I+0{$gJE`LC$~Ls_)NuxkL1_?pRco_8s|IT%y@HsMcpXMW_DaW-SBn_aYV+ zb$rFgpEo|zt4FCzx9HG)y3is>x-n=iaJgYK6nd=nV%zJgmL5Lr%ICxp5n#uqmNNo3 zq@Nr1c3!L?iL^@`#fi1OFvw%4>?@@jLkq5#|R#^hr@YIRv^aQ zNGY*1E)ZqI)+b zpUDtCKB^jF8}RQ9YX|ua&d6zt6m*>3xEf93G>#JY7@Y>wh;$0ERmRNKCIqD~bKY}5 zUYO|9c(`CC8UAuCgVle2DdVY^n~P#GHgD0&GrnZ$;LxL>E`wl~;5w;!CN6jHt54b8 zF#W@FwM_Y{ci#>Yh{=wM<_n4LiW^9tk%_jdkE>G-r^)L_m-Xsb_X)K*YviB{O(U;_ z`5yQjHnHcpW(d#qt*J_m4Y)UeBr1Kcnv96_xezo@**wH(%z9gx&OsO>HNIR- z(_&-BMYYTs-*;EzI*ix0Pkgoj*XFvVErbk_b=o`v7~C^e%+LJ*=h_Z<9H?4wdU>yhnB8Hx$CF>=tXlD#A^?pT@t-svwd; zndMz&dj<)&JTi*DNOESjy_3iqaNZqh0HXNv3!G#!j%^{v8QtWd{pW#GdNrM!8=2&N zh+@z9cBMRblK`@K)?MsSvi~M=sBPVa{GhTs;5Er!ffCn##?rjk2NTL7CD327AA0i7 z^vLPQ$VNSfj;%)*KGf|hmN=x+D@mIe{ZL2do=yAMKVN^U(ZdzHU);}BKvQ3e2eC96 zt%WcuZ}zBEw6IocEwx_!ynhLQ3BtfplIZsBG^c;Hl|$=?AUkLC=8@33*3A(&zLvpn zQal|AuPg;VzSJM)(C6sVL zWUK7yf%o9Eu5_{OjBz7j@idCPu%j|rBhzxUitI3n1J-8Z-Y(fwfq$p`$yp73tWfw643NaSYy+VdLIx?pZmrq@Y;~v|mV0@)IG4Svpis zmV`op-RPo*Pk)V2^RxNjYGi^H3im6;wpRWs=1ywEJbWQ&-6{3nuW8OG=L=e-^m$7{ zbizp5$&(jd7IG!oi<81p?_4J%y$5G^B<+3OxSZT@4v;-u>7o*$HDY;OvbJE7>%_Er z$!Zk)?_#=SYUTGZ#?D;#snNkE&on|vfn!GgVQmvwPQUz%0i&_ql1d=YM_-7u2-dfz z(W%}=1*Dw1%+)24p#5ZU59h1IW`qwt`g`*C`b8Urx-Q@9;fl*h0qdfRz6 zKs7b!lTZEr1>z4i6lgx6QRK$Bml}v^DHOBxup`KJBXe#qLkhd;eku~O8-m3S4u*w` zq>frP^bh~sJI)^aeloyM3jmz`Gk+uC3@-q1-j<420025Q>dFQH0D9C#^FIichC-F3 z<&Rko^X!ZXa0g6;@huZi(erHer;-mk!W}{l^emj0gsM{Y7 zP^WU*Kb%^#oY2ty)fuQd<6q85<(B~T)EL#nN>9V_SEp7qm;dtL=Yam>xk$qWpq}vi zb*x~TtN-DGG~mB}>etYS{vQqnP!#}>>KCU`{a>B-(x2?_d(~;#{^+z+Ck-v|k4}9V Js|7U&_%8@~EJgqT literal 0 HcmV?d00001 diff --git a/imageio/imageio-tiff/src/test/resources/tiff/planar-yuv422-jpeg-uncompressed.tif b/imageio/imageio-tiff/src/test/resources/tiff/planar-yuv422-jpeg-uncompressed.tif new file mode 100644 index 0000000000000000000000000000000000000000..fa903cd2b0502b2245b200320d5cb9fb72c4e7b6 GIT binary patch literal 33124 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 literal 0 HcmV?d00001 diff --git a/imageio/imageio-tiff/src/test/resources/tiff/planar-yuv444-jpeg-uncompressed.tif b/imageio/imageio-tiff/src/test/resources/tiff/planar-yuv444-jpeg-uncompressed.tif new file mode 100644 index 0000000000000000000000000000000000000000..21d2084f2c06bf6c59c72d6672c373c26d8d96ec GIT binary patch literal 49508 zcmeI3eQZ-@6vp4%$pVQV>B7JiC5i?KMOL?gZq|UhC5%xM)S=8+aq4i4SjCUkK(t1b z%{X;HryV*%?xhRMmRudaZbjs#wTtA{@9Zp&nLTg z?b-K6=DgB57hksM@`|cuH7n~^-*S7?y)ChaHz%In*16-Q-dFqj-+piKV~G%28kPt* z+}zl>u6g4oiLmvV7n0pCr(b(BbKryQ;m=1FS5_^ntzRV(?rPZ}5uVuA-nk>SyH6q< z{P57BPrt~0_ro>U*GYui?p(idQ~c4#B|`U$uSkRg?+#=S4T()ONnso_{r11jhCE1F(aFh&c~)>v(d>6AGJuo{=xU> z_vio5`Oo^t`p@-`>p%Gq`5*Z&`9Jj!^&j;w^*{Uv{15yW{2%-${4e}B{6GB<`akr) z=>O6Gr2k9*oBlug2lOB4U(o-ce?tF-{tf*f`bYGi=wH$QqJKvJjs6||KmG^&ANXJJ z|KNYZ|Aqez{~!KG{Ga$=@&Dp~#{d0a@V_T|GDpV7V;$+i+^J~1tAA)TvR=o+YjkW; z?H%#${cTkfbQyHBLC1eTbNbXzCk&c1=tlpY(s}f7Aa*|A77j{fm~0=zp|SK>wstG4x+5l|%oA{tx{l z`cIY0qJLGXF#2DWN~3>PsW|#?WmVC?qyNYMfd50SGWcJVRmcB>{|WyW{x@Y+@&75S zivJP+C;nIbziO4n|E#P!{%>Vf|Be6MO}D#~XSZA)3EjHMhpUqVPad`{50#f!hn7@d zQE^cyB+u2ASL(|_=ZcOy=c}LCx+)p_MPAUGzIQyoP5k4rU!X;b(6`8w?dxPbpY6+W zcDh%T9~Y2UoZl~&eZM