diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS.java
index 12309f6c..2ec75bcc 100644
--- a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS.java
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS.java
@@ -96,9 +96,12 @@ interface ICNS {
/** 1024×1024 PNG icon (10.7+)*/
int ic10 = ('i' << 24) + ('c' << 16) + ('1' << 8) + '0';
- /** Unknown */
+ /** Unknown (Version) */
int icnV = ('i' << 24) + ('c' << 16) + ('n' << 8) + 'V';
+ /** Unknown (Table of Contents) */
+ int TOC_ = ('T' << 24) + ('O' << 16) + ('C' << 8) + ' ';
+
/** JPEG 2000 magic header */
byte[] JPEG_2000_MAGIC = new byte[] {0x00, 0x00, 0x00, 0x0C, 'j', 'P', 0x20, 0x20, 0x0D, 0x0A, (byte) 0x87, 0x0A};
diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReader.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReader.java
index 6a585f4e..388b8f96 100644
--- a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReader.java
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReader.java
@@ -59,9 +59,14 @@ import java.util.List;
* @see Apple Icon Image format (Wikipedia)
*/
public final class ICNSImageReader extends ImageReaderBase {
- private static final int HEADER_SIZE = 8;
- private List icons = new ArrayList();
- private List masks = new ArrayList();
+ // TODO: Support ToC resource for faster parsing/faster determine number of icons?
+ // TODO: Subsampled reading for completeness, even if never used?
+
+ private static final int RESOURCE_HEADER_SIZE = 8;
+
+ private List icons = new ArrayList();
+ private List masks = new ArrayList();
+ private IconResource lastResourceRead;
private int length;
@@ -76,26 +81,27 @@ public final class ICNSImageReader extends ImageReaderBase {
@Override
protected void resetMembers() {
length = 0;
-
+
+ lastResourceRead = null;
icons.clear();
masks.clear();
}
@Override
public int getWidth(int imageIndex) throws IOException {
- return readIconHeader(imageIndex).size().width;
+ return readIconResource(imageIndex).size().width;
}
@Override
public int getHeight(int imageIndex) throws IOException {
- return readIconHeader(imageIndex).size().height;
+ return readIconResource(imageIndex).size().height;
}
@Override
public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException {
- IconHeader header = readIconHeader(imageIndex);
+ IconResource resource = readIconResource(imageIndex);
- switch (header.depth()) {
+ switch (resource.depth()) {
case 1:
return IndexedImageTypeSpecifier.createFromIndexColorModel(ICNS1BitColorModel.INSTANCE);
case 4:
@@ -103,13 +109,22 @@ public final class ICNSImageReader extends ImageReaderBase {
case 8:
return IndexedImageTypeSpecifier.createFromIndexColorModel(ICNS8BitColorModel.INSTANCE);
case 32:
- return ImageTypeSpecifier.createBanded(
- ColorSpace.getInstance(ColorSpace.CS_sRGB),
- new int[]{0, 1, 2, 3}, createBandOffsets(header.size().width * header.size().height),
- DataBuffer.TYPE_BYTE, true, false
- );
+ if (resource.isCompressed()) {
+ return ImageTypeSpecifier.createBanded(
+ ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ new int[]{0, 1, 2, 3}, createBandOffsets(resource.size().width * resource.size().height),
+ DataBuffer.TYPE_BYTE, true, false
+ );
+ }
+ else {
+ return ImageTypeSpecifier.createInterleaved(
+ ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ new int[]{1, 2, 3, 0},
+ DataBuffer.TYPE_BYTE, true, false
+ );
+ }
default:
- throw new IllegalStateException(String.format("Unknown bit depth: %d", header.depth()));
+ throw new IllegalStateException(String.format("Unknown bit depth: %d", resource.depth()));
}
}
@@ -120,11 +135,11 @@ public final class ICNSImageReader extends ImageReaderBase {
@Override
public Iterator getImageTypes(int imageIndex) throws IOException {
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
- IconHeader header = readIconHeader(imageIndex);
+ IconResource resource = readIconResource(imageIndex);
List specifiers = new ArrayList();
- switch (header.depth()) {
+ switch (resource.depth()) {
case 1:
case 4:
case 8:
@@ -134,7 +149,7 @@ public final class ICNSImageReader extends ImageReaderBase {
specifiers.add(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false));
break;
default:
- throw new IllegalStateException(String.format("Unknown bit depth: %d", header.depth()));
+ throw new IllegalStateException(String.format("Unknown bit depth: %d", resource.depth()));
}
specifiers.add(rawType);
@@ -154,7 +169,7 @@ public final class ICNSImageReader extends ImageReaderBase {
int num = icons.size();
while (true) {
try {
- readIconHeader(num++);
+ readIconResource(num++);
}
catch (IndexOutOfBoundsException expected) {
break;
@@ -166,20 +181,20 @@ public final class ICNSImageReader extends ImageReaderBase {
@Override
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
- IconHeader header = readIconHeader(imageIndex);
+ IconResource resource = readIconResource(imageIndex);
- imageInput.seek(header.start + HEADER_SIZE);
+ imageInput.seek(resource.start + RESOURCE_HEADER_SIZE);
// Special handling of PNG/JPEG 2000 icons
- if (header.isForeignFormat()) {
- return readForeignFormat(param, header);
+ if (resource.isForeignFormat()) {
+ return readForeignFormat(param, resource);
}
- return readICNSFormat(imageIndex, param, header);
+ return readICNSFormat(imageIndex, param, resource);
}
- private BufferedImage readICNSFormat(final int imageIndex, final ImageReadParam param, final IconHeader header) throws IOException {
- Dimension size = header.size();
+ private BufferedImage readICNSFormat(final int imageIndex, final ImageReadParam param, final IconResource resource) throws IOException {
+ Dimension size = resource.size();
int width = size.width;
int height = size.height;
@@ -202,11 +217,11 @@ public final class ICNSImageReader extends ImageReaderBase {
// Read image data
byte[] data;
- if (header.isCompressed()) {
+ if (resource.isCompressed()) {
// Only 32 bit icons may be compressed
- data = new byte[width * height * header.depth() / 8];
+ data = new byte[width * height * resource.depth() / 8];
- int packedSize = header.length - HEADER_SIZE;
+ int packedSize = resource.length - RESOURCE_HEADER_SIZE;
if (width >= 128 && height >= 128) {
// http://www.macdisk.com/maciconen.php:
@@ -225,14 +240,14 @@ public final class ICNSImageReader extends ImageReaderBase {
}
}
else {
- data = new byte[header.length - HEADER_SIZE];
+ data = new byte[resource.length - RESOURCE_HEADER_SIZE];
imageInput.readFully(data);
}
- if (header.depth() == 1) {
+ if (resource.depth() == 1) {
// Binary
DataBufferByte buffer = new DataBufferByte(data, data.length / 2, 0);
- WritableRaster raster = Raster.createPackedRaster(buffer, width, height, header.depth(), null);
+ WritableRaster raster = Raster.createPackedRaster(buffer, width, height, resource.depth(), null);
if (image.getType() == rawType.getBufferedImageType() && ((IndexColorModel) image.getColorModel()).getMapSize() == 2) {
// Preserve raw data as read (binary), discard mask
@@ -241,7 +256,7 @@ public final class ICNSImageReader extends ImageReaderBase {
else {
// Convert to 32 bit ARGB
DataBufferByte maskBuffer = new DataBufferByte(data, data.length / 2, data.length / 2);
- WritableRaster mask = Raster.createPackedRaster(maskBuffer, width, height, header.depth(), null);
+ WritableRaster mask = Raster.createPackedRaster(maskBuffer, width, height, resource.depth(), null);
Graphics2D graphics = image.createGraphics();
@@ -261,10 +276,10 @@ public final class ICNSImageReader extends ImageReaderBase {
}
}
}
- else if (header.depth() <= 8) {
+ else if (resource.depth() <= 8) {
// Indexed
DataBufferByte buffer = new DataBufferByte(data, data.length);
- WritableRaster raster = Raster.createPackedRaster(buffer, width, height, header.depth(), null);
+ WritableRaster raster = Raster.createPackedRaster(buffer, width, height, resource.depth(), null);
if (image.getType() == rawType.getBufferedImageType()) {
// Preserve raw data as read (indexed), discard mask
@@ -285,8 +300,12 @@ public final class ICNSImageReader extends ImageReaderBase {
processImageProgress(50f);
// Read mask and apply
- Raster mask = readMask(findMask(header));
- image.getAlphaRaster().setRect(mask);
+ IconResource maskResource = findMaskResource(resource);
+
+ if (maskResource != null) {
+ Raster mask = readMask(maskResource);
+ image.getAlphaRaster().setRect(mask);
+ }
}
}
else {
@@ -294,14 +313,35 @@ public final class ICNSImageReader extends ImageReaderBase {
int bandLen = data.length / 4;
DataBufferByte buffer = new DataBufferByte(data, data.length);
- WritableRaster raster = Raster.createBandedRaster(buffer, width, height, width, new int[]{0, 0, 0, 0}, createBandOffsets(bandLen), null);
+
+ WritableRaster raster;
+
+ if (resource.isCompressed()) {
+ raster = Raster.createBandedRaster(buffer, width, height, width, new int[]{0, 0, 0, 0}, createBandOffsets(bandLen), null);
+ }
+ else {
+ // NOTE: Uncompressed 32bit is interleaved RGBA, not banded...
+ raster = Raster.createInterleavedRaster(buffer, width, height, width * 4, 4, new int[]{1, 2, 3, 0}, null);
+ }
+
image.setData(raster);
processImageProgress(75f);
// Read mask and apply
- Raster mask = readMask(findMask(header));
- image.getAlphaRaster().setRect(mask);
+ IconResource maskResource = findMaskResource(resource);
+
+ if (maskResource != null) {
+ Raster mask = readMask(maskResource);
+ image.getAlphaRaster().setRect(mask);
+ }
+ else {
+ // TODO: This is simply stupid. Rewrite to use no alpha instead?
+ byte[] solid = new byte[width * height];
+ Arrays.fill(solid, (byte) -1);
+ WritableRaster mask = Raster.createBandedRaster(new DataBufferByte(solid, solid.length), width, height, width, new int[]{0}, new int[]{0}, null);
+ image.getAlphaRaster().setRect(mask);
+ }
}
// For now: Make listener tests happy
@@ -318,28 +358,52 @@ public final class ICNSImageReader extends ImageReaderBase {
return image;
}
- private Raster readMask(IconHeader header) throws IOException {
- Dimension size = header.size();
+ private Raster readMask(final IconResource resource) throws IOException {
+ Dimension size = resource.size();
int width = size.width;
int height = size.height;
- byte[] alpha = new byte[header.length - HEADER_SIZE];
+ byte[] mask = new byte[width * height];
+ imageInput.seek(resource.start + RESOURCE_HEADER_SIZE);
- imageInput.seek(header.start + HEADER_SIZE);
- imageInput.readFully(alpha);
+ if (resource.isMaskType()) {
+ // 8 bit mask
+ imageInput.readFully(mask, 0, resource.length - RESOURCE_HEADER_SIZE);
+ }
+ else if (resource.hasMask()) {
+ // Embedded 1bit mask
+ byte[] maskData = new byte[(resource.length - RESOURCE_HEADER_SIZE) / 2];
+ imageInput.skipBytes(maskData.length); // Skip the 1 bit image data
+ imageInput.readFully(maskData);
- return Raster.createBandedRaster(new DataBufferByte(alpha, alpha.length), width, height, width, new int[]{0}, new int[]{0}, null);
+ // Unpack 1bit mask to 8 bit
+ int bitPos = 0x80;
+
+ for (int i = 0, maskLength = mask.length; i < maskLength; i++) {
+ mask[i] = (byte) ((maskData[i / 8] & bitPos) != 0 ? 0xff : 0x00);
+
+ if ((bitPos >>= 1) == 0) {
+ bitPos = 0x80;
+ }
+ }
+ }
+ else {
+ throw new IllegalArgumentException(String.format("Not a mask resource: %s", resource));
+ }
+
+ return Raster.createBandedRaster(new DataBufferByte(mask, mask.length), width, height, width, new int[]{0}, new int[]{0}, null);
}
- private IconHeader findMask(final IconHeader icon) throws IOException {
+ private IconResource findMaskResource(final IconResource iconResource) throws IOException {
+ // Find 8 bit mask
try {
int i = 0;
while (true) {
- IconHeader mask = i < masks.size() ? masks.get(i++) : readNextIconHeader();
+ IconResource mask = i < masks.size() ? masks.get(i++) : readNextIconResource();
- if (mask.isMask() && mask.size().equals(icon.size())) {
+ if (mask.isMaskType() && mask.size().equals(iconResource.size())) {
return mask;
}
}
@@ -347,11 +411,18 @@ public final class ICNSImageReader extends ImageReaderBase {
catch (IndexOutOfBoundsException ignore) {
}
- throw new IIOException(String.format("No mask for icon: %s", icon));
+ // Fall back to mask from 1 bit resource if no 8 bit mask
+ for (IconResource resource : icons) {
+ if (resource.hasMask() && resource.size().equals(iconResource.size())) {
+ return resource;
+ }
+ }
+
+ return null;
}
- private BufferedImage readForeignFormat(final ImageReadParam param, final IconHeader header) throws IOException {
- ImageInputStream stream = ImageIO.createImageInputStream(IIOUtil.createStreamAdapter(imageInput, header.length));
+ private BufferedImage readForeignFormat(final ImageReadParam param, final IconResource resource) throws IOException {
+ ImageInputStream stream = ImageIO.createImageInputStream(IIOUtil.createStreamAdapter(imageInput, resource.length));
try {
Iterator readers = ImageIO.getImageReaders(stream);
@@ -364,9 +435,12 @@ public final class ICNSImageReader extends ImageReaderBase {
return reader.read(0, param);
}
catch (IOException ignore) {
- }
- finally {
- stream.seek(0);
+ if (stream.getFlushedPosition() <= 0) {
+ stream.seek(0);
+ }
+ else {
+ stream = ImageIO.createImageInputStream(IIOUtil.createStreamAdapter(imageInput, resource.length));
+ }
}
}
@@ -377,7 +451,7 @@ public final class ICNSImageReader extends ImageReaderBase {
// TODO: Create JPEG 2000 reader..? :-P
throw new IIOException(String.format(
"Cannot read %s format in type '%s' icon (no reader; installed: %s)",
- getForeignFormat(stream), ICNSUtil.intToStr(header.type), Arrays.toString(ImageIO.getReaderFormatNames())
+ getForeignFormat(stream), ICNSUtil.intToStr(resource.type), Arrays.toString(ImageIO.getReaderFormatNames())
));
}
finally {
@@ -454,25 +528,19 @@ public final class ICNSImageReader extends ImageReaderBase {
}
}
- private IconHeader readIconHeader(final int imageIndex) throws IOException {
+ private IconResource readIconResource(final int imageIndex) throws IOException {
checkBounds(imageIndex);
readeFileHeader();
while (icons.size() <= imageIndex) {
- readNextIconHeader();
+ readNextIconResource();
}
return icons.get(imageIndex);
}
- private IconHeader readNextIconHeader() throws IOException {
- IconHeader lastIcon = icons.isEmpty() ? null : icons.get(icons.size() - 1);
- IconHeader lastMask = masks.isEmpty() ? null : masks.get(masks.size() - 1);
-
- long lastReadPos = Math.max(
- lastIcon == null ? HEADER_SIZE : lastIcon.start + lastIcon.length,
- lastMask == null ? HEADER_SIZE : lastMask.start + lastMask.length
- );
+ private IconResource readNextIconResource() throws IOException {
+ long lastReadPos = lastResourceRead == null ? RESOURCE_HEADER_SIZE : lastResourceRead.start + lastResourceRead.length;
imageInput.seek(lastReadPos);
@@ -480,17 +548,20 @@ public final class ICNSImageReader extends ImageReaderBase {
throw new IndexOutOfBoundsException();
}
- IconHeader header = IconHeader.read(imageInput);
+ IconResource resource = IconResource.read(imageInput);
+// System.err.println("resource: " + resource);
- // Filter out special case icnV (version?), as this isn't really an icon..
- if (header.isMask() || header.type == ICNS.icnV) {
- masks.add(header);
+ lastResourceRead = resource;
+
+ // Filter out special cases like 'icnV' or 'TOC ' resources
+ if (resource.isMaskType()) {
+ masks.add(resource);
}
- else {
- icons.add(header);
+ else if (!resource.isUnknownType()) {
+ icons.add(resource);
}
- return header;
+ return resource;
}
private void readeFileHeader() throws IOException {
@@ -507,13 +578,13 @@ public final class ICNSImageReader extends ImageReaderBase {
}
}
- // TODO: Rewrite using subclasses!
- static final class IconHeader {
+ // TODO: Rewrite using subclasses/instances!
+ static final class IconResource {
private final long start;
private final int type;
private final int length;
- IconHeader(long start, int type, int length) {
+ IconResource(long start, int type, int length) {
validate(type, length);
this.start = start;
@@ -521,8 +592,8 @@ public final class ICNSImageReader extends ImageReaderBase {
this.length = length;
}
- public static IconHeader read(ImageInputStream input) throws IOException {
- return new IconHeader(input.getStreamPosition(), input.readInt(), input.readInt());
+ public static IconResource read(ImageInputStream input) throws IOException {
+ return new IconResource(input.getStreamPosition(), input.readInt(), input.readInt());
}
private void validate(int type, int length) {
@@ -534,7 +605,7 @@ public final class ICNSImageReader extends ImageReaderBase {
validateLengthForType(type, length, 256);
break;
case ICNS.icm_:
- validateLengthForType(type, length, 24);
+ validateLengthForType(type, length, 48);
break;
case ICNS.icm4:
validateLengthForType(type, length, 96);
@@ -579,25 +650,28 @@ public final class ICNSImageReader extends ImageReaderBase {
case ICNS.ic08:
case ICNS.ic09:
case ICNS.ic10:
- if (length > 0) {
+ if (length > RESOURCE_HEADER_SIZE) {
break;
}
throw new IllegalArgumentException(String.format("Wrong combination of icon type '%s' and length: %d", ICNSUtil.intToStr(type), length));
case ICNS.icnV:
validateLengthForType(type, length, 4);
break;
+ case ICNS.TOC_:
default:
- throw new IllegalStateException(String.format("Unknown icon type: '%s'", ICNSUtil.intToStr(type)));
+ if (length > RESOURCE_HEADER_SIZE) {
+ break;
+ }
+ throw new IllegalStateException(String.format("Unknown icon type: '%s' length: %d", ICNSUtil.intToStr(type), length));
}
-
}
private void validateLengthForType(int type, int length, final int expectedLength) {
Validate.isTrue(
- length == expectedLength + HEADER_SIZE, // Compute to make lengths more logical
+ length == expectedLength + RESOURCE_HEADER_SIZE, // Compute to make lengths more logical
String.format(
"Wrong combination of icon type '%s' and length: %d (expected: %d)",
- ICNSUtil.intToStr(type), length - HEADER_SIZE, expectedLength
+ ICNSUtil.intToStr(type), length - RESOURCE_HEADER_SIZE, expectedLength
)
);
}
@@ -677,7 +751,52 @@ public final class ICNSImageReader extends ImageReaderBase {
}
}
- public boolean isMask() {
+ public boolean isUnknownType() {
+ // These should simply be skipped
+ switch (type) {
+ case ICNS.ICON:
+ case ICNS.ICN_:
+ case ICNS.icm_:
+ case ICNS.ics_:
+ case ICNS.ich_:
+ case ICNS.icm4:
+ case ICNS.ics4:
+ case ICNS.icl4:
+ case ICNS.ich4:
+ case ICNS.icm8:
+ case ICNS.ics8:
+ case ICNS.icl8:
+ case ICNS.ich8:
+ case ICNS.s8mk:
+ case ICNS.l8mk:
+ case ICNS.h8mk:
+ case ICNS.t8mk:
+ case ICNS.is32:
+ case ICNS.il32:
+ case ICNS.ih32:
+ case ICNS.it32:
+ case ICNS.ic08:
+ case ICNS.ic09:
+ case ICNS.ic10:
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean hasMask() {
+ switch (type) {
+ case ICNS.ICN_:
+ case ICNS.icm_:
+ case ICNS.ics_:
+ case ICNS.ich_:
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean isMaskType() {
switch (type) {
case ICNS.s8mk:
case ICNS.l8mk:
@@ -698,7 +817,7 @@ public final class ICNSImageReader extends ImageReaderBase {
// http://www.macdisk.com/maciconen.php
// "One should check whether the data length corresponds to the theoretical length (width * height)."
Dimension size = size();
- if (length != size.width * size.height) {
+ if (length != (size.width * size.height * depth() / 8 + RESOURCE_HEADER_SIZE)) {
return true;
}
}
@@ -723,16 +842,16 @@ public final class ICNSImageReader extends ImageReaderBase {
@Override
public boolean equals(Object other) {
- return other == this || other != null && other.getClass() == getClass() && isEqual((IconHeader) other);
+ return other == this || other != null && other.getClass() == getClass() && isEqual((IconResource) other);
}
- private boolean isEqual(IconHeader other) {
+ private boolean isEqual(IconResource other) {
return start == other.start && type == other.type && length == other.length;
}
@Override
public String toString() {
- return String.format("%s['%s' start: %d, length: %d]", getClass().getSimpleName(), ICNSUtil.intToStr(type), start, length);
+ return String.format("%s['%s' start: %d, length: %d%s]", getClass().getSimpleName(), ICNSUtil.intToStr(type), start, length, isCompressed() ? " (compressed)" : "");
}
}
@@ -746,22 +865,50 @@ public final class ICNSImageReader extends ImageReaderBase {
requested = Integer.parseInt(args[argIndex++]);
}
- File input = new File(args[argIndex++]);
+ int imagesRead = 0;
+ int imagesSkipped = 0;
ImageReader reader = new ICNSImageReader();
- reader.setInput(ImageIO.createImageInputStream(input));
- int start = requested != -1 ? requested : 0;
- int numImages = requested != -1 ? requested + 1 : reader.getNumImages(true);
- for (int i = start; i < numImages; i++) {
- try {
- BufferedImage image = reader.read(i);
-// System.err.println("image: " + image);
- showIt(image, String.format("%s - %d", input.getName(), i));
+ while(argIndex < args.length) {
+ File input = new File(args[argIndex++]);
+ ImageInputStream stream = ImageIO.createImageInputStream(input);
+
+ if (stream == null) {
+ System.err.printf("Cannot read: %s\n", input.getAbsolutePath());
+ continue;
}
- catch (IIOException e) {
+
+ try {
+ reader.setInput(stream);
+
+ int start = requested != -1 ? requested : 0;
+ int numImages = requested != -1 ? requested + 1 : reader.getNumImages(true);
+ for (int i = start; i < numImages; i++) {
+ try {
+ BufferedImage image = reader.read(i);
+ imagesRead++;
+// System.err.println("image: " + image);
+ showIt(image, String.format("%s - %d", input.getName(), i));
+ }
+ catch (IOException e) {
+ imagesSkipped++;
+ if (e.getMessage().contains("JPEG 2000")) {
+// System.err.printf("%s: %s\n", input, e.getMessage());
+ }
+ else {
+ System.err.printf("%s: ", input);
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ System.err.printf("%s: ", input);
e.printStackTrace();
}
}
+
+ System.err.printf("Read %s images (%d skipped) in %d files\n", imagesRead, imagesSkipped, args.length);
}
private static final class ICNSBitMaskColorModel extends IndexColorModel {
diff --git a/imageio/imageio-icns/src/test/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderTest.java b/imageio/imageio-icns/src/test/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderTest.java
index c31da2bd..36b92573 100644
--- a/imageio/imageio-icns/src/test/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderTest.java
+++ b/imageio/imageio-icns/src/test/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderTest.java
@@ -50,10 +50,10 @@ public class ICNSImageReaderTest extends ImageReaderAbstractTestCase {
new TestData(
getClassLoaderResource("/icns/GenericJavaApp.icns"),
new Dimension(16, 16), // 1 bit + 1 bit mask
- new Dimension(16, 16), new Dimension(16, 16), // 8 bit CMAP, 32 bit
+ new Dimension(16, 16), new Dimension(16, 16), // 8 bit CMAP, 24 bit + 8 bit mask
new Dimension(32, 32), // 1 bit + 1 bit mask
- new Dimension(32, 32), new Dimension(32, 32), // 8 bit CMAP, 32 bit
- new Dimension(128, 128) // 32 bit
+ new Dimension(32, 32), new Dimension(32, 32), // 8 bit CMAP, 24 bit + 8 bit mask
+ new Dimension(128, 128) // 24 bit + 8 bit mask
),
new TestData(
getClassLoaderResource("/icns/Apple Retro.icns"),
@@ -61,16 +61,40 @@ public class ICNSImageReaderTest extends ImageReaderAbstractTestCase {
new Dimension(32, 32), // 24 bit + 8 bit mask
new Dimension(48, 48), // 24 bit + 8 bit mask
new Dimension(128, 128) // 24 bit + 8 bit mask
-//, new Dimension(256, 256), // JPEG 2000, not readable without JAI or other JPEG 2000 support
-// new Dimension(512, 512) // JPEG 2000
+//, new Dimension(256, 256), // JPEG 2000 ic08, not readable without JAI or other JPEG 2000 support
+// new Dimension(512, 512) // JPEG 2000 ic09
),
new TestData(
- getClassLoaderResource("/icns/7zIcon.icns"), // Contains the icnV resource, that isn't an icon
+ getClassLoaderResource("/icns/7zIcon.icns"), // Contains the icnV resource, that isn't an icon
new Dimension(16, 16), // 24 bit + 8 bit mask
new Dimension(32, 32), // 24 bit + 8 bit mask
new Dimension(128, 128) // 24 bit + 8 bit mask
-//, new Dimension(256, 256), // JPEG 2000
-// new Dimension(512, 512) // JPEG 2000
+//, new Dimension(256, 256), // JPEG 2000 ic08
+// new Dimension(512, 512) // JPEG 2000 ic09
+ ),
+ new TestData(
+ getClassLoaderResource("/icns/appStore.icns"), // Contains the 'TOC ' and icnV resources
+ new Dimension(16, 16), // 24 bit + 8 bit mask
+ new Dimension(32, 32), // 24 bit + 8 bit mask
+ new Dimension(128, 128), // 24 bit + 8 bit mask
+ new Dimension(256, 256), // PNG ic08
+ new Dimension(512, 512), // PNG ic09
+ new Dimension(1024, 1024) // PNG ic10
+ ),
+ new TestData(
+ getClassLoaderResource("/icns/XLW.icns"), // No 8 bit mask for 16x16 & 32x32, test fall back to 1 bit mask
+ new Dimension(16, 16), // 1 bit + 1 bit mask
+ new Dimension(16, 16), new Dimension(16, 16), // 4 bit CMAP, 8 bit CMAP (no 8 bit mask)
+ new Dimension(32, 32), // 1 bit + 1 bit mask
+ new Dimension(32, 32), new Dimension(32, 32), // 4 bit CMAP, 8 bit CMAP (no 8 bit mask)
+ new Dimension(128, 128) // 24 bit + 8 bit mask
+ ),
+ new TestData(
+ getClassLoaderResource("/icns/XMLExport.icns"), // No masks at all, uncompressed 32 bit data
+ new Dimension(128, 128), // 32 bit interleaved
+ new Dimension(48, 48), // 32 bit interleaved
+ new Dimension(32, 32), // 32 bit interleaved
+ new Dimension(16, 16) // 32 bit interleaved
)
);
}
diff --git a/imageio/imageio-icns/src/test/resources/icns/XLW.icns b/imageio/imageio-icns/src/test/resources/icns/XLW.icns
new file mode 100644
index 00000000..00e0d3e6
Binary files /dev/null and b/imageio/imageio-icns/src/test/resources/icns/XLW.icns differ
diff --git a/imageio/imageio-icns/src/test/resources/icns/XMLExport.icns b/imageio/imageio-icns/src/test/resources/icns/XMLExport.icns
new file mode 100644
index 00000000..573e504a
Binary files /dev/null and b/imageio/imageio-icns/src/test/resources/icns/XMLExport.icns differ
diff --git a/imageio/imageio-icns/src/test/resources/icns/appStore.icns b/imageio/imageio-icns/src/test/resources/icns/appStore.icns
new file mode 100644
index 00000000..0a64672c
Binary files /dev/null and b/imageio/imageio-icns/src/test/resources/icns/appStore.icns differ
diff --git a/imageio/imageio-icns/src/test/resources/icns/fair-use.txt b/imageio/imageio-icns/src/test/resources/icns/fair-use.txt
new file mode 100644
index 00000000..dd4fa4a1
--- /dev/null
+++ b/imageio/imageio-icns/src/test/resources/icns/fair-use.txt
@@ -0,0 +1,5 @@
+The icon files in this folder may contain copyrighted artwork. However, I believe that using them for test purposes
+(without actually displaying the artwork) must be considered fair use.
+If you disagree for any reason, please send me a note, and I will remove your icon from the distribution.
+
+ -- harald.kuhr@gmail.com