mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
#623: TGAImageReader, PCXImageReader and SGIImageReader now return more standard image types as default, for better AffineTransformOp compatibility. Added tests.
Bonus: Subsampling fix for TGAImageReader and BMPImageReader. (cherry picked from commit 812e12acb0cdcf68574f10b384ef8d6240ec3e27)
This commit is contained in:
parent
09573b52ac
commit
b55c623b87
@ -30,6 +30,22 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.event.IIOReadUpdateListener;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
@ -40,27 +56,6 @@ import java.nio.ByteOrder;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.event.IIOReadUpdateListener;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
|
||||
/**
|
||||
* ImageReader for Microsoft Windows Bitmap (BMP) format.
|
||||
*
|
||||
@ -482,8 +477,12 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
private void readRowByte(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final byte[] rowDataByte, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = !header.topDown ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataByte.length);
|
||||
|
||||
return;
|
||||
@ -498,19 +497,17 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (header.topDown) {
|
||||
destChannel.setDataElements(0, y, srcChannel);
|
||||
} else {
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
}
|
||||
|
||||
private void readRowUShort(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final short[] rowDataUShort, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = !header.topDown ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataUShort.length * 2 + (rowDataUShort.length % 2) * 2);
|
||||
|
||||
return;
|
||||
@ -530,19 +527,17 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (header.topDown) {
|
||||
destChannel.setDataElements(0, y, srcChannel);
|
||||
} else {
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
}
|
||||
|
||||
private void readRowInt(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final int[] rowDataInt, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = !header.topDown ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataInt.length * 4);
|
||||
|
||||
return;
|
||||
@ -557,14 +552,8 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (header.topDown) {
|
||||
destChannel.setDataElements(0, y, srcChannel);
|
||||
} else {
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Candidate util method
|
||||
private static void readFully(final DataInput input, final short[] shorts) throws IOException {
|
||||
|
@ -45,9 +45,8 @@ import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.awt.image.SampleModel;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@ -57,6 +56,7 @@ import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ -1738,6 +1738,43 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
|
||||
// Allow subclasses to filter out test data that can't be converted to a compatible image without data loss
|
||||
return getTestData();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAffineTransformOpCompatibility() throws IOException {
|
||||
// Test that the output of normal images are compatible with AffineTransformOp. Is unlikely to work on all test data
|
||||
ImageReader reader = createReader();
|
||||
|
||||
for (TestData testData : getTestDataForAffineTransformOpCompatibility()) {
|
||||
try (ImageInputStream input = testData.getInputStream()) {
|
||||
reader.setInput(input);
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(min(reader.getWidth(0), 64), min(reader.getHeight(0), 64)));
|
||||
|
||||
BufferedImage originalImage = reader.read(0, param);
|
||||
|
||||
AffineTransform transform = AffineTransform.getTranslateInstance(10, 10);
|
||||
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
|
||||
|
||||
try {
|
||||
BufferedImage resultImage = op.filter(originalImage, null); // The exception happens here
|
||||
assertNotNull(resultImage);
|
||||
}
|
||||
catch (ImagingOpException e) {
|
||||
fail(e.getMessage() + ".\n\t"
|
||||
+ originalImage + "\n\t"
|
||||
+ testData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Ignore("TODO: Implement")
|
||||
@Test
|
||||
public void testSetDestinationBands() {
|
||||
|
@ -38,12 +38,14 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* TGAImageReaderTest
|
||||
* HDRImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: TGAImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
* @version $Id: HDRImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
*/
|
||||
public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader> {
|
||||
@Override
|
||||
@ -58,6 +60,12 @@ public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader>
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
|
||||
// HDR images uses floating point buffers...
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("HDR", "hdr", "RGBE", "rgbe");
|
||||
|
@ -104,8 +104,15 @@ public final class PCXImageReader extends ImageReaderBase {
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
|
||||
List<ImageTypeSpecifier> specifiers = new ArrayList<>();
|
||||
|
||||
// TODO: Implement
|
||||
if (rawType.getSampleModel() instanceof BandedSampleModel) {
|
||||
if (rawType.getNumBands() == 3) {
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
|
||||
}
|
||||
else if (rawType.getNumBands() == 4) {
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE));
|
||||
}
|
||||
}
|
||||
specifiers.add(rawType);
|
||||
|
||||
return specifiers.iterator();
|
||||
@ -142,10 +149,10 @@ public final class PCXImageReader extends ImageReaderBase {
|
||||
// PCX RGB has channels for 24 bit RGB, will be validated by ImageTypeSpecifier
|
||||
return ImageTypeSpecifiers.createBanded(ColorSpace.getInstance(ColorSpace.CS_sRGB), createIndices(channels, 1), createIndices(channels, 0), DataBuffer.TYPE_BYTE, channels == 4, false);
|
||||
case 24:
|
||||
// Some sources says this is possible...
|
||||
// Some sources say this is possible...
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
||||
case 32:
|
||||
// Some sources says this is possible...
|
||||
// Some sources say this is possible...
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
|
||||
default:
|
||||
throw new IIOException("Unknown number of bytes per pixel: " + header.getBitsPerPixel());
|
||||
|
@ -78,7 +78,7 @@ public class PCXImageReaderTest extends ImageReaderAbstractTest<PCXImageReader>
|
||||
new TestData(getClassLoaderResource("/pcx/DARKSTAR.PCX"), new Dimension(88, 52)), // RLE encoded monochrome (1 bps/1 channel)
|
||||
new TestData(getClassLoaderResource("/pcx/MARBLES.PCX"), new Dimension(1419, 1001)), // RLE encoded RGB
|
||||
new TestData(getClassLoaderResource("/pcx/no-palette-monochrome.pcx"), new Dimension(128, 152)), // RLE encoded monochrome (1 bps/1 channel)
|
||||
// See cga-pcx.txt, however, the text seems to be in error, the bits can not not as described
|
||||
// See cga-pcx.txt, however, the text seems to be in error, I don't see how the bits can be as described
|
||||
new TestData(getClassLoaderResource("/pcx/CGA_BW.PCX"), new Dimension(640, 200)), // RLE encoded indexed (CGA mode)
|
||||
new TestData(getClassLoaderResource("/pcx/CGA_FSD.PCX"), new Dimension(320, 200)), // RLE encoded indexed (CGA mode)
|
||||
new TestData(getClassLoaderResource("/pcx/CGA_RGBI.PCX"), new Dimension(320, 200)), // RLE encoded indexed (CGA mode)
|
||||
|
@ -61,6 +61,20 @@ public class PNMImageReaderTest extends ImageReaderAbstractTest<PNMImageReader>
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/ppm/lena.ppm"), new Dimension(128, 128)), // P6 (PPM RAW)
|
||||
new TestData(getClassLoaderResource("/ppm/colors.ppm"), new Dimension(3, 2)), // P3 (PPM PLAIN)
|
||||
new TestData(getClassLoaderResource("/pbm/j.pbm"), new Dimension(6, 10)), // P1 (PBM PLAIN)
|
||||
new TestData(getClassLoaderResource("/pgm/feep.pgm"), new Dimension(24, 7)), // P2 (PGM PLAIN)
|
||||
new TestData(getClassLoaderResource("/pgm/feep16.pgm"), new Dimension(24, 7)), // P2 (PGM PLAIN, 16 bits/sample)
|
||||
new TestData(getClassLoaderResource("/pgm/house.l.pgm"), new Dimension(367, 241)), // P5 (PGM RAW)
|
||||
new TestData(getClassLoaderResource("/ppm/lighthouse_rgb48.ppm"), new Dimension(768, 512)) // P6 (PPM RAW, 16 bits/sample)
|
||||
// "/pfm/memorial.pfm" uses floating point
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList(
|
||||
|
@ -331,7 +331,7 @@ public final class PSDImageReader extends ImageReaderBase {
|
||||
// Just stick to the raw type
|
||||
}
|
||||
|
||||
// Finally add the raw type
|
||||
// Finally, add the raw type
|
||||
types.add(rawType);
|
||||
|
||||
return types.iterator();
|
||||
|
@ -86,8 +86,6 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
|
||||
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
|
||||
// 4 channel, CMYK, 16 bit samples
|
||||
new TestData(getClassLoaderResource("/psd/cmyk_16bits.psd"), new Dimension(1000, 275)),
|
||||
// 3 channel, RGB, 32 bit samples
|
||||
new TestData(getClassLoaderResource("/psd/32bit5x5.psd"), new Dimension(5, 5)),
|
||||
// 3 channel, RGB, 8 bit samples ("Large Document Format" aka PSB)
|
||||
new TestData(getClassLoaderResource("/psb/test_original.psb"), new Dimension(710, 512)),
|
||||
// From http://telegraphics.com.au/svn/psdparse/trunk/psd/
|
||||
@ -104,11 +102,48 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
|
||||
new TestData(getClassLoaderResource("/psd/rgb-multichannel-no-transparency.psd"), new Dimension(100, 100)),
|
||||
new TestData(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"), new Dimension(100, 100)),
|
||||
// CMYK, uncompressed + contains some uncommon MeSa (instead of 8BIM) resource blocks
|
||||
new TestData(getClassLoaderResource("/psd/fruit-cmyk-MeSa-resource.psd"), new Dimension(400, 191))
|
||||
new TestData(getClassLoaderResource("/psd/fruit-cmyk-MeSa-resource.psd"), new Dimension(400, 191)),
|
||||
// 3 channel, RGB, 32 bit samples
|
||||
new TestData(getClassLoaderResource("/psd/32bit5x5.psd"), new Dimension(5, 5))
|
||||
// TODO: Need more recent ZIP compressed PSD files from CS2/CS3+
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
|
||||
return Arrays.asList(
|
||||
// 5 channel, RGB
|
||||
new TestData(getClassLoaderResource("/psd/photoshopping.psd"), new Dimension(300, 225)),
|
||||
// 1 channel, gray, 8 bit samples
|
||||
new TestData(getClassLoaderResource("/psd/buttons.psd"), new Dimension(20, 20)),
|
||||
// 3 channel RGB, "no composite layer"
|
||||
new TestData(getClassLoaderResource("/psd/jugware-icon.psd"), new Dimension(128, 128)),
|
||||
// 3 channel RGB, old data, no layer info/mask
|
||||
new TestData(getClassLoaderResource("/psd/MARBLES.PSD"), new Dimension(1419, 1001)),
|
||||
// 1 channel, indexed color
|
||||
new TestData(getClassLoaderResource("/psd/coral_fish.psd"), new Dimension(800, 800)),
|
||||
// 1 channel, bitmap, 1 bit samples
|
||||
new TestData(getClassLoaderResource("/psd/test_bitmap.psd"), new Dimension(710, 512)),
|
||||
// 1 channel, gray, 16 bit samples
|
||||
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
|
||||
// 3 channel, RGB, 8 bit samples ("Large Document Format" aka PSB)
|
||||
new TestData(getClassLoaderResource("/psb/test_original.psb"), new Dimension(710, 512)),
|
||||
// From http://telegraphics.com.au/svn/psdparse/trunk/psd/
|
||||
new TestData(getClassLoaderResource("/psd/adobehq.psd"), new Dimension(341, 512)),
|
||||
new TestData(getClassLoaderResource("/psd/adobehq_ind.psd"), new Dimension(341, 512)),
|
||||
// Contains a shorter than normal PrintFlags chunk
|
||||
new TestData(getClassLoaderResource("/psd/adobehq-2.5.psd"), new Dimension(341, 512)),
|
||||
new TestData(getClassLoaderResource("/psd/adobehq-3.0.psd"), new Dimension(341, 512)),
|
||||
new TestData(getClassLoaderResource("/psd/adobehq-5.5.psd"), new Dimension(341, 512)),
|
||||
new TestData(getClassLoaderResource("/psd/adobehq-7.0.psd"), new Dimension(341, 512)),
|
||||
// From https://github.com/kmike/psd-tools/tree/master/tests/psd_files
|
||||
new TestData(getClassLoaderResource("/psd/masks2.psd"), new Dimension(640, 1136)),
|
||||
// RGB, multiple alpha channels, no transparency
|
||||
new TestData(getClassLoaderResource("/psd/rgb-multichannel-no-transparency.psd"), new Dimension(100, 100)),
|
||||
new TestData(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"), new Dimension(100, 100))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Collections.singletonList("psd");
|
||||
@ -472,7 +507,7 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
|
||||
public void testMultiChannelNoTransparencyPSB() throws IOException {
|
||||
PSDImageReader imageReader = createReader();
|
||||
|
||||
// The following PSB is RGB, has 4 channels (1 alpha/auxillary channel), but should be treated as opaque
|
||||
// The following PSB is RGB, has 4 channels (1 alpha/auxiliary channel), but should be treated as opaque
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"))) {
|
||||
imageReader.setInput(stream);
|
||||
|
||||
|
@ -59,7 +59,7 @@ public final class SGIImageReader extends ImageReaderBase {
|
||||
|
||||
private SGIHeader header;
|
||||
|
||||
protected SGIImageReader(final ImageReaderSpi provider) {
|
||||
SGIImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@ -88,9 +88,31 @@ public final class SGIImageReader extends ImageReaderBase {
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException {
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
|
||||
List<ImageTypeSpecifier> specifiers = new ArrayList<ImageTypeSpecifier>();
|
||||
List<ImageTypeSpecifier> specifiers = new ArrayList<>();
|
||||
|
||||
int channels = header.getChannels();
|
||||
|
||||
switch (header.getBytesPerPixel()) {
|
||||
case 1:
|
||||
if (channels == 1) {
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY));
|
||||
}
|
||||
else if (channels == 3) {
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
|
||||
}
|
||||
else if (channels == 4) {
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
|
||||
}
|
||||
|
||||
break;
|
||||
case 2:
|
||||
if (channels == 1) {
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
specifiers.add(rawType);
|
||||
|
||||
return specifiers.iterator();
|
||||
|
@ -96,10 +96,18 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
|
||||
List<ImageTypeSpecifier> specifiers = new ArrayList<>();
|
||||
|
||||
// TODO: Implement
|
||||
specifiers.add(rawType);
|
||||
|
||||
if (rawType.getBufferedImageType() == BufferedImage.TYPE_INT_RGB) {
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_BGR));
|
||||
}
|
||||
else if (rawType.getBufferedImageType() == BufferedImage.TYPE_INT_ARGB) {
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB_PRE));
|
||||
}
|
||||
else if (rawType.getBufferedImageType() == BufferedImage.TYPE_INT_ARGB_PRE) {
|
||||
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB));
|
||||
}
|
||||
|
||||
return specifiers.iterator();
|
||||
}
|
||||
|
||||
@ -121,7 +129,8 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
|
||||
case 16:
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY);
|
||||
default: throw new IIOException("Unknown pixel depth for monochrome: " + header.getPixelDepth());
|
||||
default:
|
||||
throw new IIOException("Unknown pixel depth for monochrome: " + header.getPixelDepth());
|
||||
}
|
||||
case TGA.IMAGETYPE_TRUECOLOR:
|
||||
case TGA.IMAGETYPE_TRUECOLOR_RLE:
|
||||
@ -141,12 +150,14 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
case 24:
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
||||
case 32:
|
||||
// 4BYTE_BGRX...
|
||||
// Can't mask out alpha (efficiently) for 4BYTE, so we'll ignore it while reading instead,
|
||||
// if hasAlpha is false
|
||||
return ImageTypeSpecifiers.createInterleaved(sRGB, new int[] {2, 1, 0, 3}, DataBuffer.TYPE_BYTE, true, isAlphaPremultiplied);
|
||||
// NOTE: We'll read using little endian byte order, thus the file layout is BGRA/BGRx
|
||||
if (hasAlpha) {
|
||||
return ImageTypeSpecifier.createFromBufferedImageType(isAlphaPremultiplied ? BufferedImage.TYPE_INT_ARGB_PRE : BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
|
||||
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
|
||||
default:
|
||||
throw new IIOException("Unknown pixel depth for truecolor: " + header.getPixelDepth());
|
||||
throw new IIOException("Unknown pixel depth for true color: " + header.getPixelDepth());
|
||||
}
|
||||
default:
|
||||
throw new IIOException("Unknown image type: " + header.getImageType());
|
||||
@ -193,20 +204,26 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
input = imageInput;
|
||||
}
|
||||
|
||||
int pixelDepth = header.getPixelDepth();
|
||||
boolean flipped = isOriginLowerLeft(header.getOrigin());
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
switch (header.getPixelDepth()) {
|
||||
switch (pixelDepth) {
|
||||
case 8:
|
||||
case 24:
|
||||
case 32:
|
||||
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||
readRowByte(input, height, srcRegion, header.getOrigin(), xSub, ySub, rowDataByte, destRaster, clippedRow, y);
|
||||
readRowByte(input, height, srcRegion, flipped, xSub, ySub, rowDataByte, destRaster, clippedRow, y);
|
||||
break;
|
||||
case 16:
|
||||
short[] rowDataUShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
||||
readRowUShort(input, height, srcRegion, header.getOrigin(), xSub, ySub, rowDataUShort, destRaster, clippedRow, y);
|
||||
readRowUShort(input, height, srcRegion, flipped, xSub, ySub, rowDataUShort, destRaster, clippedRow, y);
|
||||
break;
|
||||
case 32:
|
||||
int[] rowDataInt = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
||||
readRowInt(input, height, srcRegion, flipped, xSub, ySub, rowDataInt, destRaster, clippedRow, y);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unsupported pixel depth: " + header.getPixelDepth());
|
||||
throw new AssertionError("Unsupported pixel depth: " + pixelDepth);
|
||||
}
|
||||
|
||||
processImageProgress(100f * y / height);
|
||||
@ -226,10 +243,26 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
return destination;
|
||||
}
|
||||
|
||||
private void readRowByte(final DataInput input, int height, Rectangle srcRegion, int origin, int xSub, int ySub,
|
||||
private boolean isOriginLowerLeft(final int origin) throws IIOException {
|
||||
switch (origin) {
|
||||
case TGA.ORIGIN_LOWER_LEFT:
|
||||
return true;
|
||||
case TGA.ORIGIN_UPPER_LEFT:
|
||||
return false;
|
||||
default:
|
||||
// Other orientations are not supported
|
||||
throw new IIOException("Unsupported origin: " + origin);
|
||||
}
|
||||
}
|
||||
|
||||
private void readRowByte(final DataInput input, int height, Rectangle srcRegion, boolean flip, int xSub, int ySub,
|
||||
byte[] rowDataByte, WritableRaster destChannel, Raster srcChannel, int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = flip ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (y % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataByte.length);
|
||||
|
||||
return;
|
||||
@ -250,19 +283,7 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
switch (origin) {
|
||||
case TGA.ORIGIN_LOWER_LEFT:
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
break;
|
||||
case TGA.ORIGIN_UPPER_LEFT:
|
||||
dstY = y / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
break;
|
||||
default:
|
||||
throw new IIOException("Unsupported origin: " + origin);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeAlpha32(final byte[] rowData) {
|
||||
@ -271,10 +292,14 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void readRowUShort(final DataInput input, int height, Rectangle srcRegion, int origin, int xSub, int ySub,
|
||||
private void readRowUShort(final DataInput input, int height, Rectangle srcRegion, boolean flip, int xSub, int ySub,
|
||||
short[] rowDataUShort, WritableRaster destChannel, Raster srcChannel, int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = flip ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (y % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataUShort.length * 2);
|
||||
|
||||
return;
|
||||
@ -289,19 +314,32 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
switch (origin) {
|
||||
case TGA.ORIGIN_LOWER_LEFT:
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
break;
|
||||
case TGA.ORIGIN_UPPER_LEFT:
|
||||
dstY = y / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
break;
|
||||
default:
|
||||
throw new IIOException("Unsupported origin: " + origin);
|
||||
}
|
||||
|
||||
private void readRowInt(final DataInput input, int height, Rectangle srcRegion, boolean flip, int xSub, int ySub,
|
||||
int[] rowDataInt, WritableRaster destChannel, Raster srcChannel, int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = flip ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataInt.length * 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
readFully(input, rowDataInt);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub; x < ((srcRegion.x + srcRegion.width) / xSub); x++) {
|
||||
rowDataInt[x] = rowDataInt[x * xSub];
|
||||
}
|
||||
}
|
||||
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
|
||||
// TODO: Candidate util method
|
||||
@ -317,6 +355,19 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Candidate util method
|
||||
private static void readFully(final DataInput input, final int[] ints) throws IOException {
|
||||
if (input instanceof ImageInputStream) {
|
||||
// Optimization for ImageInputStreams, read all in one go
|
||||
((ImageInputStream) input).readFully(ints, 0, ints.length);
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
ints[i] = input.readInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Raster clipRowToRect(final Raster raster, final Rectangle rect, final int[] bands, final int xSub) {
|
||||
if (rect.contains(raster.getMinX(), 0, raster.getWidth(), 1)
|
||||
&& xSub == 1
|
||||
@ -452,20 +503,26 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
// Thumbnail is always stored non-compressed, no need for RLE support
|
||||
imageInput.seek(extensions.getThumbnailOffset() + 2);
|
||||
|
||||
int pixelDepth = header.getPixelDepth();
|
||||
boolean flipped = isOriginLowerLeft(header.getOrigin());
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
switch (header.getPixelDepth()) {
|
||||
switch (pixelDepth) {
|
||||
case 8:
|
||||
case 24:
|
||||
case 32:
|
||||
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||
readRowByte(imageInput, height, srcRegion, header.getOrigin(), 1, 1, rowDataByte, destRaster, rowRaster, y);
|
||||
readRowByte(imageInput, height, srcRegion, flipped, 1, 1, rowDataByte, destRaster, rowRaster, y);
|
||||
break;
|
||||
case 16:
|
||||
short[] rowDataUShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
||||
readRowUShort(imageInput, height, srcRegion, header.getOrigin(), 1, 1, rowDataUShort, destRaster, rowRaster, y);
|
||||
readRowUShort(imageInput, height, srcRegion, flipped, 1, 1, rowDataUShort, destRaster, rowRaster, y);
|
||||
break;
|
||||
case 32:
|
||||
int[] rowDataInt = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
||||
readRowInt(imageInput, height, srcRegion, flipped, 1, 1, rowDataInt, destRaster, rowRaster, y);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unsupported pixel depth: " + header.getPixelDepth());
|
||||
throw new AssertionError("Unsupported pixel depth: " + pixelDepth);
|
||||
}
|
||||
|
||||
processThumbnailProgress(100f * y / height);
|
||||
|
@ -97,7 +97,6 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
||||
new TestData(getClassLoaderResource("/tiff/test-single-gray-compression-type-2.tiff"), new Dimension(1728, 1146)), // Gray, CCITT type 2 compressed
|
||||
new TestData(getClassLoaderResource("/tiff/cramps-tile.tif"), new Dimension(800, 607)), // Gray/WhiteIsZero, uncompressed, striped & tiled...
|
||||
new TestData(getClassLoaderResource("/tiff/lzw-long-strings-sample.tif"), new Dimension(316, 173)), // RGBA, LZW compressed w/predictor
|
||||
new TestData(getClassLoaderResource("/tiff/part.tif"), new Dimension(50, 50)), // Gray/BlackIsZero, uncompressed, striped signed int (SampleFormat 2)
|
||||
new TestData(getClassLoaderResource("/tiff/cmyk_jpeg_no_profile.tif"), new Dimension(150, 63)), // CMYK, JPEG compressed, no ICC profile
|
||||
new TestData(getClassLoaderResource("/tiff/cmyk_jpeg.tif"), new Dimension(100, 100)), // CMYK, JPEG compressed, with ICC profile
|
||||
new TestData(getClassLoaderResource("/tiff/grayscale-alpha.tiff"), new Dimension(248, 351)), // Gray + unassociated alpha
|
||||
@ -172,7 +171,52 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
||||
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.tif"), new Dimension(512, 512)), // Lossless JPEG RGB, 8 bit/sample
|
||||
// Custom PIXTIFF ZIP (Compression: 50013)
|
||||
new TestData(getClassLoaderResource("/tiff/pixtiff/40-8bit-gray-zip.tif"), new Dimension(801, 1313)) // ZIP Gray, 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/pixtiff/40-8bit-gray-zip.tif"), new Dimension(801, 1313)), // ZIP Gray, 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/part.tif"), new Dimension(50, 50)) // Gray/BlackIsZero, uncompressed, striped signed int (SampleFormat 2)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-02.tif"), new Dimension(73, 43)), // Gray 2 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-04.tif"), new Dimension(73, 43)), // Gray 4 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-06.tif"), new Dimension(73, 43)), // Gray 6 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-08.tif"), new Dimension(73, 43)), // Gray 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-10.tif"), new Dimension(73, 43)), // Gray 10 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-12.tif"), new Dimension(73, 43)), // Gray 12 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-14.tif"), new Dimension(73, 43)), // Gray 14 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-16.tif"), new Dimension(73, 43)), // Gray 16 bit/sample
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-24.tif"), new Dimension(73, 43)), // Gray 24 bit/sample
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-32.tif"), new Dimension(73, 43)), // Gray 32 bit/sample
|
||||
// Palette
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-02.tif"), new Dimension(73, 43)), // Palette 2 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-04.tif"), new Dimension(73, 43)), // Palette 4 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-08.tif"), new Dimension(73, 43)), // Palette 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-16.tif"), new Dimension(73, 43)), // Palette 16 bit/sample
|
||||
// RGB Interleaved (PlanarConfiguration: 1)
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-08.tif"), new Dimension(73, 43)), // RGB 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-10.tif"), new Dimension(73, 43)), // RGB 10 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-12.tif"), new Dimension(73, 43)), // RGB 12 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-14.tif"), new Dimension(73, 43)), // RGB 14 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-16.tif"), new Dimension(73, 43)), // RGB 16 bit/sample
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-24.tif"), new Dimension(73, 43)), // RGB 24 bit/sample
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-32.tif"), new Dimension(73, 43)), // RGB 32 bit/sample
|
||||
// RGB Planar (PlanarConfiguration: 2)
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-08.tif"), new Dimension(73, 43)) // RGB 8 bit/sample
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-10.tif"), new Dimension(73, 43)), // RGB 10 bit/sample
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-12.tif"), new Dimension(73, 43)), // RGB 12 bit/sample
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-14.tif"), new Dimension(73, 43)), // RGB 14 bit/sample
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-16.tif"), new Dimension(73, 43)), // RGB 16 bit/sample
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-24.tif"), new Dimension(73, 43)), // RGB 24 bit/sample
|
||||
// // RGB Interleaved Floating point..!? We can read this one, but the samples are not normalized, so colors are way too bright...
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-32.tif"), new Dimension(73, 43)), // RGB 32 bit FP samples (!)
|
||||
// // Separated (CMYK) Interleaved (PlanarConfiguration: 1)
|
||||
// new TestData(getClassLoaderResource("/tiff/depth/flower-separated-contig-08.tif"), new Dimension(73, 43)), // CMYK 8 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)
|
||||
// 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
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user