mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 19:15:29 -04:00
TMI-TIFF: Fixed bug in YCbCr reading. Implemented "old-style" JPEG reading for two test images. More work needed.
This commit is contained in:
parent
fcd15a9e36
commit
c394f8a4bc
@ -163,4 +163,10 @@ public interface TIFF {
|
|||||||
int TAG_TILE_BYTE_COUNTS = 325;
|
int TAG_TILE_BYTE_COUNTS = 325;
|
||||||
|
|
||||||
int TAG_JPEG_TABLES = 347;
|
int TAG_JPEG_TABLES = 347;
|
||||||
|
|
||||||
|
// "Old-style" JPEG (Obsolete)
|
||||||
|
int TAG_JPEG_PROC = 512;
|
||||||
|
int TAG_JPEG_QTABLES = 519;
|
||||||
|
int TAG_JPEG_DCTABLES = 520;
|
||||||
|
int TAG_JPEG_ACTABLES = 521;
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ class JPEGTables {
|
|||||||
|
|
||||||
// Read lengths as short array
|
// Read lengths as short array
|
||||||
short[] lengths = new short[DHT_LENGTH];
|
short[] lengths = new short[DHT_LENGTH];
|
||||||
for (int i = 0, lengthsLength = lengths.length; i < lengthsLength; i++) {
|
for (int i = 0; i < DHT_LENGTH; i++) {
|
||||||
lengths[i] = (short) data.readUnsignedByte();
|
lengths[i] = (short) data.readUnsignedByte();
|
||||||
}
|
}
|
||||||
read += lengths.length;
|
read += lengths.length;
|
||||||
|
@ -65,4 +65,8 @@ interface TIFFExtension {
|
|||||||
int SAMPLEFORMAT_INT = 2;
|
int SAMPLEFORMAT_INT = 2;
|
||||||
int SAMPLEFORMAT_FP = 3;
|
int SAMPLEFORMAT_FP = 3;
|
||||||
int SAMPLEFORMAT_UNDEFINED = 4;
|
int SAMPLEFORMAT_UNDEFINED = 4;
|
||||||
|
|
||||||
|
// "Old-style" JPEG (obsolete)
|
||||||
|
int JPEG_PROC_BASELINE = 1;
|
||||||
|
int JPEG_PROC_LOSSLESS = 14;
|
||||||
}
|
}
|
||||||
|
@ -59,9 +59,7 @@ import java.awt.color.ICC_Profile;
|
|||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.Inflater;
|
import java.util.zip.Inflater;
|
||||||
import java.util.zip.InflaterInputStream;
|
import java.util.zip.InflaterInputStream;
|
||||||
@ -485,9 +483,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
WritableRaster rowRaster = rawType.getColorModel().createCompatibleWritableRaster(stripTileWidth, 1);
|
WritableRaster rowRaster = rawType.getColorModel().createCompatibleWritableRaster(stripTileWidth, 1);
|
||||||
int row = 0;
|
int row = 0;
|
||||||
|
|
||||||
// Read data
|
|
||||||
processImageStarted(imageIndex);
|
|
||||||
|
|
||||||
switch (compression) {
|
switch (compression) {
|
||||||
// TIFF Baseline
|
// TIFF Baseline
|
||||||
case TIFFBaseline.COMPRESSION_NONE:
|
case TIFFBaseline.COMPRESSION_NONE:
|
||||||
@ -547,6 +542,9 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read data
|
||||||
|
processImageStarted(imageIndex);
|
||||||
|
|
||||||
// TODO: Read only tiles that lies within region
|
// TODO: Read only tiles that lies within region
|
||||||
// General uncompressed/compressed reading
|
// General uncompressed/compressed reading
|
||||||
for (int y = 0; y < tilesDown; y++) {
|
for (int y = 0; y < tilesDown; y++) {
|
||||||
@ -606,6 +604,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
case TIFFExtension.COMPRESSION_JPEG:
|
case TIFFExtension.COMPRESSION_JPEG:
|
||||||
// JPEG ('new-style' JPEG)
|
// JPEG ('new-style' JPEG)
|
||||||
// TODO: Refactor all JPEG reading out to separate JPEG support class?
|
// TODO: Refactor all JPEG reading out to separate JPEG support class?
|
||||||
|
// TODO: Cache the JPEG reader for later use? Remember to reset to avoid resource leaks
|
||||||
|
|
||||||
// TIFF is strictly ISO JPEG, so we should probably stick to the standard reader
|
// TIFF is strictly ISO JPEG, so we should probably stick to the standard reader
|
||||||
ImageReader jpegReader = new JPEGImageReader(getOriginatingProvider());
|
ImageReader jpegReader = new JPEGImageReader(getOriginatingProvider());
|
||||||
@ -680,6 +679,9 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
// ...and the JPEG reader will probably choke on missing tables...
|
// ...and the JPEG reader will probably choke on missing tables...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read data
|
||||||
|
processImageStarted(imageIndex);
|
||||||
|
|
||||||
for (int y = 0; y < tilesDown; y++) {
|
for (int y = 0; y < tilesDown; y++) {
|
||||||
int col = 0;
|
int col = 0;
|
||||||
int rowsInTile = Math.min(stripTileHeight, height - row);
|
int rowsInTile = Math.min(stripTileHeight, height - row);
|
||||||
@ -689,14 +691,14 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
int colsInTile = Math.min(stripTileWidth, width - col);
|
int colsInTile = Math.min(stripTileWidth, width - col);
|
||||||
|
|
||||||
imageInput.seek(stripTileOffsets[i]);
|
imageInput.seek(stripTileOffsets[i]);
|
||||||
SubImageInputStream subStream = new SubImageInputStream(imageInput, stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE);
|
ImageInputStream subStream = new SubImageInputStream(imageInput, stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE);
|
||||||
try {
|
try {
|
||||||
jpegReader.setInput(subStream);
|
jpegReader.setInput(subStream);
|
||||||
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
||||||
jpegParam.setDestinationOffset(new Point(col, row));
|
jpegParam.setDestinationOffset(new Point(col, row));
|
||||||
jpegParam.setDestination(destination);
|
jpegParam.setDestination(destination);
|
||||||
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
|
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
|
||||||
// In the latter case we will have to use readAsRaster
|
// In the latter case we will have to use readAsRaster and do color conversion ourselves
|
||||||
jpegReader.read(0, jpegParam);
|
jpegReader.read(0, jpegParam);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@ -722,6 +724,240 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TIFFExtension.COMPRESSION_OLD_JPEG:
|
||||||
|
// JPEG ('old-style' JPEG, later overridden in Technote2)
|
||||||
|
|
||||||
|
// http://www.remotesensing.org/libtiff/TIFFTechNote2.html
|
||||||
|
// TODO: Issue warning?
|
||||||
|
|
||||||
|
int mode = getValueAsIntWithDefault(TIFF.TAG_JPEG_PROC, 1);
|
||||||
|
if (mode == TIFFExtension.JPEG_PROC_LOSSLESS) {
|
||||||
|
throw new IIOException("Unsupported TIFF JPEGProcessingMode: Lossless (14)");
|
||||||
|
}
|
||||||
|
else if (mode != TIFFExtension.JPEG_PROC_BASELINE) {
|
||||||
|
throw new IIOException("Unknown TIFF JPEGProcessingMode value: " + mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// May use normal tiling??
|
||||||
|
|
||||||
|
// 512/JPEGProc: 1=Baseline, 14=Lossless (with Huffman coding), no default, although 1 is assumed if absent
|
||||||
|
// 513/JPEGInterchangeFormat (may be absent...)
|
||||||
|
// 514/JPEGInterchangeFormatLength (may be absent...)
|
||||||
|
// 515/JPEGRestartInterval (may be absent)
|
||||||
|
|
||||||
|
// 517/JPEGLosslessPredictors
|
||||||
|
// 518/JPEGPointTransforms
|
||||||
|
|
||||||
|
// 519/JPEGQTables
|
||||||
|
// 520/JPEGDCTables
|
||||||
|
// 521/JPEGACTables
|
||||||
|
|
||||||
|
// This field was originally intended to point to a list of offsets to the quantization tables, one per
|
||||||
|
// component. Each table consists of 64 BYTES (one for each DCT coefficient in the 8x8 block). The
|
||||||
|
// quantization tables are stored in zigzag order, and are compatible with the quantization tables
|
||||||
|
// usually found in a JPEG stream DQT marker.
|
||||||
|
|
||||||
|
// The original specification strongly recommended that, within the TIFF file, each component be
|
||||||
|
// assigned separate tables, and labelled this field as mandatory whenever the JPEGProc field specifies
|
||||||
|
// a DCT-based process.
|
||||||
|
|
||||||
|
// We've seen old-style JPEG in TIFF files where some or all Table offsets, contained the JPEGQTables,
|
||||||
|
// JPEGDCTables, and JPEGACTables tags are incorrect values beyond EOF. However, these files do always
|
||||||
|
// seem to contain a useful JPEGInterchangeFormat tag. Therefore, we recommend a careful attempt to read
|
||||||
|
// the Tables tags only as a last resort, if no table data is found in a JPEGInterchangeFormat stream.
|
||||||
|
|
||||||
|
|
||||||
|
// TIFF is strictly ISO JPEG, so we should probably stick to the standard reader
|
||||||
|
jpegReader = new JPEGImageReader(getOriginatingProvider());
|
||||||
|
jpegParam = (JPEGImageReadParam) jpegReader.getDefaultReadParam();
|
||||||
|
|
||||||
|
int jpegOffset = getValueAsIntWithDefault(TIFF.TAG_JPEG_INTERCHANGE_FORMAT, -1);
|
||||||
|
int jpegLenght = getValueAsIntWithDefault(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, -1);
|
||||||
|
|
||||||
|
ImageInputStream subStream;
|
||||||
|
|
||||||
|
if (jpegOffset != -1) {
|
||||||
|
// Straight forward case: We're good to go! We'll disregard tiling and any tables tags
|
||||||
|
|
||||||
|
imageInput.seek(jpegOffset);
|
||||||
|
subStream = new SubImageInputStream(imageInput, jpegLenght != -1 ? jpegLenght : Short.MAX_VALUE);
|
||||||
|
jpegReader.setInput(subStream);
|
||||||
|
|
||||||
|
// Read data
|
||||||
|
processImageStarted(imageIndex);
|
||||||
|
|
||||||
|
try {
|
||||||
|
jpegParam.setSourceRegion(new Rectangle(0, 0, width, height));
|
||||||
|
jpegParam.setDestination(destination);
|
||||||
|
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
|
||||||
|
// In the latter case we will have to use readAsRaster and do color conversion ourselves
|
||||||
|
jpegReader.read(0, jpegParam);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
subStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
processImageProgress(100f * row / (float) height);
|
||||||
|
|
||||||
|
if (abortRequested()) {
|
||||||
|
processReadAborted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The hard way: Read tables and re-create a full JFIF stream
|
||||||
|
|
||||||
|
// TODO: If any of the q/dc/ac tables are equal (or have same offset, even if "spec" violation),
|
||||||
|
// use only the first occurrence, and update selectors in SOF0 and SOS
|
||||||
|
|
||||||
|
long[] qTablesOffsets = getValueAsLongArray(TIFF.TAG_JPEG_QTABLES, "JPEGQTables", true);
|
||||||
|
byte[][] qTables = new byte[3][(int) (qTablesOffsets[1] - qTablesOffsets[0])]; // TODO: Using the offsets seems fragile.. Use fixed length??
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
imageInput.seek(qTablesOffsets[j]);
|
||||||
|
imageInput.readFully(qTables[j]);
|
||||||
|
}
|
||||||
|
// System.err.println("qTables: " + qTables[0].length);
|
||||||
|
|
||||||
|
long[] dcTablesOffsets = getValueAsLongArray(TIFF.TAG_JPEG_DCTABLES, "JPEGDCTables", true);
|
||||||
|
byte[][] dcTables = new byte[3][(int) (dcTablesOffsets[1] - dcTablesOffsets[0])];
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
imageInput.seek(dcTablesOffsets[j]);
|
||||||
|
imageInput.readFully(dcTables[j]);
|
||||||
|
}
|
||||||
|
// System.err.println("dcTables: " + dcTables[0].length);
|
||||||
|
|
||||||
|
long[] acTablesOffsets = getValueAsLongArray(TIFF.TAG_JPEG_ACTABLES, "JPEGACTables", true);
|
||||||
|
byte[][] acTables = new byte[3][(int) (acTablesOffsets[1] - acTablesOffsets[0])];
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
imageInput.seek(acTablesOffsets[j]);
|
||||||
|
imageInput.readFully(acTables[j]);
|
||||||
|
}
|
||||||
|
// System.err.println("acTables: " + acTables[0].length);
|
||||||
|
|
||||||
|
// Read data
|
||||||
|
processImageStarted(imageIndex);
|
||||||
|
|
||||||
|
for (int y = 0; y < tilesDown; y++) {
|
||||||
|
int col = 0;
|
||||||
|
int rowsInTile = Math.min(stripTileHeight, height - row);
|
||||||
|
|
||||||
|
for (int x = 0; x < tilesAcross; x++) {
|
||||||
|
int colsInTile = Math.min(stripTileWidth, width - col);
|
||||||
|
int i = y * tilesAcross + x;
|
||||||
|
|
||||||
|
imageInput.seek(stripTileOffsets[i]);
|
||||||
|
subStream = ImageIO.createImageInputStream(new SequenceInputStream(Collections.enumeration(
|
||||||
|
Arrays.asList(
|
||||||
|
// TODO; Get rid of hardcoded data + extract method/class...
|
||||||
|
// TODO:
|
||||||
|
// - Create a BAIS with size large enough to keep JFIF structure incl tables and SOS,
|
||||||
|
// - Wrap in DataInput,
|
||||||
|
// - Insert width/height, component ids etc at correct place
|
||||||
|
// - Insert tables at correct place
|
||||||
|
|
||||||
|
new ByteArrayInputStream(new byte[] {(byte) 0xff, (byte) 0xd8, // SOI
|
||||||
|
// SOF0 (short), length (short)
|
||||||
|
(byte) 0xff, (byte) 0xc0, 0x00, 0x11, // SOF0, 17 bytes
|
||||||
|
// bits (byte), width (short), height (short)
|
||||||
|
0x08, 0x00, (byte) 0xe0, 0x00, (byte) 0xf0,
|
||||||
|
// num comp (byte), (id (byte) h/vsub (byte), qtsel (byte) * num comp)
|
||||||
|
0x03, 0x00, 0x22, 0x00, 0x01, 0x11, 0x01, 0x02, 0x11, 0x02,
|
||||||
|
// 0x03, 0x00, 0x22, 0x00, 0x01, 0x11, 0x01, 0x02, 0x11, 0x01,
|
||||||
|
// DQT
|
||||||
|
(byte) 0xff, (byte) 0xdb, 0x00, 0x43, 0x00,
|
||||||
|
}),
|
||||||
|
// ... table data
|
||||||
|
new ByteArrayInputStream(qTables[0]),
|
||||||
|
new ByteArrayInputStream(new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xdb, 0x00, 0x43, 0x01,
|
||||||
|
}),
|
||||||
|
// ... table data
|
||||||
|
new ByteArrayInputStream(qTables[1]),
|
||||||
|
new ByteArrayInputStream(new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xdb, 0x00, 0x43, 0x02,
|
||||||
|
}),
|
||||||
|
// ... table data
|
||||||
|
new ByteArrayInputStream(qTables[2]),
|
||||||
|
|
||||||
|
// DHT (DC)
|
||||||
|
new ByteArrayInputStream(new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xc4, 0x00, 0x1f, 0x00,
|
||||||
|
}),
|
||||||
|
// ... table data
|
||||||
|
new ByteArrayInputStream(dcTables[0]),
|
||||||
|
new ByteArrayInputStream(new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xc4, 0x00, 0x1f, 0x01,
|
||||||
|
}),
|
||||||
|
// ... table data
|
||||||
|
new ByteArrayInputStream(dcTables[1]),
|
||||||
|
new ByteArrayInputStream(new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xc4, 0x00, 0x1f, 0x02,
|
||||||
|
}),
|
||||||
|
// ... table data
|
||||||
|
new ByteArrayInputStream(dcTables[2]),
|
||||||
|
|
||||||
|
// DHT (AC)
|
||||||
|
new ByteArrayInputStream(new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xc4, 0x00, (byte) 0xb5, 0x10,
|
||||||
|
}),
|
||||||
|
// ... table data
|
||||||
|
new ByteArrayInputStream(acTables[0]),
|
||||||
|
new ByteArrayInputStream(new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xc4, 0x00, (byte) 0xb5, 0x11,
|
||||||
|
}),
|
||||||
|
// ... table data
|
||||||
|
new ByteArrayInputStream(acTables[1]),
|
||||||
|
new ByteArrayInputStream(new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xc4, 0x00, (byte) 0xb5, 0x12,
|
||||||
|
}),
|
||||||
|
// ... table data
|
||||||
|
new ByteArrayInputStream(acTables[2]),
|
||||||
|
|
||||||
|
new ByteArrayInputStream(new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xda, // SOS
|
||||||
|
// TODO: Figure out what the last 3 bytes are...
|
||||||
|
// Length: 12 (short), num comp (byte), (id (byte), dc/ac sel (byte) * num comp), ?? byte, ?? byte ?? byte
|
||||||
|
0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x00, 0x00
|
||||||
|
// 0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02, 0x12, 0x00, 0x63, 0x00
|
||||||
|
}),
|
||||||
|
IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE),
|
||||||
|
new ByteArrayInputStream(new byte[] {(byte) 0xff, (byte) 0xd9}) // EOI
|
||||||
|
)
|
||||||
|
)));
|
||||||
|
|
||||||
|
jpegReader.setInput(subStream);
|
||||||
|
|
||||||
|
try {
|
||||||
|
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
||||||
|
jpegParam.setDestinationOffset(new Point(col, row));
|
||||||
|
jpegParam.setDestination(destination);
|
||||||
|
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
|
||||||
|
// In the latter case we will have to use readAsRaster and do color conversion ourselves
|
||||||
|
jpegReader.read(0, jpegParam);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
subStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abortRequested()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
col += colsInTile;
|
||||||
|
}
|
||||||
|
|
||||||
|
processImageProgress(100f * row / (float) height);
|
||||||
|
|
||||||
|
if (abortRequested()) {
|
||||||
|
processReadAborted();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
row += rowsInTile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case TIFFBaseline.COMPRESSION_CCITT_HUFFMAN:
|
case TIFFBaseline.COMPRESSION_CCITT_HUFFMAN:
|
||||||
// CCITT modified Huffman
|
// CCITT modified Huffman
|
||||||
// Additionally, the specification defines these values as part of the TIFF extensions:
|
// Additionally, the specification defines these values as part of the TIFF extensions:
|
||||||
@ -729,8 +965,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
// CCITT Group 3 fax encoding
|
// CCITT Group 3 fax encoding
|
||||||
case TIFFExtension.COMPRESSION_CCITT_T6:
|
case TIFFExtension.COMPRESSION_CCITT_T6:
|
||||||
// CCITT Group 4 fax encoding
|
// CCITT Group 4 fax encoding
|
||||||
case TIFFExtension.COMPRESSION_OLD_JPEG:
|
|
||||||
// JPEG ('old-style' JPEG, later overridden in Technote2)
|
|
||||||
|
|
||||||
throw new IIOException("Unsupported TIFF Compression value: " + compression);
|
throw new IIOException("Unsupported TIFF Compression value: " + compression);
|
||||||
default:
|
default:
|
||||||
|
@ -216,18 +216,21 @@ final class YCbCrUpsamplerStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, final int offset) {
|
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 y = yCbCr[offset ] & 0xff;
|
double cb = (yCbCr[offset + 1] & 0xff) - 128; // TODO: The -128 part seems bogus... Consult ReferenceBlackWhite??? But default to these values?
|
||||||
double cb = yCbCr[offset + 1] & 0xff;
|
double cr = (yCbCr[offset + 2] & 0xff) - 128;
|
||||||
double cr = yCbCr[offset + 2] & 0xff;
|
|
||||||
|
|
||||||
double lumaRed = coefficients[0];
|
double lumaRed = coefficients[0];
|
||||||
double lumaGreen = coefficients[1];
|
double lumaGreen = coefficients[1];
|
||||||
double lumaBlue = coefficients[2];
|
double lumaBlue = coefficients[2];
|
||||||
|
|
||||||
rgb[offset ] = clamp((int) Math.round(cr * (2 - 2 * lumaRed) + y));
|
int red = (int) Math.round(cr * (2 - 2 * lumaRed) + y);
|
||||||
rgb[offset + 2] = clamp((int) Math.round(cb * (2 - 2 * lumaBlue) + y));
|
int blue = (int) Math.round(cb * (2 - 2 * lumaBlue) + y);
|
||||||
rgb[offset + 1] = clamp((int) Math.round((y - lumaRed * (rgb[offset] & 0xff) - lumaBlue * (rgb[offset + 2] & 0xff)) / lumaGreen));
|
int green = (int) Math.round((y - lumaRed * (rgb[offset] & 0xff) - lumaBlue * (rgb[offset + 2] & 0xff)) / lumaGreen);
|
||||||
|
|
||||||
|
rgb[offset ] = clamp(red);
|
||||||
|
rgb[offset + 2] = clamp(blue);
|
||||||
|
rgb[offset + 1] = clamp(green);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte clamp(int val) {
|
private static byte clamp(int val) {
|
||||||
|
@ -52,11 +52,14 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTestCase<TIFFImageRe
|
|||||||
new TestData(getClassLoaderResource("/tiff/sm_colors_tile.tif"), new Dimension(64, 64)), // RGB, uncompressed, tiled
|
new TestData(getClassLoaderResource("/tiff/sm_colors_tile.tif"), new Dimension(64, 64)), // RGB, uncompressed, tiled
|
||||||
new TestData(getClassLoaderResource("/tiff/sm_colors_pb_tile.tif"), new Dimension(64, 64)), // RGB, PackBits compressed, tiled
|
new TestData(getClassLoaderResource("/tiff/sm_colors_pb_tile.tif"), new Dimension(64, 64)), // RGB, PackBits compressed, tiled
|
||||||
new TestData(getClassLoaderResource("/tiff/galaxy.tif"), new Dimension(965, 965)), // RGB, LZW compressed
|
new TestData(getClassLoaderResource("/tiff/galaxy.tif"), new Dimension(965, 965)), // RGB, LZW compressed
|
||||||
new TestData(getClassLoaderResource("/tiff/quad-lzw.tif"), new Dimension(512, 384)), // RGB, OLD LZW compressed, tiled
|
new TestData(getClassLoaderResource("/tiff/quad-lzw.tif"), new Dimension(512, 384)), // RGB, Old spec (reversed) LZW compressed, tiled
|
||||||
new TestData(getClassLoaderResource("/tiff/bali.tif"), new Dimension(725, 489)), // Palette-based, LZW compressed
|
new TestData(getClassLoaderResource("/tiff/bali.tif"), new Dimension(725, 489)), // Palette-based, LZW compressed
|
||||||
new TestData(getClassLoaderResource("/tiff/f14.tif"), new Dimension(640, 480)), // Gray, uncompressed
|
new TestData(getClassLoaderResource("/tiff/f14.tif"), new Dimension(640, 480)), // Gray, uncompressed
|
||||||
new TestData(getClassLoaderResource("/tiff/marbles.tif"), new Dimension(1419, 1001)), // RGB, LZW compressed w/predictor
|
new TestData(getClassLoaderResource("/tiff/marbles.tif"), new Dimension(1419, 1001)), // RGB, LZW compressed w/predictor
|
||||||
new TestData(getClassLoaderResource("/tiff/chifley_logo.tif"), new Dimension(591, 177)) // CMYK, uncompressed
|
new TestData(getClassLoaderResource("/tiff/chifley_logo.tif"), new Dimension(591, 177)), // CMYK, uncompressed
|
||||||
|
new TestData(getClassLoaderResource("/tiff/ycbcr-cat.tif"), new Dimension(250, 325)), // YCbCr, LZW compressed
|
||||||
|
new TestData(getClassLoaderResource("/tiff/smallliz.tif"), new Dimension(160, 160)), // YCbCr, Old-Style JPEG compressed (full JFIF stream)
|
||||||
|
new TestData(getClassLoaderResource("/tiff/zackthecat.tif"), new Dimension(234, 213)) // YCbCr, Old-Style JPEG compressed (tables, no JFIF stream)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,4 +92,6 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTestCase<TIFFImageRe
|
|||||||
protected List<String> getMIMETypes() {
|
protected List<String> getMIMETypes() {
|
||||||
return Arrays.asList("image/tiff");
|
return Arrays.asList("image/tiff");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Test YCbCr colors
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ import java.io.InputStream;
|
|||||||
*/
|
*/
|
||||||
@Ignore
|
@Ignore
|
||||||
public class YCbCrUpsamplerStreamTest extends InputStreamAbstractTestCase {
|
public class YCbCrUpsamplerStreamTest extends InputStreamAbstractTestCase {
|
||||||
// TODO: Implement
|
// TODO: Implement + add @Ignore for all tests that makes no sense for this class.
|
||||||
@Override
|
@Override
|
||||||
protected InputStream makeInputStream(byte[] pBytes) {
|
protected InputStream makeInputStream(byte[] pBytes) {
|
||||||
return new YCbCrUpsamplerStream(new ByteArrayInputStream(pBytes), new int[] {2, 2}, pBytes.length / 4, null);
|
return new YCbCrUpsamplerStream(new ByteArrayInputStream(pBytes), new int[] {2, 2}, pBytes.length / 4, null);
|
||||||
|
BIN
imageio/imageio-tiff/src/test/resources/tiff/smallliz.tif
Executable file
BIN
imageio/imageio-tiff/src/test/resources/tiff/smallliz.tif
Executable file
Binary file not shown.
BIN
imageio/imageio-tiff/src/test/resources/tiff/ycbcr-cat.tif
Normal file
BIN
imageio/imageio-tiff/src/test/resources/tiff/ycbcr-cat.tif
Normal file
Binary file not shown.
BIN
imageio/imageio-tiff/src/test/resources/tiff/zackthecat.tif
Normal file
BIN
imageio/imageio-tiff/src/test/resources/tiff/zackthecat.tif
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user