mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 12:05:29 -04:00
#304 TIFF JPEG Lossless support
This commit is contained in:
parent
2e90e4b897
commit
3b76d9fcfd
@ -148,7 +148,7 @@ class JPEGImage10Metadata extends AbstractMetadata {
|
|||||||
if (segment instanceof Frame) {
|
if (segment instanceof Frame) {
|
||||||
Frame sofSegment = (Frame) segment;
|
Frame sofSegment = (Frame) segment;
|
||||||
IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
|
IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
|
||||||
colorSpaceType.setAttribute("name", sofSegment.componentsInFrame() == 1 ? "Gray" : "RGB"); // TODO YCC, YCCK, CMYK etc
|
colorSpaceType.setAttribute("name", sofSegment.componentsInFrame() == 1 ? "GRAY" : "RGB"); // TODO YCC, YCCK, CMYK etc
|
||||||
chroma.appendChild(colorSpaceType);
|
chroma.appendChild(colorSpaceType);
|
||||||
|
|
||||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||||
|
@ -352,10 +352,24 @@ public final class JPEGImageReader extends ImageReaderBase {
|
|||||||
JPEGColorSpace sourceCSType = getSourceCSType(getJFIF(), adobeDCT, sof);
|
JPEGColorSpace sourceCSType = getSourceCSType(getJFIF(), adobeDCT, sof);
|
||||||
|
|
||||||
if (sof.marker == JPEG.SOF3) {
|
if (sof.marker == JPEG.SOF3) {
|
||||||
|
// Read image as lossless
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("Reading using Lossless decoder");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: What about stream position?
|
// TODO: What about stream position?
|
||||||
// TODO: Param handling: Source region, offset, subsampling, destination, destination type, etc....
|
// TODO: Param handling: Source region, offset, subsampling, destination, destination type, etc....
|
||||||
// Read image as lossless
|
BufferedImage bufferedImage = new JPEGLosslessDecoderWrapper(this).readImage(segments, imageInput);
|
||||||
return new JPEGLosslessDecoderWrapper(this).readImage(segments, imageInput);
|
|
||||||
|
// TODO: This is QnD, move param handling to lossless wrapper
|
||||||
|
// TODO: Create test!
|
||||||
|
BufferedImage destination = param != null ? param.getDestination() : null;
|
||||||
|
if (destination != null) {
|
||||||
|
destination.getRaster().setDataElements(0, 0, bufferedImage.getRaster());
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufferedImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to apply ICC profile unless the profile is sRGB/default gray (whatever that is)
|
// We need to apply ICC profile unless the profile is sRGB/default gray (whatever that is)
|
||||||
@ -382,6 +396,18 @@ public final class JPEGImageReader extends ImageReaderBase {
|
|||||||
return delegate.read(imageIndex, param);
|
return delegate.read(imageIndex, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void drawOnto(final BufferedImage pDestination, final Image pSource) {
|
||||||
|
Graphics2D g = pDestination.createGraphics();
|
||||||
|
try {
|
||||||
|
g.setComposite(AlphaComposite.Src);
|
||||||
|
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
||||||
|
g.drawImage(pSource, 0, 0, null);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
g.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private BufferedImage readImageAsRasterAndReplaceColorProfile(int imageIndex, ImageReadParam param, Frame startOfFrame, JPEGColorSpace csType, ICC_Profile profile) throws IOException {
|
private BufferedImage readImageAsRasterAndReplaceColorProfile(int imageIndex, ImageReadParam param, Frame startOfFrame, JPEGColorSpace csType, ICC_Profile profile) throws IOException {
|
||||||
int origWidth = getWidth(imageIndex);
|
int origWidth = getWidth(imageIndex);
|
||||||
int origHeight = getHeight(imageIndex);
|
int origHeight = getHeight(imageIndex);
|
||||||
|
@ -33,10 +33,9 @@ import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
|
|||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.*;
|
||||||
import java.awt.image.DataBufferByte;
|
import java.awt.color.ColorSpace;
|
||||||
import java.awt.image.DataBufferUShort;
|
import java.awt.image.*;
|
||||||
import java.awt.image.Raster;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -71,7 +70,7 @@ final class JPEGLosslessDecoderWrapper {
|
|||||||
* - 16Bit, Grayscale -> BufferedImage.TYPE_USHORT_GRAY
|
* - 16Bit, Grayscale -> BufferedImage.TYPE_USHORT_GRAY
|
||||||
*
|
*
|
||||||
* @param segments segments
|
* @param segments segments
|
||||||
* @param input input stream which contains a jpeg lossless data
|
* @param input input stream which contains JPEG Lossless data
|
||||||
* @return if successfully a BufferedImage is returned
|
* @return if successfully a BufferedImage is returned
|
||||||
* @throws IOException is thrown if the decoder failed or a conversion is not supported
|
* @throws IOException is thrown if the decoder failed or a conversion is not supported
|
||||||
*/
|
*/
|
||||||
@ -92,8 +91,11 @@ final class JPEGLosslessDecoderWrapper {
|
|||||||
switch (decoder.getPrecision()) {
|
switch (decoder.getPrecision()) {
|
||||||
case 8:
|
case 8:
|
||||||
return to8Bit1ComponentGrayScale(decoded, width, height);
|
return to8Bit1ComponentGrayScale(decoded, width, height);
|
||||||
|
case 10:
|
||||||
|
case 12:
|
||||||
|
case 14:
|
||||||
case 16:
|
case 16:
|
||||||
return to16Bit1ComponentGrayScale(decoded, width, height);
|
return to16Bit1ComponentGrayScale(decoded, decoder.getPrecision(), width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 3 components, assumed to be RGB
|
// 3 components, assumed to be RGB
|
||||||
@ -121,12 +123,20 @@ final class JPEGLosslessDecoderWrapper {
|
|||||||
* precision: 16 bit, componentCount = 1
|
* precision: 16 bit, componentCount = 1
|
||||||
*
|
*
|
||||||
* @param decoded data buffer
|
* @param decoded data buffer
|
||||||
|
* @param precision
|
||||||
* @param width of the image
|
* @param width of the image
|
||||||
* @param height of the image
|
* @param height of the image @return a BufferedImage.TYPE_USHORT_GRAY
|
||||||
* @return a BufferedImage.TYPE_USHORT_GRAY
|
|
||||||
*/
|
*/
|
||||||
private BufferedImage to16Bit1ComponentGrayScale(int[][] decoded, int width, int height) {
|
private BufferedImage to16Bit1ComponentGrayScale(int[][] decoded, int precision, int width, int height) {
|
||||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
|
BufferedImage image;
|
||||||
|
if (precision == 16) {
|
||||||
|
image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] {precision}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
|
||||||
|
image = new BufferedImage(colorModel, colorModel.createCompatibleWritableRaster(width, height), colorModel.isAlphaPremultiplied(), null);
|
||||||
|
}
|
||||||
|
|
||||||
short[] imageBuffer = ((DataBufferUShort) image.getRaster().getDataBuffer()).getData();
|
short[] imageBuffer = ((DataBufferUShort) image.getRaster().getDataBuffer()).getData();
|
||||||
|
|
||||||
for (int i = 0; i < imageBuffer.length; i++) {
|
for (int i = 0; i < imageBuffer.length; i++) {
|
||||||
|
@ -21,6 +21,11 @@
|
|||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio-metadata</artifactId>
|
<artifactId>imageio-metadata</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
|
<artifactId>imageio-jpeg</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio-core</artifactId>
|
<artifactId>imageio-core</artifactId>
|
||||||
|
@ -38,6 +38,7 @@ import com.twelvemonkeys.imageio.metadata.Directory;
|
|||||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||||
import com.twelvemonkeys.imageio.metadata.iptc.IPTCReader;
|
import com.twelvemonkeys.imageio.metadata.iptc.IPTCReader;
|
||||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
||||||
|
import com.twelvemonkeys.imageio.metadata.psd.PSD;
|
||||||
import com.twelvemonkeys.imageio.metadata.psd.PSDReader;
|
import com.twelvemonkeys.imageio.metadata.psd.PSDReader;
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.Rational;
|
import com.twelvemonkeys.imageio.metadata.tiff.Rational;
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||||
@ -51,6 +52,7 @@ import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
|||||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||||
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
import javax.imageio.*;
|
import javax.imageio.*;
|
||||||
@ -239,9 +241,97 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
if (Arrays.equals(foo.getBytes(StandardCharsets.US_ASCII), Arrays.copyOf(value, foo.length()))) {
|
if (Arrays.equals(foo.getBytes(StandardCharsets.US_ASCII), Arrays.copyOf(value, foo.length()))) {
|
||||||
System.err.println("foo: " + foo);
|
System.err.println("foo: " + foo);
|
||||||
// int offset = foo.length() + 1;
|
int offset = foo.length() + 1;
|
||||||
// ImageInputStream input = new ByteArrayImageInputStream(value, offset, value.length - offset);
|
ImageInputStream input = new ByteArrayImageInputStream(value, offset, value.length - offset);
|
||||||
// input.setByteOrder(ByteOrder.LITTLE_ENDIAN); // TODO: WHY???!
|
// input.setByteOrder(ByteOrder.LITTLE_ENDIAN); // TODO: WHY???!
|
||||||
|
|
||||||
|
while (input.getStreamPosition() < value.length - offset) {
|
||||||
|
int resourceId = input.readInt();
|
||||||
|
if (resourceId != PSD.RESOURCE_TYPE) {
|
||||||
|
System.err.println("Not a PSD resource: " + resourceId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resourceKey = input.readInt();
|
||||||
|
System.err.println("resourceKey: " + intToStr(resourceKey));
|
||||||
|
long resourceLength = input.readUnsignedInt();
|
||||||
|
System.err.println("resourceLength: " + resourceLength);
|
||||||
|
|
||||||
|
long pad = (4 - (resourceLength % 4)) % 4;
|
||||||
|
long resourceLengthPadded = resourceLength + pad; // Padded to 32 bit boundary, possibly 64 bit for 8B64 resources
|
||||||
|
long streamPosition = input.getStreamPosition();
|
||||||
|
|
||||||
|
if (resourceKey == ('L' << 24 | 'a' << 16 | 'y' << 8 | 'r')) {
|
||||||
|
short count = input.readShort();
|
||||||
|
System.err.println("layer count: " + count);
|
||||||
|
|
||||||
|
for (int layer = 0; layer < count; layer++) {
|
||||||
|
int top = input.readInt();
|
||||||
|
int left = input.readInt();
|
||||||
|
int bottom = input.readInt();
|
||||||
|
int right = input.readInt();
|
||||||
|
System.err.printf("%d, %d, %d, %d\n", top, left, bottom, right);
|
||||||
|
|
||||||
|
short channels = input.readShort();
|
||||||
|
System.err.println("channels: " + channels);
|
||||||
|
|
||||||
|
for (int channel = 0; channel < channels; channel++) {
|
||||||
|
short channelId = input.readShort();
|
||||||
|
System.err.println("channelId: " + channelId);
|
||||||
|
long channelLength = input.readUnsignedInt();
|
||||||
|
System.err.println("channelLength: " + channelLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println("8BIM: " + intToStr(input.readInt()));
|
||||||
|
int blendMode = input.readInt();
|
||||||
|
System.err.println("blend mode key: " + intToStr(blendMode));
|
||||||
|
|
||||||
|
int opacity = input.readUnsignedByte();
|
||||||
|
System.err.println("opacity: " + opacity);
|
||||||
|
int clipping = input.readUnsignedByte();
|
||||||
|
System.err.println("clipping: " + clipping);
|
||||||
|
byte flags = input.readByte();
|
||||||
|
System.err.printf("flags: 0x%02x\n", flags);
|
||||||
|
input.readByte(); // Pad
|
||||||
|
|
||||||
|
long layerExtraDataLength = input.readUnsignedInt();
|
||||||
|
long pos = input.getStreamPosition();
|
||||||
|
System.err.println("length: " + layerExtraDataLength);
|
||||||
|
|
||||||
|
long layerMaskSize = input.readUnsignedInt();
|
||||||
|
input.skipBytes(layerMaskSize);
|
||||||
|
long layerBlendingRangesSize = input.readUnsignedInt();
|
||||||
|
input.skipBytes(layerBlendingRangesSize);
|
||||||
|
|
||||||
|
String layerName = readPascalString(input);
|
||||||
|
System.err.println("layerName: " + layerName);
|
||||||
|
int mod = (layerName.length() + 1) % 4; // len + 1 for null-term
|
||||||
|
System.err.println("mod: " + mod);
|
||||||
|
if (mod != 0) {
|
||||||
|
input.skipBytes(4 - mod);
|
||||||
|
}
|
||||||
|
System.err.println("input.getStreamPosition(): " + input.getStreamPosition());
|
||||||
|
|
||||||
|
// TODO: More data here
|
||||||
|
System.err.println(TIFFReader.HexDump.dump(0, value, (int) (offset + input.getStreamPosition()), 64));;
|
||||||
|
|
||||||
|
input.seek(pos + layerExtraDataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// long len = input.readUnsignedInt();
|
||||||
|
// System.err.println("len: " + len);
|
||||||
|
//
|
||||||
|
// int count = input.readUnsignedShort();
|
||||||
|
// System.err.println("count: " + count);
|
||||||
|
|
||||||
|
System.err.println(TIFFReader.HexDump.dump(0, value, (int) (offset + input.getStreamPosition()), 64));;
|
||||||
|
|
||||||
|
}
|
||||||
|
input.seek(streamPosition + resourceLengthPadded);
|
||||||
|
System.out.println("input.getStreamPosition(): " + input.getStreamPosition());
|
||||||
|
}
|
||||||
|
|
||||||
// Directory psd2 = new PSDReader().read(input);
|
// Directory psd2 = new PSDReader().read(input);
|
||||||
// System.err.println("-----------------------------------------------------------------------------");
|
// System.err.println("-----------------------------------------------------------------------------");
|
||||||
// System.err.println("psd2: " + psd2);
|
// System.err.println("psd2: " + psd2);
|
||||||
@ -251,6 +341,30 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String readPascalString(final DataInput pInput) throws IOException {
|
||||||
|
int length = pInput.readUnsignedByte();
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = new byte[length];
|
||||||
|
pInput.readFully(bytes);
|
||||||
|
|
||||||
|
return StringUtil.decode(bytes, 0, bytes.length, "ASCII");
|
||||||
|
}
|
||||||
|
|
||||||
|
static String intToStr(int value) {
|
||||||
|
return new String(
|
||||||
|
new byte[]{
|
||||||
|
(byte) ((value & 0xff000000) >>> 24),
|
||||||
|
(byte) ((value & 0x00ff0000) >> 16),
|
||||||
|
(byte) ((value & 0x0000ff00) >> 8),
|
||||||
|
(byte) ((value & 0x000000ff))
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void readIFD(final int imageIndex) throws IOException {
|
private void readIFD(final int imageIndex) throws IOException {
|
||||||
readMetadata();
|
readMetadata();
|
||||||
checkBounds(imageIndex);
|
checkBounds(imageIndex);
|
||||||
@ -1048,7 +1162,18 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
// Otherwise, it's likely CMYK or some other interpretation we don't need to convert.
|
// Otherwise, it's likely CMYK or some other interpretation we don't need to convert.
|
||||||
// We'll have to use readAsRaster and later apply color space conversion ourselves
|
// We'll have to use readAsRaster and later apply color space conversion ourselves
|
||||||
Raster raster = jpegReader.readRaster(0, jpegParam);
|
Raster raster = jpegReader.readRaster(0, jpegParam);
|
||||||
normalizeColor(interpretation, ((DataBufferByte) raster.getDataBuffer()).getData());
|
// TODO: Refactor + duplicate this for all JPEG-in-TIFF cases
|
||||||
|
switch (raster.getTransferType()) {
|
||||||
|
case DataBuffer.TYPE_BYTE:
|
||||||
|
normalizeColor(interpretation, ((DataBufferByte) raster.getDataBuffer()).getData());
|
||||||
|
break;
|
||||||
|
case DataBuffer.TYPE_USHORT:
|
||||||
|
normalizeColor(interpretation, ((DataBufferUShort) raster.getDataBuffer()).getData());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unsupported transfer type: " + raster.getTransferType());
|
||||||
|
}
|
||||||
|
|
||||||
destination.getRaster().setDataElements(offset.x, offset.y, raster);
|
destination.getRaster().setDataElements(offset.x, offset.y, raster);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1081,9 +1206,8 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
int mode = getValueAsIntWithDefault(TIFF.TAG_OLD_JPEG_PROC, TIFFExtension.JPEG_PROC_BASELINE);
|
int mode = getValueAsIntWithDefault(TIFF.TAG_OLD_JPEG_PROC, TIFFExtension.JPEG_PROC_BASELINE);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case TIFFExtension.JPEG_PROC_BASELINE:
|
case TIFFExtension.JPEG_PROC_BASELINE:
|
||||||
break; // Supported
|
|
||||||
case TIFFExtension.JPEG_PROC_LOSSLESS:
|
case TIFFExtension.JPEG_PROC_LOSSLESS:
|
||||||
throw new IIOException("Unsupported TIFF JPEGProcessingMode: Lossless (14)");
|
break; // Supported
|
||||||
default:
|
default:
|
||||||
throw new IIOException("Unknown TIFF JPEGProcessingMode value: " + mode);
|
throw new IIOException("Unknown TIFF JPEGProcessingMode value: " + mode);
|
||||||
}
|
}
|
||||||
@ -1350,7 +1474,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
return jpegReader.getImageMetadata(0);
|
return jpegReader.getImageMetadata(0);
|
||||||
}
|
}
|
||||||
catch (IIOException e) {
|
catch (IIOException e) {
|
||||||
processWarningOccurred("Could not read metadata metadata JPEG compressed TIFF: " + e.getMessage() + " colors may look incorrect");
|
processWarningOccurred("Could not read metadata for JPEG compressed TIFF (" + e.getMessage() + "): Colors may look incorrect");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -1391,20 +1515,8 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ImageReader createJPEGDelegate() throws IOException {
|
private ImageReader createJPEGDelegate() throws IOException {
|
||||||
// TIFF is strictly ISO JPEG, so we should probably stick to the standard reader
|
// We'll just use the default (first) reader
|
||||||
ImageReaderSpi jpegProvider = lookupProviderByName(IIORegistry.getDefaultInstance(), "com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi");
|
// If it's the TwelveMonkeys one, we will be able to read JPEG Lossless etc.
|
||||||
|
|
||||||
if (jpegProvider != null) {
|
|
||||||
return jpegProvider.createReaderInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to default reader below
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("Could not create " + "com.sun.imageio.plugins.jpeg.JPEGImageReader"
|
|
||||||
+ ", falling back to default JPEG capable ImageReader");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can't get the standard reader, fall back to the default (first) reader
|
|
||||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
||||||
if (!readers.hasNext()) {
|
if (!readers.hasNext()) {
|
||||||
throw new IIOException("Could not instantiate JPEGImageReader");
|
throw new IIOException("Could not instantiate JPEGImageReader");
|
||||||
@ -1415,28 +1527,17 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
private static InputStream createJFIFStream(int bands, int stripTileWidth, int stripTileHeight, byte[][] qTables, byte[][] dcTables, byte[][] acTables) throws IOException {
|
private static InputStream createJFIFStream(int bands, int stripTileWidth, int stripTileHeight, byte[][] qTables, byte[][] dcTables, byte[][] acTables) throws IOException {
|
||||||
FastByteArrayOutputStream stream = new FastByteArrayOutputStream(
|
FastByteArrayOutputStream stream = new FastByteArrayOutputStream(
|
||||||
2 + 2 + 2 + 6 + 3 * bands +
|
2 +
|
||||||
5 * qTables.length + qTables.length * qTables[0].length +
|
5 * qTables.length + qTables.length * qTables[0].length +
|
||||||
5 * dcTables.length + dcTables.length * dcTables[0].length +
|
5 * dcTables.length + dcTables.length * dcTables[0].length +
|
||||||
5 * acTables.length + acTables.length * acTables[0].length +
|
5 * acTables.length + acTables.length * acTables[0].length +
|
||||||
|
2 + 2 + 6 + 3 * bands +
|
||||||
8 + 2 * bands
|
8 + 2 * bands
|
||||||
);
|
);
|
||||||
|
|
||||||
DataOutputStream out = new DataOutputStream(stream);
|
DataOutputStream out = new DataOutputStream(stream);
|
||||||
|
|
||||||
out.writeShort(JPEG.SOI);
|
out.writeShort(JPEG.SOI);
|
||||||
out.writeShort(JPEG.SOF0);
|
|
||||||
out.writeShort(2 + 6 + 3 * bands); // SOF0 len
|
|
||||||
out.writeByte(8); // bits TODO: Consult raster/transfer type or BitsPerSample for 12/16 bits support
|
|
||||||
out.writeShort(stripTileHeight); // height
|
|
||||||
out.writeShort(stripTileWidth); // width
|
|
||||||
out.writeByte(bands); // Number of components
|
|
||||||
|
|
||||||
for (int comp = 0; comp < bands; comp++) {
|
|
||||||
out.writeByte(comp); // Component id
|
|
||||||
out.writeByte(comp == 0 ? 0x22 : 0x11); // h/v subsampling TODO: FixMe, consult YCbCrSubsampling
|
|
||||||
out.writeByte(comp); // Q table selector TODO: Consider merging if tables are equal
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Consider merging if tables are equal
|
// TODO: Consider merging if tables are equal
|
||||||
for (int tableIndex = 0; tableIndex < qTables.length; tableIndex++) {
|
for (int tableIndex = 0; tableIndex < qTables.length; tableIndex++) {
|
||||||
@ -1465,6 +1566,19 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
out.write(table); // Table data
|
out.write(table); // Table data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.writeShort(JPEG.SOF0);
|
||||||
|
out.writeShort(2 + 6 + 3 * bands); // SOF0 len
|
||||||
|
out.writeByte(8); // bits TODO: Consult raster/transfer type or BitsPerSample for 12/16 bits support
|
||||||
|
out.writeShort(stripTileHeight); // height
|
||||||
|
out.writeShort(stripTileWidth); // width
|
||||||
|
out.writeByte(bands); // Number of components
|
||||||
|
|
||||||
|
for (int comp = 0; comp < bands; comp++) {
|
||||||
|
out.writeByte(comp); // Component id
|
||||||
|
out.writeByte(comp == 0 ? 0x22 : 0x11); // h/v subsampling TODO: FixMe, consult YCbCrSubsampling
|
||||||
|
out.writeByte(comp); // Q table selector TODO: Consider merging if tables are equal
|
||||||
|
}
|
||||||
|
|
||||||
out.writeShort(JPEG.SOS);
|
out.writeShort(JPEG.SOS);
|
||||||
out.writeShort(6 + 2 * bands); // SOS length
|
out.writeShort(6 + 2 * bands); // SOS length
|
||||||
out.writeByte(bands); // Num comp
|
out.writeByte(bands); // Num comp
|
||||||
|
@ -149,11 +149,15 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-contig-16.tif"), new Dimension(73, 43)), // CMYK 16 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-contig-16.tif"), new Dimension(73, 43)), // CMYK 16 bit/sample
|
||||||
// Separated (CMYK) Planar (PlanarConfiguration: 2)
|
// Separated (CMYK) Planar (PlanarConfiguration: 2)
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-planar-08.tif"), new Dimension(73, 43)), // CMYK 8 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-planar-08.tif"), new Dimension(73, 43)), // CMYK 8 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-planar-16.tif"), new Dimension(73, 43)) // CMYK 16 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-planar-16.tif"), new Dimension(73, 43)), // CMYK 16 bit/sample
|
||||||
|
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-8bit-gray.tif"), new Dimension(512, 512)), // Lossless JPEG Gray, 8 bit/sample
|
||||||
|
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-12bit-gray.tif"), new Dimension(512, 512)), // Lossless JPEG Gray, 12 bit/sample
|
||||||
|
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-16bit-gray.tif"), new Dimension(512, 512)), // Lossless JPEG Gray, 16 bit/sample
|
||||||
|
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-24bit-rgb"), new Dimension(512, 512)) // Lossless JPEG RGB, 8 bit/sample
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<TestData> getUnsupportedTestData() {
|
private List<TestData> getUnsupportedTestData() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
// RGB Interleaved (PlanarConfiguration: 1)
|
// RGB Interleaved (PlanarConfiguration: 1)
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-02.tif"), new Dimension(73, 43)), // RGB 2 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-02.tif"), new Dimension(73, 43)), // RGB 2 bit/sample
|
||||||
@ -265,7 +269,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
|
|
||||||
assertNotNull(image);
|
assertNotNull(image);
|
||||||
assertEquals(testData.getDimension(0), new Dimension(image.getWidth(), image.getHeight()));
|
assertEquals(testData.getDimension(0), new Dimension(image.getWidth(), image.getHeight()));
|
||||||
verify(warningListener, atLeastOnce()).warningOccurred(eq(reader), contains("metadata"));
|
verify(warningListener, atLeastOnce()).warningOccurred(eq(reader), contains("JPEG"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
imageio/imageio-tiff/src/test/resources/tiff/jpeg-lossless-12bit-gray.tif
Executable file
BIN
imageio/imageio-tiff/src/test/resources/tiff/jpeg-lossless-12bit-gray.tif
Executable file
Binary file not shown.
BIN
imageio/imageio-tiff/src/test/resources/tiff/jpeg-lossless-16bit-gray.tif
Executable file
BIN
imageio/imageio-tiff/src/test/resources/tiff/jpeg-lossless-16bit-gray.tif
Executable file
Binary file not shown.
BIN
imageio/imageio-tiff/src/test/resources/tiff/jpeg-lossless-24bit-rgb
Executable file
BIN
imageio/imageio-tiff/src/test/resources/tiff/jpeg-lossless-24bit-rgb
Executable file
Binary file not shown.
BIN
imageio/imageio-tiff/src/test/resources/tiff/jpeg-lossless-8bit-gray.tif
Executable file
BIN
imageio/imageio-tiff/src/test/resources/tiff/jpeg-lossless-8bit-gray.tif
Executable file
Binary file not shown.
@ -135,6 +135,12 @@
|
|||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>imageio-jpeg</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.groupId}</groupId>
|
<groupId>${project.groupId}</groupId>
|
||||||
<artifactId>imageio-core</artifactId>
|
<artifactId>imageio-core</artifactId>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user