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 2ec75bcc..723d7a51 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
@@ -36,75 +36,78 @@ package com.twelvemonkeys.imageio.plugins.icns;
* @version $Id: ICNS.java,v 1.0 25.10.11 19:10 haraldk Exp$
*/
interface ICNS {
- /** "icns" magic identifier */
+ /** Resource header size (8). */
+ int RESOURCE_HEADER_SIZE = 8;
+
+ /** ICNS magic identifier ("icns"). */
int MAGIC = ('i' << 24) + ('c' << 16) + ('n' << 8) + 's';
- /** 32×32 1-bit mono icon */
+ /** 32×32 1-bit mono icon. */
int ICON = ('I' << 24) + ('C' << 16) + ('O' << 8) + 'N';
- /** 32×32 1-bit mono icon with 1-bit mask*/
+ /** 32×32 1-bit mono icon with 1-bit mask. */
int ICN_ = ('I' << 24) + ('C' << 16) + ('N' << 8) + '#';
- /** 16×12 1 bit mask*/
+ /** 16×12 1 bit mask. */
int icm_ = ('i' << 24) + ('c' << 16) + ('m' << 8) + '#';
- /** 16×12 4 bit icon */
+ /** 16×12 4 bit icon. */
int icm4 = ('i' << 24) + ('c' << 16) + ('m' << 8) + '4';
- /** 16×12 8 bit icon */
+ /** 16×12 8 bit icon. */
int icm8 = ('i' << 24) + ('c' << 16) + ('m' << 8) + '8';
- /** 16×16 1-bit icon with 1-bit mask */
+ /** 16×16 1-bit icon with 1-bit mask. */
int ics_ = ('i' << 24) + ('c' << 16) + ('s' << 8) + '#';
- /** 16×16 4-bit icon */
+ /** 16×16 4-bit icon. */
int ics4 = ('i' << 24) + ('c' << 16) + ('s' << 8) + '4';
- /** 16×16 8-bit icon */
+ /** 16×16 8-bit icon. */
int ics8 = ('i' << 24) + ('c' << 16) + ('s' << 8) + '8';
- /** 16×16 24-bit icon, run-length compressed */
+ /** 16×16 24-bit icon, possibly run-length compressed. */
int is32 = ('i' << 24) + ('s' << 16) + ('3' << 8) + '2';
- /** 16x16 8-bit mask */
+ /** 16x16 8-bit mask. */
int s8mk = ('s' << 24) + ('8' << 16) + ('m' << 8) + 'k';
- /** 32×32 4-bit icon */
+ /** 32×32 4-bit icon. */
int icl4 = ('i' << 24) + ('c' << 16) + ('l' << 8) + '4';
- /** 32×32 8-bit icon */
+ /** 32×32 8-bit icon. */
int icl8 = ('i' << 24) + ('c' << 16) + ('l' << 8) + '8';
- /** 32×32 24-bit icon, run-length compressed */
+ /** 32×32 24-bit icon, possibly run-length compressed. */
int il32 = ('i' << 24) + ('l' << 16) + ('3' << 8) + '2';
- /** 32×32 8-bit mask */
+ /** 32×32 8-bit mask. */
int l8mk = ('l' << 24) + ('8' << 16) + ('m' << 8) + 'k';
- /** 48×48 1-bit icon with 1 bit mask */
+ /** 48×48 1-bit icon with 1 bit mask. */
int ich_ = ('i' << 24) + ('c' << 16) + ('h' << 8) + '#';
- /** 48×48 4-bit icon */
+ /** 48×48 4-bit icon. */
int ich4 = ('i' << 24) + ('c' << 16) + ('h' << 8) + '4';
- /** 48×48 8-bit icon */
+ /** 48×48 8-bit icon. */
int ich8 = ('i' << 24) + ('c' << 16) + ('h' << 8) + '8';
- /** 48×48 24-bit icon, run-length compressed */
+ /** 48×48 24-bit icon, possibly run-length compressed. */
int ih32 = ('i' << 24) + ('h' << 16) + ('3' << 8) + '2';
- /** 48×48 8-bit mask */
+ /** 48×48 8-bit mask. */
int h8mk = ('h' << 24) + ('8' << 16) + ('m' << 8) + 'k';
- /** 128×128 24-bit icon, run-length compressed */
+ /** 128×128 24-bit icon, possibly run-length compressed. */
int it32 = ('i' << 24) + ('t' << 16) + ('3' << 8) + '2';
- /** 128×128 8-bit mask */
+ /** 128×128 8-bit mask. */
int t8mk = ('t' << 24) + ('8' << 16) + ('m' << 8) + 'k';
- /** 256×256 JPEG 2000 or PNG icon (10.x+) */
+ /** 256×256 JPEG 2000 or PNG icon (10.x+). */
int ic08 = ('i' << 24) + ('c' << 16) + ('0' << 8) + '8';
- /** 512×512 JPEG 2000 or PNG icon (10.x+) */
+ /** 512×512 JPEG 2000 or PNG icon (10.x+). */
int ic09 = ('i' << 24) + ('c' << 16) + ('0' << 8) + '9';
- /** 1024×1024 PNG icon (10.7+)*/
+ /** 1024×1024 PNG icon (10.7+). */
int ic10 = ('i' << 24) + ('c' << 16) + ('1' << 8) + '0';
- /** Unknown (Version) */
+ /** Unknown (Version). */
int icnV = ('i' << 24) + ('c' << 16) + ('n' << 8) + 'V';
- /** Unknown (Table of Contents) */
+ /** Unknown (Table of Contents). */
int TOC_ = ('T' << 24) + ('O' << 16) + ('C' << 8) + ' ';
- /** JPEG 2000 magic header */
+ /** JPEG 2000 magic header. */
byte[] JPEG_2000_MAGIC = new byte[] {0x00, 0x00, 0x00, 0x0C, 'j', 'P', 0x20, 0x20, 0x0D, 0x0A, (byte) 0x87, 0x0A};
- /** PNG magic header */
+ /** PNG magic header. */
byte[] PNG_MAGIC = new byte[] {(byte) 0x89, (byte) 'P', (byte) 'N', (byte) 'G', 0x0d, 0x0a, 0x1a, 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 388b8f96..7f6cb105 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
@@ -31,7 +31,6 @@ package com.twelvemonkeys.imageio.plugins.icns;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
-import com.twelvemonkeys.lang.Validate;
import javax.imageio.*;
import javax.imageio.spi.ImageReaderSpi;
@@ -49,7 +48,7 @@ import java.util.Iterator;
import java.util.List;
/**
- * ICNSImageReader
+ * ImageReader for Apple Icon Image (ICNS) format.
*
* @author Harald Kuhr
* @author last modified by $Author: haraldk$
@@ -61,9 +60,6 @@ import java.util.List;
public final class ICNSImageReader extends ImageReaderBase {
// 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;
@@ -163,6 +159,7 @@ public final class ICNSImageReader extends ImageReaderBase {
if (!allowSearch) {
// Return icons.size if we know we have read all?
+ // TODO: If the first resource is a TOC_ resource, we don't need to perform a search.
return -1;
}
@@ -183,7 +180,7 @@ public final class ICNSImageReader extends ImageReaderBase {
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
IconResource resource = readIconResource(imageIndex);
- imageInput.seek(resource.start + RESOURCE_HEADER_SIZE);
+ imageInput.seek(resource.start + ICNS.RESOURCE_HEADER_SIZE);
// Special handling of PNG/JPEG 2000 icons
if (resource.isForeignFormat()) {
@@ -221,7 +218,7 @@ public final class ICNSImageReader extends ImageReaderBase {
// Only 32 bit icons may be compressed
data = new byte[width * height * resource.depth() / 8];
- int packedSize = resource.length - RESOURCE_HEADER_SIZE;
+ int packedSize = resource.length - ICNS.RESOURCE_HEADER_SIZE;
if (width >= 128 && height >= 128) {
// http://www.macdisk.com/maciconen.php:
@@ -233,14 +230,14 @@ public final class ICNSImageReader extends ImageReaderBase {
InputStream input = IIOUtil.createStreamAdapter(imageInput, packedSize);
try {
- decompress(new DataInputStream(input), data, 0, (data.length * 24) / 32); // 24 bit data
+ ICNSUtil.decompress(new DataInputStream(input), data, 0, (data.length * 24) / 32); // 24 bit data
}
finally {
input.close();
}
}
else {
- data = new byte[resource.length - RESOURCE_HEADER_SIZE];
+ data = new byte[resource.length - ICNS.RESOURCE_HEADER_SIZE];
imageInput.readFully(data);
}
@@ -365,15 +362,15 @@ public final class ICNSImageReader extends ImageReaderBase {
int height = size.height;
byte[] mask = new byte[width * height];
- imageInput.seek(resource.start + RESOURCE_HEADER_SIZE);
+ imageInput.seek(resource.start + ICNS.RESOURCE_HEADER_SIZE);
if (resource.isMaskType()) {
// 8 bit mask
- imageInput.readFully(mask, 0, resource.length - RESOURCE_HEADER_SIZE);
+ imageInput.readFully(mask, 0, resource.length - ICNS.RESOURCE_HEADER_SIZE);
}
else if (resource.hasMask()) {
// Embedded 1bit mask
- byte[] maskData = new byte[(resource.length - RESOURCE_HEADER_SIZE) / 2];
+ byte[] maskData = new byte[(resource.length - ICNS.RESOURCE_HEADER_SIZE) / 2];
imageInput.skipBytes(maskData.length); // Skip the 1 bit image data
imageInput.readFully(maskData);
@@ -482,52 +479,6 @@ public final class ICNSImageReader extends ImageReaderBase {
return format;
}
- /*
- * http://www.macdisk.com/maciconen.php:
- * "For [...] (width * height of the icon), read a byte.
- * if bit 8 of the byte is set:
- * This is a compressed run, for some value (next byte).
- * The length is byte - 125. (*
- * Put so many copies of the byte in the current color channel.
- * Else:
- * This is an uncompressed run, whose values follow.
- * The length is byte + 1.
- * Read the bytes and put them in the current color channel."
- *
- * *): With signed bytes, byte is always negative in this case, so it's actually -byte - 125,
- * which is the same as byte + 131.
- */
- // NOTE: This is very close to PackBits (as described by the Wikipedia article), but it is not PackBits!
- static void decompress(final DataInputStream input, final byte[] result, int offset, int length) throws IOException {
- int resultPos = offset;
- int remaining = length;
-
- while (remaining > 0) {
- byte run = input.readByte();
- int runLength;
-
- if ((run & 0x80) != 0) {
- // Compressed run
- runLength = run + 131; // PackBits: -run + 1 and run == 0x80 is no-op... This allows 1 byte longer runs...
-
- byte runData = input.readByte();
-
- for (int i = 0; i < runLength; i++) {
- result[resultPos++] = runData;
- }
- }
- else {
- // Uncompressed run
- runLength = run + 1;
-
- input.readFully(result, resultPos, runLength);
- resultPos += runLength;
- }
-
- remaining -= runLength;
- }
- }
-
private IconResource readIconResource(final int imageIndex) throws IOException {
checkBounds(imageIndex);
readeFileHeader();
@@ -540,7 +491,7 @@ public final class ICNSImageReader extends ImageReaderBase {
}
private IconResource readNextIconResource() throws IOException {
- long lastReadPos = lastResourceRead == null ? RESOURCE_HEADER_SIZE : lastResourceRead.start + lastResourceRead.length;
+ long lastReadPos = lastResourceRead == null ? ICNS.RESOURCE_HEADER_SIZE : lastResourceRead.start + lastResourceRead.length;
imageInput.seek(lastReadPos);
@@ -578,280 +529,11 @@ public final class ICNSImageReader extends ImageReaderBase {
}
}
- // TODO: Rewrite using subclasses/instances!
- static final class IconResource {
- private final long start;
- private final int type;
- private final int length;
+ private static final class ICNSBitMaskColorModel extends IndexColorModel {
+ static final IndexColorModel INSTANCE = new ICNSBitMaskColorModel();
- IconResource(long start, int type, int length) {
- validate(type, length);
-
- this.start = start;
- this.type = type;
- this.length = length;
- }
-
- public static IconResource read(ImageInputStream input) throws IOException {
- return new IconResource(input.getStreamPosition(), input.readInt(), input.readInt());
- }
-
- private void validate(int type, int length) {
- switch (type) {
- case ICNS.ICON:
- validateLengthForType(type, length, 128);
- break;
- case ICNS.ICN_:
- validateLengthForType(type, length, 256);
- break;
- case ICNS.icm_:
- validateLengthForType(type, length, 48);
- break;
- case ICNS.icm4:
- validateLengthForType(type, length, 96);
- break;
- case ICNS.icm8:
- validateLengthForType(type, length, 192);
- break;
- case ICNS.ics_:
- validateLengthForType(type, length, 64);
- break;
- case ICNS.ics4:
- validateLengthForType(type, length, 128);
- break;
- case ICNS.ics8:
- case ICNS.s8mk:
- validateLengthForType(type, length, 256);
- break;
- case ICNS.icl4:
- validateLengthForType(type, length, 512);
- break;
- case ICNS.icl8:
- case ICNS.l8mk:
- validateLengthForType(type, length, 1024);
- break;
- case ICNS.ich_:
- validateLengthForType(type, length, 576);
- break;
- case ICNS.ich4:
- validateLengthForType(type, length, 1152);
- break;
- case ICNS.ich8:
- case ICNS.h8mk:
- validateLengthForType(type, length, 2304);
- break;
- case ICNS.t8mk:
- validateLengthForType(type, length, 16384);
- break;
- case ICNS.ih32:
- case ICNS.is32:
- case ICNS.il32:
- case ICNS.it32:
- case ICNS.ic08:
- case ICNS.ic09:
- case ICNS.ic10:
- 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:
- 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 + 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 - RESOURCE_HEADER_SIZE, expectedLength
- )
- );
- }
-
- public Dimension size() {
- switch (type) {
- case ICNS.ICON:
- case ICNS.ICN_:
- return new Dimension(32, 32);
- case ICNS.icm_:
- case ICNS.icm4:
- case ICNS.icm8:
- return new Dimension(16, 12);
- case ICNS.ics_:
- case ICNS.ics4:
- case ICNS.ics8:
- case ICNS.is32:
- case ICNS.s8mk:
- return new Dimension(16, 16);
- case ICNS.icl4:
- case ICNS.icl8:
- case ICNS.il32:
- case ICNS.l8mk:
- return new Dimension(32, 32);
- case ICNS.ich_:
- case ICNS.ich4:
- case ICNS.ich8:
- case ICNS.ih32:
- case ICNS.h8mk:
- return new Dimension(48, 48);
- case ICNS.it32:
- case ICNS.t8mk:
- return new Dimension(128, 128);
- case ICNS.ic08:
- return new Dimension(256, 256);
- case ICNS.ic09:
- return new Dimension(512, 512);
- case ICNS.ic10:
- return new Dimension(1024, 1024);
- default:
- throw new IllegalStateException(String.format("Unknown icon type: '%s'", ICNSUtil.intToStr(type)));
- }
- }
-
- public int depth() {
- switch (type) {
- case ICNS.ICON:
- case ICNS.ICN_:
- case ICNS.icm_:
- case ICNS.ics_:
- case ICNS.ich_:
- return 1;
- case ICNS.icm4:
- case ICNS.ics4:
- case ICNS.icl4:
- case ICNS.ich4:
- return 4;
- case ICNS.icm8:
- case ICNS.ics8:
- case ICNS.icl8:
- case ICNS.ich8:
- case ICNS.s8mk:
- case ICNS.l8mk:
- case ICNS.h8mk:
- case ICNS.t8mk:
- return 8;
- case ICNS.is32:
- case ICNS.il32:
- case ICNS.ih32:
- case ICNS.it32:
- case ICNS.ic08:
- case ICNS.ic09:
- case ICNS.ic10:
- return 32;
- default:
- throw new IllegalStateException(String.format("Unknown icon type: '%s'", ICNSUtil.intToStr(type)));
- }
- }
-
- 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:
- case ICNS.h8mk:
- case ICNS.t8mk:
- return true;
- }
-
- return false;
- }
-
- public boolean isCompressed() {
- switch (type) {
- case ICNS.is32:
- case ICNS.il32:
- case ICNS.ih32:
- case ICNS.it32:
- // 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 * depth() / 8 + RESOURCE_HEADER_SIZE)) {
- return true;
- }
- }
-
- return false;
- }
-
- public boolean isForeignFormat() {
- switch (type) {
- case ICNS.ic08:
- case ICNS.ic09:
- case ICNS.ic10:
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return (int) start ^ type;
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this || other != null && other.getClass() == getClass() && isEqual((IconResource) 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%s]", getClass().getSimpleName(), ICNSUtil.intToStr(type), start, length, isCompressed() ? " (compressed)" : "");
+ private ICNSBitMaskColorModel() {
+ super(1, 2, new int[]{0, 0xffffffff}, 0, true, 0, DataBuffer.TYPE_BYTE);
}
}
@@ -910,12 +592,4 @@ public final class ICNSImageReader extends ImageReaderBase {
System.err.printf("Read %s images (%d skipped) in %d files\n", imagesRead, imagesSkipped, args.length);
}
-
- private static final class ICNSBitMaskColorModel extends IndexColorModel {
- static final IndexColorModel INSTANCE = new ICNSBitMaskColorModel();
-
- private ICNSBitMaskColorModel() {
- super(1, 2, new int[]{0, 0xffffffff}, 0, true, 0, DataBuffer.TYPE_BYTE);
- }
- }
}
diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSUtil.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSUtil.java
index fb1246f4..71fb6b06 100644
--- a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSUtil.java
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSUtil.java
@@ -28,6 +28,9 @@
package com.twelvemonkeys.imageio.plugins.icns;
+import java.io.DataInputStream;
+import java.io.IOException;
+
/**
* ICNSUtil
*
@@ -50,4 +53,50 @@ final class ICNSUtil {
}
);
}
+
+ /*
+ * http://www.macdisk.com/maciconen.php:
+ * "For [...] (width * height of the icon), read a byte.
+ * if bit 8 of the byte is set:
+ * This is a compressed run, for some value (next byte).
+ * The length is byte - 125. (*
+ * Put so many copies of the byte in the current color channel.
+ * Else:
+ * This is an uncompressed run, whose values follow.
+ * The length is byte + 1.
+ * Read the bytes and put them in the current color channel."
+ *
+ * *): With signed bytes, byte is always negative in this case, so it's actually -byte - 125,
+ * which is the same as byte + 131.
+ */
+ // NOTE: This is very close to PackBits (as described by the Wikipedia article), but it is not PackBits!
+ static void decompress(final DataInputStream input, final byte[] result, int offset, int length) throws IOException {
+ int resultPos = offset;
+ int remaining = length;
+
+ while (remaining > 0) {
+ byte run = input.readByte();
+ int runLength;
+
+ if ((run & 0x80) != 0) {
+ // Compressed run
+ runLength = run + 131; // PackBits: -run + 1 and run == 0x80 is no-op... This allows 1 byte longer runs...
+
+ byte runData = input.readByte();
+
+ for (int i = 0; i < runLength; i++) {
+ result[resultPos++] = runData;
+ }
+ }
+ else {
+ // Uncompressed run
+ runLength = run + 1;
+
+ input.readFully(result, resultPos, runLength);
+ resultPos += runLength;
+ }
+
+ remaining -= runLength;
+ }
+ }
}
diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/IconResource.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/IconResource.java
new file mode 100644
index 00000000..04ce6d90
--- /dev/null
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/IconResource.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2011, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.plugins.icns;
+
+import com.twelvemonkeys.lang.Validate;
+
+import javax.imageio.stream.ImageInputStream;
+import java.awt.*;
+import java.io.IOException;
+
+/**
+ * IconResource
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: IconResource.java,v 1.0 23.11.11 13:35 haraldk Exp$
+ */
+final class IconResource {
+ // TODO: Rewrite using subclasses/instances!
+
+ protected final long start;
+ protected final int type;
+ protected final int length;
+
+ private IconResource(long start, int type, int length) {
+ validate(type, length);
+
+ this.start = start;
+ this.type = type;
+ this.length = length;
+ }
+
+ public static IconResource read(ImageInputStream input) throws IOException {
+ return new IconResource(input.getStreamPosition(), input.readInt(), input.readInt());
+ }
+
+ private void validate(int type, int length) {
+ switch (type) {
+ case ICNS.ICON:
+ validateLengthForType(type, length, 128);
+ break;
+ case ICNS.ICN_:
+ validateLengthForType(type, length, 256);
+ break;
+ case ICNS.icm_:
+ validateLengthForType(type, length, 48);
+ break;
+ case ICNS.icm4:
+ validateLengthForType(type, length, 96);
+ break;
+ case ICNS.icm8:
+ validateLengthForType(type, length, 192);
+ break;
+ case ICNS.ics_:
+ validateLengthForType(type, length, 64);
+ break;
+ case ICNS.ics4:
+ validateLengthForType(type, length, 128);
+ break;
+ case ICNS.ics8:
+ case ICNS.s8mk:
+ validateLengthForType(type, length, 256);
+ break;
+ case ICNS.icl4:
+ validateLengthForType(type, length, 512);
+ break;
+ case ICNS.icl8:
+ case ICNS.l8mk:
+ validateLengthForType(type, length, 1024);
+ break;
+ case ICNS.ich_:
+ validateLengthForType(type, length, 576);
+ break;
+ case ICNS.ich4:
+ validateLengthForType(type, length, 1152);
+ break;
+ case ICNS.ich8:
+ case ICNS.h8mk:
+ validateLengthForType(type, length, 2304);
+ break;
+ case ICNS.t8mk:
+ validateLengthForType(type, length, 16384);
+ break;
+ case ICNS.ih32:
+ case ICNS.is32:
+ case ICNS.il32:
+ case ICNS.it32:
+ case ICNS.ic08:
+ case ICNS.ic09:
+ case ICNS.ic10:
+ if (length > ICNS.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:
+ if (length > ICNS.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 + ICNS.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 - ICNS.RESOURCE_HEADER_SIZE, expectedLength
+ )
+ );
+ }
+
+ public Dimension size() {
+ switch (type) {
+ case ICNS.ICON:
+ case ICNS.ICN_:
+ return new Dimension(32, 32);
+ case ICNS.icm_:
+ case ICNS.icm4:
+ case ICNS.icm8:
+ return new Dimension(16, 12);
+ case ICNS.ics_:
+ case ICNS.ics4:
+ case ICNS.ics8:
+ case ICNS.is32:
+ case ICNS.s8mk:
+ return new Dimension(16, 16);
+ case ICNS.icl4:
+ case ICNS.icl8:
+ case ICNS.il32:
+ case ICNS.l8mk:
+ return new Dimension(32, 32);
+ case ICNS.ich_:
+ case ICNS.ich4:
+ case ICNS.ich8:
+ case ICNS.ih32:
+ case ICNS.h8mk:
+ return new Dimension(48, 48);
+ case ICNS.it32:
+ case ICNS.t8mk:
+ return new Dimension(128, 128);
+ case ICNS.ic08:
+ return new Dimension(256, 256);
+ case ICNS.ic09:
+ return new Dimension(512, 512);
+ case ICNS.ic10:
+ return new Dimension(1024, 1024);
+ default:
+ throw new IllegalStateException(String.format("Unknown icon type: '%s'", ICNSUtil.intToStr(type)));
+ }
+ }
+
+ public int depth() {
+ switch (type) {
+ case ICNS.ICON:
+ case ICNS.ICN_:
+ case ICNS.icm_:
+ case ICNS.ics_:
+ case ICNS.ich_:
+ return 1;
+ case ICNS.icm4:
+ case ICNS.ics4:
+ case ICNS.icl4:
+ case ICNS.ich4:
+ return 4;
+ case ICNS.icm8:
+ case ICNS.ics8:
+ case ICNS.icl8:
+ case ICNS.ich8:
+ case ICNS.s8mk:
+ case ICNS.l8mk:
+ case ICNS.h8mk:
+ case ICNS.t8mk:
+ return 8;
+ case ICNS.is32:
+ case ICNS.il32:
+ case ICNS.ih32:
+ case ICNS.it32:
+ case ICNS.ic08:
+ case ICNS.ic09:
+ case ICNS.ic10:
+ return 32;
+ default:
+ throw new IllegalStateException(String.format("Unknown icon type: '%s'", ICNSUtil.intToStr(type)));
+ }
+ }
+
+ 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:
+ case ICNS.h8mk:
+ case ICNS.t8mk:
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean isCompressed() {
+ switch (type) {
+ case ICNS.is32:
+ case ICNS.il32:
+ case ICNS.ih32:
+ case ICNS.it32:
+ // 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 * depth() / 8 + ICNS.RESOURCE_HEADER_SIZE)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean isForeignFormat() {
+ switch (type) {
+ case ICNS.ic08:
+ case ICNS.ic09:
+ case ICNS.ic10:
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) start ^ type;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this || other != null && other.getClass() == getClass() && isEqual((IconResource) 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%s]", getClass().getSimpleName(), ICNSUtil.intToStr(type), start, length, isCompressed() ? " (compressed)" : "");
+ }
+}