TMI-TIFF: Implemented YCbCr reading.

This commit is contained in:
Harald Kuhr 2013-01-31 15:38:45 +01:00
parent 8c4f9d3ed6
commit 47fbf473db
4 changed files with 467 additions and 21 deletions

View File

@ -35,7 +35,8 @@ import java.io.IOException;
import java.io.InputStream;
/**
* LZWDecoder
* Implements Lempel-Ziv & Welch (LZW) decompression.
* Inspired by libTiff's LZW decompression.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
@ -67,8 +68,9 @@ abstract class LZWDecoder implements Decoder {
protected LZWDecoder(final boolean compatibilityMode) {
this.compatibilityMode = compatibilityMode;
table = new String[compatibilityMode ? 4096 + 1024 : 4096]; // libTiff adds another 1024 "for compatibility"...
table = new String[compatibilityMode ? 4096 + 1024 : 4096]; // libTiff adds 1024 "for compatibility"...
// First 258 entries of table is always fixed
for (int i = 0; i < 256; i++) {
table[i] = new String((byte) i);
}

View File

@ -35,6 +35,7 @@ import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.exif.EXIFReader;
import com.twelvemonkeys.imageio.metadata.exif.Rational;
import com.twelvemonkeys.imageio.metadata.exif.TIFF;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
@ -129,6 +130,9 @@ public class TIFFImageReader extends ImageReaderBase {
private final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.tiff.debug"));
// NOTE: DO NOT MODIFY OR EXPOSE!
static final double[] CCIR_601_1_COEFFICIENTS = new double[] {299.0 / 1000.0, 587.0 / 1000.0, 114.0 / 1000.0};
private CompoundDirectory IFDs;
private Directory currentIFD;
@ -217,7 +221,7 @@ public class TIFFImageReader extends ImageReaderBase {
getSampleFormat(); // We don't support anything but SAMPLEFORMAT_UINT at the moment, just sanity checking input
int planarConfiguration = getValueAsIntWithDefault(TIFF.TAG_PLANAR_CONFIGURATION, TIFFExtension.PLANARCONFIG_PLANAR);
int interpretation = getValueAsInt(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, "PhotometricInterpretation");
int samplesPerPixel = getValueAsIntWithDefault(TIFF.TAG_SAMPLES_PER_PIXELS, 1);
int samplesPerPixel = getValueAsIntWithDefault(TIFF.TAG_SAMPLES_PER_PIXEL, 1);
int bitsPerSample = getBitsPerSample();
int dataType = bitsPerSample <= 8 ? DataBuffer.TYPE_BYTE : bitsPerSample <= 16 ? DataBuffer.TYPE_USHORT : DataBuffer.TYPE_INT;
@ -254,6 +258,7 @@ public class TIFFImageReader extends ImageReaderBase {
case TIFFExtension.PHOTOMETRIC_YCBCR:
// JPEG reader will handle YCbCr to RGB for us, we'll have to do it ourselves if not JPEG...
// TODO: Sanity check that we have SamplesPerPixel == 3, BitsPerSample == [8,8,8] and Compression == 1 (none), 5 (LZW), or 6 (JPEG)
// TODO: Handle YCbCrSubsampling (up-scaler stream, or read data as-is + up-sample (sub-)raster after read? Apply smoothing?)
case TIFFBaseline.PHOTOMETRIC_RGB:
// RGB
@ -427,6 +432,10 @@ public class TIFFImageReader extends ImageReaderBase {
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
readIFD(imageIndex);
System.err.println("currentIFD.getEntryById(TIFF.TAG_REFERENCE_BLACK_WHITE): " + currentIFD.getEntryById(TIFF.TAG_REFERENCE_BLACK_WHITE));
System.err.println("currentIFD.getEntryById(TIFF.TAG_TRANSFER_FUNCTION): " + currentIFD.getEntryById(TIFF.TAG_TRANSFER_FUNCTION));
System.err.println("currentIFD.getEntryById(TIFF.TAG_TRANSFER_RANGE): " + currentIFD.getEntryById(TIFF.TAG_TRANSFER_RANGE));
int width = getWidth(imageIndex);
int height = getHeight(imageIndex);
@ -496,8 +505,53 @@ public class TIFFImageReader extends ImageReaderBase {
case TIFFExtension.COMPRESSION_ZLIB:
// 'Adobe-style' Deflate
// TODO: Read only tiles that lies within region
int[] yCbCrSubsampling = null;
int yCbCrPos = 1;
double[] yCbCrCoefficients = null;
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
// getRawImageType does the lookup/conversion for these
if (raster.getNumBands() != 3) {
throw new IIOException("TIFF PhotometricInterpreatation YCbCr requires SamplesPerPixel == 3: " + raster.getNumBands());
}
if (raster.getTransferType() != DataBuffer.TYPE_BYTE) {
throw new IIOException("TIFF PhotometricInterpreatation YCbCr requires BitsPerSample == [8,8,8]");
}
yCbCrPos = getValueAsIntWithDefault(TIFF.TAG_YCBCR_POSITIONING, 1);
Entry subSampling = currentIFD.getEntryById(TIFF.TAG_YCBCR_SUB_SAMPLING);
if (subSampling != null) {
try {
yCbCrSubsampling = (int[]) subSampling.getValue();
}
catch (ClassCastException e) {
throw new IIOException("Unknown TIFF YCbCrSubSampling value type: " + subSampling.getTypeName(), e);
}
if (yCbCrSubsampling.length != 2 ||
yCbCrSubsampling[0] != 1 && yCbCrSubsampling[0] != 2 && yCbCrSubsampling[0] != 4 ||
yCbCrSubsampling[1] != 1 && yCbCrSubsampling[1] != 2 && yCbCrSubsampling[1] != 4 ||
yCbCrSubsampling[0] < yCbCrSubsampling[1]) {
throw new IIOException("Bad TIFF YCbCrSubSampling value: " + Arrays.toString(yCbCrSubsampling));
}
}
else {
yCbCrSubsampling = new int[] {2, 2};
}
Entry coefficients = currentIFD.getEntryById(TIFF.TAG_YCBCR_COEFFICIENTS);
if (coefficients != null) {
Rational[] value = (Rational[]) coefficients.getValue();
yCbCrCoefficients = new double[] {value[0].doubleValue(), value[1].doubleValue(), value[2].doubleValue()};
}
else {
// Default to y CCIR Recommendation 601-1 values
yCbCrCoefficients = CCIR_601_1_COEFFICIENTS;
}
}
// TODO: Read only tiles that lies within region
// General uncompressed/compressed reading
for (int y = 0; y < tilesDown; y++) {
int col = 0;
@ -510,7 +564,8 @@ public class TIFFImageReader extends ImageReaderBase {
imageInput.seek(stripTileOffsets[i]);
DataInput input;
if (compression == TIFFBaseline.COMPRESSION_NONE) {
if (compression == TIFFBaseline.COMPRESSION_NONE && interpretation != TIFFExtension.PHOTOMETRIC_YCBCR) {
// No need for transformation, fast forward
input = imageInput;
}
else {
@ -518,11 +573,16 @@ public class TIFFImageReader extends ImageReaderBase {
? IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts[i])
: IIOUtil.createStreamAdapter(imageInput);
adapter = createDecoderInputStream(compression, adapter);
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
adapter = new YCbCrUpsamplerStream(adapter, yCbCrSubsampling, colsInTile, yCbCrCoefficients);
}
// According to the spec, short/long/etc should follow order of containing stream
input = imageInput.getByteOrder() == ByteOrder.BIG_ENDIAN
? new DataInputStream(createDecoderInputStream(compression, adapter))
: new LittleEndianDataInputStream(createDecoderInputStream(compression, adapter));
? new DataInputStream(adapter)
: new LittleEndianDataInputStream(adapter);
}
// Read a full strip/tile
@ -566,6 +626,9 @@ public class TIFFImageReader extends ImageReaderBase {
// Might have something to do with subsampling?
// How do we pass the chroma-subsampling parameter from the TIFF structure to the JPEG reader?
// TODO: Consider splicing the TAG_JPEG_TABLES into the streams for each tile, for a more
// compatible approach..?
jpegReader.setInput(new ByteArrayImageInputStream(tablesValue));
// NOTE: This initializes the tables AND MORE secret internal settings for the reader (as if by magic).
@ -690,6 +753,7 @@ public class TIFFImageReader extends ImageReaderBase {
switch (rowRaster.getTransferType()) {
case DataBuffer.TYPE_BYTE:
byte[] rowData = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
for (int j = 0; j < rowsInStrip; j++) {
int row = startRow + j;
@ -699,17 +763,6 @@ public class TIFFImageReader extends ImageReaderBase {
input.readFully(rowData);
// for (int k = 0; k < rowData.length; k++) {
// try {
// rowData[k] = input.readByte();
// }
// catch (IOException e) {
// Arrays.fill(rowData, k, rowData.length, (byte) -1);
// System.err.printf("Unexpected EOF or bad data at [%d, %d]\n", col + k, row);
// break;
// }
// }
unPredict(predictor, colsInStrip, 1, numBands, rowData);
normalizeBlack(interpretation, rowData);
@ -725,6 +778,7 @@ public class TIFFImageReader extends ImageReaderBase {
break;
case DataBuffer.TYPE_USHORT:
short [] rowDataShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
for (int j = 0; j < rowsInStrip; j++) {
int row = startRow + j;
@ -751,6 +805,7 @@ public class TIFFImageReader extends ImageReaderBase {
break;
case DataBuffer.TYPE_INT:
int [] rowDataInt = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
for (int j = 0; j < rowsInStrip; j++) {
int row = startRow + j;
@ -861,6 +916,8 @@ public class TIFFImageReader extends ImageReaderBase {
private InputStream createDecoderInputStream(final int compression, final InputStream stream) throws IOException {
switch (compression) {
case TIFFBaseline.COMPRESSION_NONE:
return stream;
case TIFFBaseline.COMPRESSION_PACKBITS:
return new DecoderStream(stream, new PackBitsDecoder(), 1024);
case TIFFExtension.COMPRESSION_LZW:
@ -894,7 +951,7 @@ public class TIFFImageReader extends ImageReaderBase {
short[] shorts = (short[]) entry.getValue();
value = new long[shorts.length];
for (int i = 0, stripOffsetsValueLength = value.length; i < stripOffsetsValueLength; i++) {
for (int i = 0, length = value.length; i < length; i++) {
value[i] = shorts[i];
}
}
@ -902,7 +959,7 @@ public class TIFFImageReader extends ImageReaderBase {
int[] ints = (int[]) entry.getValue();
value = new long[ints.length];
for (int i = 0, stripOffsetsValueLength = value.length; i < stripOffsetsValueLength; i++) {
for (int i = 0, length = value.length; i < length; i++) {
value[i] = ints[i];
}
}

View File

@ -0,0 +1,336 @@
/*
* Copyright (c) 2013, 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 "TwelveMonkeys" 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 OWNER 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 java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* YCbCrUpsamplerStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: YCbCrUpsamplerStream.java,v 1.0 31.01.13 09:25 haraldk Exp$
*/
final class YCbCrUpsamplerStream extends FilterInputStream {
static final boolean DEBUG = false;
private final int horizChromaSub;
private final int vertChromaSub;
private final double[] coefficients;
private final int units;
private final int unitSize;
private final byte[] decodedRows;
int decodedLength;
int decodedPos;
private final byte[] buffer;
int bufferLength;
int bufferPos;
public YCbCrUpsamplerStream(InputStream stream, int[] chromaSub, int cols, double[] coefficients) {
super(stream);
this.horizChromaSub = chromaSub[0];
this.vertChromaSub = chromaSub[1];
this.coefficients = Arrays.equals(TIFFImageReader.CCIR_601_1_COEFFICIENTS, coefficients) ? null : coefficients;
// In TIFF, subsampled streams are stored in "units" of horiz * vert pixels.
// For a 4:2 subsampled stream like this:
//
// Y0 Y1 Y2 Y3 Cb0 Cr0 Y8 Y9 Y10 Y11 Cb1 Cr1
// Y4 Y5 Y6 Y7 Y12Y13Y14 Y15
//
// In the stream, the order is: Y0,Y1,Y2..Y7,Cb0,Cr0, Y8...Y15,Cb1,Cr1, Y16...
units = cols / horizChromaSub;
unitSize = horizChromaSub * vertChromaSub + 2;
decodedRows = new byte[cols * vertChromaSub * 3];
buffer = new byte[unitSize * units];
}
@Override
public int read() throws IOException {
if (decodedLength < 0) {
return -1;
}
if (decodedPos >= decodedLength) {
fetch();
if (decodedLength < 0) {
return -1;
}
}
return decodedRows[decodedPos++];
}
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 = super.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;
int rowOff = horizChromaSub * units;
for (int u = 0; u < units; u++) {
if (bufferPos >= bufferLength) {
throw new EOFException("Unexpected end of stream");
}
// Decode one unit
byte cb = buffer[bufferPos + unitSize - 2];
byte cr = buffer[bufferPos + unitSize - 1];
for (int y = 0; y < vertChromaSub; y++) {
for (int x = 0; x < horizChromaSub; x++) {
int pixelOff = 3 * (rowOff * y + horizChromaSub * u + x);
decodedRows[pixelOff] = buffer[bufferPos++];
decodedRows[pixelOff + 1] = cb;
decodedRows[pixelOff + 2] = cr;
// Convert to RGB
if (coefficients == null) {
YCbCrConverter.convertYCbCr2RGB(decodedRows, decodedRows, pixelOff);
}
else {
convertYCbCr2RGB(decodedRows, decodedRows, coefficients, pixelOff);
}
}
}
bufferPos+= 2;
}
bufferPos = bufferLength;
decodedPos = 0;
}
@Override
public final int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@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");
}
private void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, final int offset) {
// TODO: FixMe: This is bogus...
double y = yCbCr[offset ] & 0xff;
double cb = yCbCr[offset + 1] & 0xff;
double cr = yCbCr[offset + 2] & 0xff;
double lumaRed = coefficients[0];
double lumaGreen = coefficients[1];
double lumaBlue = coefficients[2];
rgb[offset ] = clamp((int) Math.round(cr * (2 - 2 * lumaRed) + y));
rgb[offset + 2] = clamp((int) Math.round(cb * (2 - 2 * lumaBlue) + y) - 128);
rgb[offset + 1] = clamp((int) Math.round((y - lumaRed * (rgb[offset] & 0xff) - lumaBlue * (rgb[offset + 2] & 0xff)) / lumaGreen));
}
private static byte clamp(int val) {
return (byte) Math.max(0, Math.min(255, val));
}
// TODO: This code is copied from JPEG package, make it "more" public: com.tm.imageio.color package?
/**
* Static inner class for lazy-loading of conversion tables.
*/
static final class YCbCrConverter {
/** Define tables for YCC->RGB color space conversion. */
private final static int SCALEBITS = 16;
private final static int MAXJSAMPLE = 255;
private final static int CENTERJSAMPLE = 128;
private final static int ONE_HALF = 1 << (SCALEBITS - 1);
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
/**
* Initializes tables for YCC->RGB color space conversion.
*/
private static void buildYCCtoRGBtable() {
if (DEBUG) {
System.err.println("Building YCC conversion table");
}
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
// Cr=>R value is nearest int to 1.40200 * x
Cr_R_LUT[i] = (int) ((1.40200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
// Cb=>B value is nearest int to 1.77200 * x
Cb_B_LUT[i] = (int) ((1.77200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
// Cr=>G value is scaled-up -0.71414 * x
Cr_G_LUT[i] = -(int) (0.71414 * (1 << SCALEBITS) + 0.5) * x;
// Cb=>G value is scaled-up -0.34414 * x
// We also add in ONE_HALF so that need not do it in inner loop
Cb_G_LUT[i] = -(int) ((0.34414) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
}
}
static {
buildYCCtoRGBtable();
}
static void convertYCbCr2RGB(final Raster raster) {
final int height = raster.getHeight();
final int width = raster.getWidth();
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
convertYCbCr2RGB(data, data, (x + y * width) * 3);
}
}
}
static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
int y = yCbCr[offset ] & 0xff;
int cr = yCbCr[offset + 2] & 0xff;
int cb = yCbCr[offset + 1] & 0xff;
rgb[offset ] = clamp(y + Cr_R_LUT[cr]);
rgb[offset + 1] = clamp(y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS));
rgb[offset + 2] = clamp(y + Cb_B_LUT[cb]);
}
static void convertYCCK2CMYK(final Raster raster) {
final int height = raster.getHeight();
final int width = raster.getWidth();
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
convertYCCK2CMYK(data, data, (x + y * width) * 4);
}
}
}
private static void convertYCCK2CMYK(byte[] ycck, byte[] cmyk, int offset) {
// Inverted
int y = 255 - ycck[offset ] & 0xff;
int cb = 255 - ycck[offset + 1] & 0xff;
int cr = 255 - ycck[offset + 2] & 0xff;
int k = 255 - ycck[offset + 3] & 0xff;
int cmykC = MAXJSAMPLE - (y + Cr_R_LUT[cr]);
int cmykM = MAXJSAMPLE - (y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS));
int cmykY = MAXJSAMPLE - (y + Cb_B_LUT[cb]);
cmyk[offset ] = clamp(cmykC);
cmyk[offset + 1] = clamp(cmykM);
cmyk[offset + 2] = clamp(cmykY);
cmyk[offset + 3] = (byte) k; // K passes through unchanged
}
// private static byte clamp(int val) {
// return (byte) Math.max(0, Math.min(255, val));
// }
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2013, 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 "TwelveMonkeys" 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 OWNER 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.io.InputStreamAbstractTestCase;
import org.junit.Ignore;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* YCbCrUpsamplerStreamTest
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: YCbCrUpsamplerStreamTest.java,v 1.0 31.01.13 14:35 haraldk Exp$
*/
@Ignore
public class YCbCrUpsamplerStreamTest extends InputStreamAbstractTestCase {
// TODO: Implement
@Override
protected InputStream makeInputStream(byte[] pBytes) {
return new YCbCrUpsamplerStream(new ByteArrayInputStream(pBytes), new int[] {2, 2}, pBytes.length / 4, null);
}
}