diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageReaderBase.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageReaderBase.java
old mode 100755
new mode 100644
index 5f36ff01..0b6b23ff
--- a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageReaderBase.java
+++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageReaderBase.java
@@ -31,9 +31,7 @@ package com.twelvemonkeys.imageio;
import com.twelvemonkeys.image.BufferedImageIcon;
import com.twelvemonkeys.imageio.util.IIOUtil;
-import javax.imageio.ImageIO;
-import javax.imageio.ImageReadParam;
-import javax.imageio.ImageReader;
+import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
@@ -46,6 +44,7 @@ import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
+import java.util.Iterator;
/**
* ImageReaderBase
@@ -191,6 +190,79 @@ public abstract class ImageReaderBase extends ImageReader {
}
}
+ /**
+ * Returns the {@code BufferedImage} to which decoded pixel
+ * data should be written.
+ *
+ * As {@link javax.imageio.ImageReader#getDestination} but tests if the explicit destination
+ * image (if set) is valid according to the {@code ImageTypeSpecifier}s given in {@code pTypes}
+ *
+ *
+ * @param pParam an {@code ImageReadParam} to be used to get
+ * the destination image or image type, or {@code null}.
+ * @param pTypes an {@code Iterator} of
+ * {@code ImageTypeSpecifier}s indicating the legal image
+ * types, with the default first.
+ * @param pWidth the true width of the image or tile begin decoded.
+ * @param pHeight the true width of the image or tile being decoded.
+ *
+ * @return the {@code BufferedImage} to which decoded pixel
+ * data should be written.
+ *
+ * @exception IIOException if the {@code ImageTypeSpecifier} or {@code BufferedImage}
+ * specified by {@code pParam} does not match any of the legal
+ * ones from {@code pTypes}.
+ * @throws IllegalArgumentException if {@code pTypes}
+ * is {@code null} or empty, or if an object not of type
+ * {@code ImageTypeSpecifier} is retrieved from it.
+ * Or, if the resulting image would
+ * have a width or height less than 1,
+ * or if the product of
+ * {@code pWidth} and {@code pHeight} is greater than
+ * {@code Integer.MAX_VALUE}.
+ */
+ public static BufferedImage getDestination(final ImageReadParam pParam, final Iterator pTypes,
+ final int pWidth, final int pHeight) throws IIOException {
+ BufferedImage image = ImageReader.getDestination(pParam, pTypes, pWidth, pHeight);
+
+ if (pParam != null) {
+ BufferedImage dest = pParam.getDestination();
+ if (dest != null) {
+ boolean found = false;
+
+ // NOTE: This is bad, as it relies on implementation details of super method...
+ // We know that the iterator has not been touched if explicit destination..
+ while (pTypes.hasNext()) {
+ ImageTypeSpecifier specifier = pTypes.next();
+ int imageType = specifier.getBufferedImageType();
+
+ if (imageType != 0 && imageType == dest.getType()) {
+ // Known types equal, perfect match
+ found = true;
+ break;
+ }
+ else {
+ // If types are different, or TYPE_CUSTOM, test if
+ // - transferType is ok
+ // - bands are ok
+ // TODO: Test if color model is ok?
+ if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType() &&
+ specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ throw new IIOException(String.format("Illegal explicit destination image %s", dest));
+ }
+ }
+ }
+
+ return image;
+ }
+
/**
* Utility method for getting the area of interest (AOI) of an image.
* The AOI is defined by the {@link javax.imageio.IIOParam#setSourceRegion(java.awt.Rectangle)}
diff --git a/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageReaderAbstractTestCase.java b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageReaderAbstractTestCase.java
index 84909fe7..776c768d 100644
--- a/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageReaderAbstractTestCase.java
+++ b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageReaderAbstractTestCase.java
@@ -540,14 +540,14 @@ public abstract class ImageReaderAbstractTestCase extends
}
- public void readAsRenderedImageIndexNegative() {
+ public void testReadAsRenderedImageIndexNegative() {
ImageReader reader = createReader();
TestData data = getTestData().get(0);
reader.setInput(data.getInputStream());
- BufferedImage image = null;
+ RenderedImage image = null;
try {
- image = reader.read(-1, reader.getDefaultReadParam());
+ image = reader.readAsRenderedImage(-1, reader.getDefaultReadParam());
fail("Read image with illegal index");
}
catch (IndexOutOfBoundsException expected) {
@@ -559,14 +559,14 @@ public abstract class ImageReaderAbstractTestCase extends
assertNull(image);
}
- public void readAsRenderedImageIndexOutOfBounds() {
+ public void testReadAsRenderedImageIndexOutOfBounds() {
ImageReader reader = createReader();
TestData data = getTestData().get(0);
reader.setInput(data.getInputStream());
- BufferedImage image = null;
+ RenderedImage image = null;
try {
- image = reader.read(11, reader.getDefaultReadParam());
+ image = reader.readAsRenderedImage(reader.getNumImages(true), reader.getDefaultReadParam());
fail("Read image with index out of bounds");
}
catch (IndexOutOfBoundsException expected) {
@@ -578,7 +578,7 @@ public abstract class ImageReaderAbstractTestCase extends
assertNull(image);
}
- public void readAsRenderedImageNoInput() {
+ public void testReadAsRenderedImageNoInput() {
ImageReader reader = createReader();
// Do not set input
@@ -596,7 +596,7 @@ public abstract class ImageReaderAbstractTestCase extends
assertNull(image);
}
- public void readAsRenderedImage() {
+ public void testReadAsRenderedImage() {
ImageReader reader = createReader();
TestData data = getTestData().get(0);
reader.setInput(data.getInputStream());
@@ -615,7 +615,7 @@ public abstract class ImageReaderAbstractTestCase extends
data.getDimension(0).height, image.getHeight());
}
- public void readAsRenderedImageWithDefaultParam() {
+ public void testReadAsRenderedImageWithDefaultParam() {
ImageReader reader = createReader();
TestData data = getTestData().get(0);
reader.setInput(data.getInputStream());
@@ -1181,28 +1181,93 @@ public abstract class ImageReaderAbstractTestCase extends
assertSame(destination, result);
}
- // TODO: This test is foobar..
public void testSetDestinationIllegal() throws IOException {
- // TODO: Test that the reader throws IIOException if given an illegal destination
final ImageReader reader = createReader();
TestData data = getTestData().get(0);
reader.setInput(data.getInputStream());
- Iterator types = reader.getImageTypes(0);
+
+ List illegalTypes = createIllegalTypes(reader.getImageTypes(0));
ImageReadParam param = reader.getDefaultReadParam();
- // TODO: Should either be a type from image type specifiers or throw IIOException in read
- BufferedImage destination = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
- param.setDestination(destination);
+ for (ImageTypeSpecifier illegalType : illegalTypes) {
+ BufferedImage destination = illegalType.createBufferedImage(50, 50);
+ param.setDestination(destination);
- try {
- reader.read(0, param);
- fail("Expected to throw exception with wrong type specifier");
+ try {
+ reader.read(0, param);
+
+ // NOTE: We allow the reader to read, as it's inconvenient to test all possible cases.
+ // However, it may NOT fail with any other exception in that case.
+ System.err.println("WARNING: Reader does not throw exception with non-declared destination: " + destination);
+ }
+ catch (IIOException expected) {
+ // TODO: This is thrown by ImageReader.getDestination. But are we happy with that?
+ // The problem is that the checkReadParamBandSettings throws IllegalArgumentException, which seems more appropriate...
+ String message = expected.getMessage();
+ assertTrue("Wrong message: " + message, message.toLowerCase().contains("destination"));
+ }
+ catch (IllegalArgumentException expected) {
+ String message = expected.getMessage();
+ assertTrue("Wrong message: " + message, message.toLowerCase().contains("dest"));
+ }
}
- catch (IIOException e) {
- assertTrue(e.getMessage().toLowerCase().contains("type"));
+ }
+
+ public void testSetDestinationTypeIllegal() throws IOException {
+ final ImageReader reader = createReader();
+ TestData data = getTestData().get(0);
+ reader.setInput(data.getInputStream());
+
+ List illegalTypes = createIllegalTypes(reader.getImageTypes(0));
+
+ ImageReadParam param = reader.getDefaultReadParam();
+ for (ImageTypeSpecifier illegalType : illegalTypes) {
+ param.setDestinationType(illegalType);
+
+ try {
+ reader.read(0, param);
+ fail("Expected to throw exception with illegal type specifier");
+ }
+ catch (IIOException expected) {
+ // TODO: This is thrown by ImageReader.getDestination. But are we happy with that?
+ String message = expected.getMessage();
+ assertTrue(message.toLowerCase().contains("destination"));
+ assertTrue(message.toLowerCase().contains("type"));
+ }
+ catch (IllegalArgumentException expected) {
+ String message = expected.getMessage();
+ assertTrue(message.toLowerCase().contains("destination"));
+ assertTrue(message.toLowerCase().contains("type"));
+ }
}
}
+ private List createIllegalTypes(Iterator pValidTypes) {
+ List allTypes = new ArrayList();
+ for (int i = BufferedImage.TYPE_INT_RGB; i < BufferedImage.TYPE_BYTE_INDEXED; i++) {
+ allTypes.add(ImageTypeSpecifier.createFromBufferedImageType(i));
+ }
+
+ List illegalTypes = new ArrayList(allTypes);
+ while (pValidTypes.hasNext()) {
+ ImageTypeSpecifier valid = pValidTypes.next();
+ boolean removed = illegalTypes.remove(valid);
+
+ // TODO: 4BYTE_ABGR (6) and 4BYTE_ABGR_PRE (7) is essentially the same type...
+ // !#$#§%$! ImageTypeSpecifier.equals is not well-defined
+ if (!removed) {
+ for (Iterator iterator = illegalTypes.iterator(); iterator.hasNext();) {
+ ImageTypeSpecifier illegalType = iterator.next();
+ if (illegalType.getBufferedImageType() == valid.getBufferedImageType()) {
+ iterator.remove();
+ }
+ }
+ }
+ }
+
+ return illegalTypes;
+ }
+
// TODO: Test dest offset + destination set?
public void testSetDestinationOffset() throws IOException {
final ImageReader reader = createReader();
@@ -1266,18 +1331,17 @@ public abstract class ImageReaderAbstractTestCase extends
}
}
- public void testSetDestinationTypeIllegal() throws IOException {
- throw new UnsupportedOperationException("Method testSetDestinationTypeIllegal not implemented"); // TODO: Implement
- }
-
- public void testSetDestinationBands() throws IOException {
- throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement
- }
-
- public void testSetSourceBands() throws IOException {
- throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement
- }
-
+// public void testSetDestinationTypeIllegal() throws IOException {
+// throw new UnsupportedOperationException("Method testSetDestinationTypeIllegal not implemented"); // TODO: Implement
+// }
+//
+// public void testSetDestinationBands() throws IOException {
+// throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement
+// }
+//
+// public void testSetSourceBands() throws IOException {
+// throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement
+// }
protected URL getClassLoaderResource(final String pName) {
return getClass().getResource(pName);
diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java
old mode 100755
new mode 100644
index 9e3503fb..7be3ab71
--- a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java
+++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java
@@ -292,10 +292,9 @@ public class ICOImageReader extends ImageReaderBase {
}
private BufferedImage readBitmap(final DirectoryEntry pEntry) throws IOException {
- // TODO: Currently, we have a memory leak, as the values refer to the keys...
BitmapDescriptor descriptor = mDescriptors.get(pEntry);
- if (!mDescriptors.containsKey(pEntry)) {
+ if (descriptor == null || !mDescriptors.containsKey(pEntry)) {
DIBHeader header = getHeader(pEntry);
int offset = pEntry.getOffset() + header.getSize();
diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java
index 4e246ef2..bce32188 100644
--- a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java
+++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java
@@ -2604,6 +2604,7 @@ public class PICTImageReader extends ImageReaderBase {
processImageStarted(pIndex);
// TODO: Param handling
+ // TODO: Real subsampling for bit/pixmap/QT stills
final int subX, subY;
if (pParam != null) {
subX = pParam.getSourceXSubsampling();
diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java
index 1c81c24b..98c6e01d 100644
--- a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java
+++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java
@@ -276,11 +276,9 @@ public class PSDImageReader extends ImageReaderBase {
readImageResources(false);
readLayerAndMaskInfo(false);
- // TODO: Test if explicit destination is compatible or throw IllegalArgumentException
BufferedImage image = getDestination(pParam, getImageTypes(pIndex), mHeader.mWidth, mHeader.mHeight);
ImageTypeSpecifier rawType = getRawImageType(pIndex);
-
- processImageStarted(pIndex);
+ checkReadParamBandSettings(pParam, rawType.getNumBands(), image.getSampleModel().getNumBands());
final Rectangle source = new Rectangle();
final Rectangle dest = new Rectangle();
@@ -293,7 +291,7 @@ public class PSDImageReader extends ImageReaderBase {
// TODO: Create temp raster in native format w * 1
// Read (sub-sampled) row into temp raster (skip other rows)
// If color model (color space) is not RGB, do color convert op
- // Otherwise, copy "through" ColorMode?l
+ // Otherwise, copy "through" ColorModel?
// Copy pixels from temp raster
// If possible, leave the destination image "untouched" (accelerated)
@@ -322,6 +320,8 @@ public class PSDImageReader extends ImageReaderBase {
ySub = pParam.getSourceYSubsampling();
}
+ processImageStarted(pIndex);
+
int[] offsets = null;
int compression = mImageInput.readShort();
@@ -342,7 +342,12 @@ public class PSDImageReader extends ImageReaderBase {
// Could be same as PNG prediction? Read up...
throw new IIOException("ZIP compression not supported yet");
default:
- throw new IIOException("Unknown compression type: " + compression);
+ throw new IIOException(
+ String.format(
+ "Unknown PSD compression: %d. Expected 0 (none), 1 (RLE), 2 (ZIP) or 3 (ZIP w/prediction).",
+ compression
+ )
+ );
}
// What we read here is the "composite layer" of the PSD file
@@ -532,7 +537,8 @@ public class PSDImageReader extends ImageReaderBase {
final byte[] pRow,
final Rectangle pSource, final Rectangle pDest, final int pXSub, final int pYSub,
final int[] pRowOffsets, boolean pRLECompressed) throws IOException {
-
+ // NOTE: 1 bit channels only occurs once
+
final int destWidth = (pDest.width + 7) / 8;
for (int y = 0; y < mHeader.mHeight; y++) {
@@ -878,7 +884,7 @@ public class PSDImageReader extends ImageReaderBase {
@Override
public BufferedImage readThumbnail(int pImageIndex, int pThumbnailIndex) throws IOException {
- // TODO: Thumbnail listeners...
+ // TODO: Thumbnail progress listeners...
PSDThumbnail thumbnail = getThumbnailResource(pImageIndex, pThumbnailIndex);
// TODO: Defer decoding