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
new file mode 100644
index 00000000..87cb0815
--- /dev/null
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS.java
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+/**
+ * ICNS
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: ICNS.java,v 1.0 25.10.11 19:10 haraldk Exp$
+ */
+interface ICNS {
+ /** "icns" magic identifier */
+ int MAGIC = ('i' << 24) + ('c' << 16) + ('n' << 8) + 's';
+
+ /** 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*/
+ int ICN_ = ('I' << 24) + ('C' << 16) + ('N' << 8) + '#';
+
+ /** 16×12 1 bit mask*/
+ int icm_ = ('i' << 24) + ('c' << 16) + ('m' << 8) + '#';
+ /** 16×12 4 bit icon */
+ int icm4 = ('i' << 24) + ('c' << 16) + ('m' << 8) + '4';
+ /** 16×12 8 bit icon */
+ int icm8 = ('i' << 24) + ('c' << 16) + ('m' << 8) + '8';
+
+ /** 16×16 1-bit mask */
+ int ics_ = ('i' << 24) + ('c' << 16) + ('s' << 8) + '#';
+ /** 16×16 4-bit icon */
+ int ics4 = ('i' << 24) + ('c' << 16) + ('s' << 8) + '4';
+ /** 16×16 8-bit icon */
+ int ics8 = ('i' << 24) + ('c' << 16) + ('s' << 8) + '8';
+ /** 16×16 24-bit icon, run-length compressed */
+ int is32 = ('i' << 24) + ('s' << 16) + ('3' << 8) + '2';
+ /** 16x16 8-bit mask */
+ int s8mk = ('s' << 24) + ('8' << 16) + ('m' << 8) + 'k';
+
+ /** 32×32 4-bit icon */
+ int icl4 = ('i' << 24) + ('c' << 16) + ('l' << 8) + '4';
+ /** 32×32 8-bit icon */
+ int icl8 = ('i' << 24) + ('c' << 16) + ('l' << 8) + '8';
+ /** 32×32 24-bit icon, run-length compressed */
+ int il32 = ('i' << 24) + ('l' << 16) + ('3' << 8) + '2';
+ /** 32×32 8-bit mask */
+ int l8mk = ('l' << 24) + ('8' << 16) + ('m' << 8) + 'k';
+
+ /** 48×48 1-bit mask */
+ int ich_ = ('i' << 24) + ('c' << 16) + ('h' << 8) + '#';
+ /** 48×48 4-bit icon */
+ int ich4 = ('i' << 24) + ('c' << 16) + ('h' << 8) + '4';
+ /** 48×48 8-bit icon */
+ int ich8 = ('i' << 24) + ('c' << 16) + ('h' << 8) + '8';
+ /** 48×48 24-bit icon, run-length compressed */
+ int ih32 = ('i' << 24) + ('h' << 16) + ('3' << 8) + '2';
+ /** 48×48 8-bit mask */
+ int h8mk = ('h' << 24) + ('8' << 16) + ('m' << 8) + 'k';
+
+ /** 128×128 24-bit icon, run-length compressed */
+ int it32 = ('i' << 24) + ('t' << 16) + ('3' << 8) + '2';
+ /** 128×128 8-bit mask */
+ int t8mk = ('t' << 24) + ('8' << 16) + ('m' << 8) + 'k';
+
+ /** 256×256 JPEG 2000 or PNG icon */
+ int ic08 = ('i' << 24) + ('c' << 16) + ('0' << 8) + '8';
+
+ /** 512×512 JPEG 2000 or PNG icon */
+ int ic09 = ('i' << 24) + ('c' << 16) + ('0' << 8) + '9';
+
+ /** 1024×1024 PNG icon (10.7)*/
+ int ic10 = ('i' << 24) + ('c' << 16) + ('1' << 8) + '0';
+
+ /*
+ ICN# 256 32 32×32 1-bit mono icon with 1-bit mask
+ icm# 24 16 16×12 1 bit mask
+ icm4 96 16 16×12 4 bit icon
+ icm8 192 16 16×12 8 bit icon
+ ics# 32 16 16×16 1-bit mask
+ ics4 128 16 16×16 4-bit icon
+ ics8 256 16 16x16 8 bit icon
+ is32 varies (768) 16 16×16 24-bit icon
+ s8mk 256 16 16x16 8-bit mask
+ icl4 512 32 32×32 4-bit icon
+ icl8 1,024 32 32×32 8-bit icon
+ il32 varies (3,072) 32 32x32 24-bit icon
+ l8mk 1,024 32 32×32 8-bit mask
+ ich# 288 48 48×48 1-bit mask
+ ich4 1,152 48 48×48 4-bit icon
+ ich8 2,304 48 48×48 8-bit icon
+ ih32 varies (6,912) 48 48×48 24-bit icon
+ h8mk 2,304 48 48×48 8-bit mask
+ it32 varies (49,152) 128 128×128 24-bit icon
+ t8mk 16,384 128 128×128 8-bit mask
+ ic08 varies 256 256×256 icon in JPEG 2000 or PNG format
+ ic09 varies 512 512×512 icon in JPEG 2000 or PNG format
+ ic10 varies 1024 1024×1024 icon in PNG format (added in Mac OS X 10.7)
+ */
+
+ byte[] JPEG_2000_MAGIC = new byte[] {0x00, 0x00, 0x00, 0x0C, 'j', 'P', 0x20, 0x20, 0x0D, 0x0A, (byte) 0x87, 0x0A};
+ 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
new file mode 100644
index 00000000..a4e34d80
--- /dev/null
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReader.java
@@ -0,0 +1,579 @@
+/*
+ * 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.imageio.ImageReaderBase;
+import com.twelvemonkeys.imageio.util.IIOUtil;
+
+import javax.imageio.*;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
+import java.awt.*;
+import java.awt.color.ColorSpace;
+import java.awt.image.*;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * ICNSImageReader
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: ICNSImageReader.java,v 1.0 25.10.11 18:42 haraldk Exp$
+ */
+public final class ICNSImageReader extends ImageReaderBase {
+ // TODO: Merge masks with icon in front + calculate image count based on this...
+
+ private static final int HEADER_SIZE = 8;
+ private List iconHeaders = new ArrayList();
+ private int length;
+
+ public ICNSImageReader() {
+ this(new ICNSImageReaderSpi());
+ }
+
+ ICNSImageReader(final ImageReaderSpi provider) {
+ super(provider);
+ }
+
+ @Override
+ protected void resetMembers() {
+ }
+
+ @Override
+ public int getWidth(int imageIndex) throws IOException {
+ return readIconHeader(imageIndex).size().width;
+ }
+
+ @Override
+ public int getHeight(int imageIndex) throws IOException {
+ return readIconHeader(imageIndex).size().height;
+ }
+
+ @Override
+ public Iterator getImageTypes(int imageIndex) throws IOException {
+ IconHeader header = readIconHeader(imageIndex);
+
+ List specifiers = new ArrayList();
+
+ switch (header.depth()) {
+ case 1:
+ specifiers.add(ImageTypeSpecifier.createGrayscale(1, DataBuffer.TYPE_BYTE, false));
+ // Fall through
+ case 4:
+ specifiers.add(ImageTypeSpecifier.createGrayscale(4, DataBuffer.TYPE_BYTE, false));
+ // Fall through
+ case 8:
+ specifiers.add(ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false));
+ // Fall through
+ case 24:
+ specifiers.add(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{0, 1, 2}, DataBuffer.TYPE_BYTE, false, false));
+ case 32:
+ specifiers.add(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{0, 1, 2, 3}, DataBuffer.TYPE_BYTE, true, false));
+ break;
+ default:
+ throw new IllegalStateException(String.format("Unknown bit depth: %d", header.depth()));
+ }
+
+ return specifiers.iterator();
+ }
+
+ @Override
+ public int getNumImages(boolean allowSearch) throws IOException {
+ assertInput();
+
+ if (!allowSearch) {
+ return -1;
+ }
+
+ int num = iconHeaders.size();
+ while (true) {
+ try {
+ readIconHeader(num);
+ num++;
+ }
+ catch (IndexOutOfBoundsException expected) {
+ break;
+ }
+ }
+
+ return num;
+ }
+
+ @Override
+ public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
+ IconHeader header = readIconHeader(imageIndex);
+// System.err.println("header: " + header);
+
+ imageInput.seek(header.start + HEADER_SIZE);
+
+ // TODO: Extract in separate method/class
+ // Special handling of PNG/JPEG 2000 icons
+ if (header.isForeignFormat()) {
+ ImageInputStream stream = ImageIO.createImageInputStream(IIOUtil.createStreamAdapter(imageInput, header.length));
+ try {
+ // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
+ // JPEG2000 magic bytes: 00 00 00 0C 6A 50 20 20 0D 0A 87 0A 00 00 00 14 66 74 79 70 6A 70 32
+ // 00 00 00 0C 6A 50 20 20 0D 0A 87 0A
+ // 12 j P sp sp \r \n
+ byte[] magic = new byte[12];
+ stream.readFully(magic);
+// System.out.println("magic: " + Arrays.toString(magic));
+
+ String format;
+ if (Arrays.equals(ICNS.PNG_MAGIC, magic)) {
+ format = "PNG";
+ }
+ else if (Arrays.equals(ICNS.JPEG_2000_MAGIC, magic)) {
+ format = "JPEG 2000";
+ }
+ else {
+ format = "unknown";
+ }
+
+ stream.seek(0);
+
+ Iterator readers = ImageIO.getImageReaders(stream);
+
+ while (readers.hasNext()) {
+ ImageReader reader = readers.next();
+ reader.setInput(stream);
+
+ try {
+ return reader.read(0, param);
+ }
+ catch (IOException ignore) {
+ }
+
+ stream.seek(0);
+ }
+
+ // TODO: There's no JPEG 2000 reader installed in ImageIO by default (requires JAI ImageIO installed)
+ // TODO: Return blank icon? We know the image dimensions, we just can't read the data... Return blank image? Pretend it's not in the stream? ;-)
+ // TODO: Create JPEG 2000 reader..? :-P
+ throw new IIOException(String.format(
+ "Cannot read %s format in type '%s' icon (no reader; installed: %s)",
+ format, ICNSUtil.intToStr(header.type), Arrays.toString(ImageIO.getReaderFormatNames())
+ ));
+ }
+ finally {
+ stream.close();
+ }
+ }
+
+ Dimension size = header.size();
+ int width = size.width;
+ int height = size.height;
+
+ BufferedImage image = getDestination(param, getImageTypes(imageIndex), width, height);
+ ImageTypeSpecifier rawType = getRawImageType(imageIndex);
+ checkReadParamBandSettings(param, rawType.getNumBands(), image.getSampleModel().getNumBands());
+
+ final Rectangle source = new Rectangle();
+ final Rectangle dest = new Rectangle();
+ computeRegions(param, width, height, image, source, dest);
+
+ // Read image data
+ byte[] data;
+ if (header.isPackbits()) {
+ data = new byte[width * height * header.depth() / 8];
+
+ int packedSize = header.length - HEADER_SIZE;
+ if (width >= 128 && height >= 128) {
+ imageInput.skipBytes(4);
+ packedSize -= 4;
+ }
+
+ InputStream input = IIOUtil.createStreamAdapter(imageInput, packedSize);
+ unpackbits(new DataInputStream(input), data, 0, data.length);
+ input.close();
+ }
+ else {
+ data = new byte[header.length - HEADER_SIZE];
+ imageInput.readFully(data);
+ }
+
+ switch (header.depth()) {
+ case 1:
+ break;
+ case 4:
+ break;
+ case 8:
+ break;
+ case 24:
+ break;
+ default:
+ throw new IllegalStateException(String.format("Unknown bit depth for icon: %d", header.depth()));
+ }
+
+ if (header.depth() <= 8) {
+ DataBufferByte buffer = new DataBufferByte(data, data.length);
+ image.setData(Raster.createPackedRaster(buffer, width, height, header.depth(), null));
+ }
+ else {
+// System.err.println("image: " + image);
+// DataBufferByte buffer = new DataBufferByte(data, data.length);
+// WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, width * header.depth() / 8, header.depth() / 8, new int[]{0, 1, 2}, null);
+// WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, width * header.depth() / 8, header.depth() / 8, new int[]{0, 1, 2, 3}, null);
+// 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}, new int[]{0, bandLen, bandLen * 2, bandLen * 3}, null);
+ int bandLen = data.length / 3;
+ DataBufferByte buffer = new DataBufferByte(data, data.length);
+ WritableRaster raster = Raster.createBandedRaster(buffer, width, height, width, new int[]{0, 0, 0}, new int[]{0, bandLen, bandLen * 2}, null);
+ ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
+
+ BufferedImage temp = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
+// showIt(temp, "foo");
+
+// image.setData(raster);
+ Graphics2D graphics = image.createGraphics();
+ try {
+ graphics.drawImage(temp, 0, 0, null);
+ }
+ finally {
+ graphics.dispose();
+ }
+ }
+
+ return image;
+ }
+
+ // TODO: Is this really packbits?! Don't think so, but it's very close...
+ static void unpackbits(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) {
+ // Repeated run
+ runLength = run + 131; // Packbits says: -run + 1 and 0x80 should be no-op... This inverts the lengths, but allows longer runs...
+
+ byte runData = input.readByte();
+ for (int i = 0; i < runLength; i++) {
+ result[resultPos++] = runData;
+ }
+ }
+ else {
+ // Literal run
+ runLength = run + 1;
+
+ input.readFully(result, resultPos, runLength);
+ resultPos += runLength;
+ }
+
+ remaining -= runLength;
+ }
+ }
+
+ private IconHeader readIconHeader(int imageIndex) throws IOException {
+ checkBounds(imageIndex);
+ readeFileHeader();
+
+ if (iconHeaders.size() <= imageIndex) {
+ int lastReadIndex = iconHeaders.size() - 1;
+ IconHeader lastRead = iconHeaders.isEmpty() ? null : iconHeaders.get(lastReadIndex);
+
+ for (int i = lastReadIndex; i < imageIndex; i++) {
+ imageInput.seek(lastRead == null ? HEADER_SIZE : lastRead.start + lastRead.length);
+
+ if (imageInput.getStreamPosition() >= length) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ lastRead = IconHeader.read(imageInput);
+ iconHeaders.add(lastRead);
+ }
+ }
+
+ return iconHeaders.get(imageIndex);
+ }
+
+ private void readeFileHeader() throws IOException {
+ assertInput();
+
+ if (length <= 0) {
+ imageInput.seek(0);
+
+ if (imageInput.readInt() != ICNS.MAGIC) {
+ throw new IIOException("Not an Apple Icon Image");
+ }
+
+ length = imageInput.readInt();
+ }
+ }
+
+ // TODO: Rewrite using subclasses!
+ static final class IconHeader {
+ private final long start;
+ private final int type;
+ private final int length;
+
+ IconHeader(long start, int type, int length) {
+ validate(type, length);
+
+ this.start = start;
+ this.type = type;
+ this.length = length;
+ }
+
+ public static IconHeader read(ImageInputStream input) throws IOException {
+ return new IconHeader(input.getStreamPosition(), input.readInt(), input.readInt());
+ }
+
+ private void validate(int type, int length) {
+ switch (type) {
+ case ICNS.ICON:
+ if (length == 128) {
+ return;
+ }
+ case ICNS.ICN_:
+ if (length == 256) {
+ return;
+ }
+ case ICNS.icm_:
+ if (length == 24) {
+ return;
+ }
+ case ICNS.icm4:
+ if (length == 96) {
+ return;
+ }
+ case ICNS.icm8:
+ if (length == 192) {
+ return;
+ }
+ case ICNS.ics_:
+ if (length == 32) {
+ return;
+ }
+ case ICNS.ics4:
+ if (length == 128) {
+ return;
+ }
+ case ICNS.ics8:
+ case ICNS.s8mk:
+ if (length == 256) {
+ return;
+ }
+ case ICNS.icl4:
+ if (length == 512) {
+ return;
+ }
+ case ICNS.icl8:
+ case ICNS.l8mk:
+ if (length == 1024) {
+ return;
+ }
+ case ICNS.ich_:
+ if (length == 288) {
+ return;
+ }
+ case ICNS.ich4:
+ if (length == 1152) {
+ return;
+ }
+ case ICNS.ich8:
+ case ICNS.h8mk:
+ if (length == 2034) {
+ return;
+ }
+ case ICNS.t8mk:
+ if (length == 16384) {
+ return;
+ }
+ case ICNS.ih32:
+ case ICNS.is32:
+ case ICNS.il32:
+ case ICNS.it32:
+ case ICNS.ic08:
+ case ICNS.ic09:
+ case ICNS.ic10:
+ if (length > 0) {
+ return;
+ }
+ throw new IllegalArgumentException(String.format("Wrong combination of icon type '%s' and length: %d", ICNSUtil.intToStr(type), length));
+ default:
+ throw new IllegalStateException(String.format("Unknown icon type: '%s'", ICNSUtil.intToStr(type)));
+ }
+ }
+
+ 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_: // Specical case? Wikipedi say 1 bit + 1 bit mask
+ 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 24;
+ default:
+ throw new IllegalStateException(String.format("Unknown icon type: '%s'", ICNSUtil.intToStr(type)));
+ }
+ }
+
+ public boolean isPackbits() {
+ switch (type) {
+ case ICNS.ih32:
+ case ICNS.il32:
+ case ICNS.is32:
+ case ICNS.it32:
+ 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((IconHeader) other);
+ }
+
+ private boolean isEqual(IconHeader 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);
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ int argIndex = 0;
+
+ int requested = -1;
+ if (args[argIndex].charAt(0) == '-') {
+ argIndex++;
+ requested = Integer.parseInt(args[argIndex++]);
+ }
+
+ File input = new File(args[argIndex++]);
+ 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));
+ }
+ catch (IIOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderSpi.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderSpi.java
new file mode 100644
index 00000000..5b023276
--- /dev/null
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderSpi.java
@@ -0,0 +1,95 @@
+/*
+ * 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.imageio.spi.ProviderInfo;
+import com.twelvemonkeys.imageio.util.IIOUtil;
+
+import javax.imageio.ImageReader;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * ICNSImageReaderSpi
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: ICNSImageReaderSpi.java,v 1.0 25.10.11 18:41 haraldk Exp$
+ */
+public final class ICNSImageReaderSpi extends ImageReaderSpi{
+ public ICNSImageReaderSpi() {
+ this(IIOUtil.getProviderInfo(ICNSImageReaderSpi.class));
+ }
+
+ private ICNSImageReaderSpi(final ProviderInfo pProviderInfo) {
+ super(
+ pProviderInfo.getVendorName(),
+ pProviderInfo.getVersion(),
+ new String[]{"icns", "ICNS"},
+ new String[]{"icns"},
+ new String[]{
+ "image/x-apple-icons", // Common extension MIME
+ },
+ "com.twelvemonkeys.imageio.plugins.icns.ICNSImageReader",
+ STANDARD_INPUT_TYPE,
+ null,
+ true, null, null, null, null,
+ true,
+ null, null,
+ null, null
+ );
+ }
+
+ @Override
+ public boolean canDecodeInput(Object source) throws IOException {
+ return source instanceof ImageInputStream && canDecode((ImageInputStream) source);
+ }
+
+ private static boolean canDecode(ImageInputStream input) throws IOException {
+ try {
+ input.mark();
+ return input.readInt() == ICNS.MAGIC;
+ }
+ finally {
+ input.reset();
+ }
+ }
+
+ @Override
+ public ImageReader createReaderInstance(Object extension) throws IOException {
+ return new ICNSImageReader(this);
+ }
+
+ @Override
+ public String getDescription(Locale locale) {
+ return "Apple Icon Image (icns) format Reader";
+ }
+}
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
new file mode 100644
index 00000000..fb1246f4
--- /dev/null
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSUtil.java
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+/**
+ * ICNSUtil
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: ICNSUtil.java,v 1.0 26.10.11 11:49 haraldk Exp$
+ */
+final class ICNSUtil {
+
+ private ICNSUtil() {}
+
+ // TODO: Duplicated code from IFF plugin, move to some common util?
+ static String intToStr(int pChunkId) {
+ return new String(
+ new byte[]{
+ (byte) ((pChunkId & 0xff000000) >> 24),
+ (byte) ((pChunkId & 0x00ff0000) >> 16),
+ (byte) ((pChunkId & 0x0000ff00) >> 8),
+ (byte) ((pChunkId & 0x000000ff))
+ }
+ );
+ }
+}
diff --git a/imageio/imageio-icns/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/imageio/imageio-icns/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi
new file mode 100644
index 00000000..8e2e73e4
--- /dev/null
+++ b/imageio/imageio-icns/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+com.twelvemonkeys.imageio.plugins.icns.ICNSImageReaderSpi
\ No newline at end of file
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
new file mode 100644
index 00000000..c8534631
--- /dev/null
+++ b/imageio/imageio-icns/src/test/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.imageio.util.ImageReaderAbstractTestCase;
+
+import javax.imageio.ImageReader;
+import javax.imageio.spi.ImageReaderSpi;
+import java.awt.*;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * ICNSImageReaderTest
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: ICNSImageReaderTest.java,v 1.0 25.10.11 18:44 haraldk Exp$
+ */
+public class ICNSImageReaderTest extends ImageReaderAbstractTestCase {
+ @Override
+ protected List getTestData() {
+ return Arrays.asList(
+ new TestData(
+ getClassLoaderResource("/icns/GenericJavaApp.icns"),
+ new Dimension(16, 16), new Dimension(16, 16), // 1 bit, 8 bit
+ new Dimension(16, 16), new Dimension(16, 16), // 24 bit + 8 bit mask
+ new Dimension(32, 32), new Dimension(32, 32),
+ new Dimension(32, 32), new Dimension(32, 32),
+ new Dimension(128, 128), new Dimension(128, 128)
+ ),
+ new TestData(
+ getClassLoaderResource("/icns/Apple Retro.icns"),
+ new Dimension(16, 16), new Dimension(16, 16), // 24 bit + 8 bit mask
+ new Dimension(32, 32), new Dimension(32, 32), // 24 bit + 8 bit mask
+ new Dimension(48, 48), new Dimension(48, 48) ,// 24 bit + 8 bit mask
+ new Dimension(128, 128), new Dimension(128, 128), // 24 bit + 8 bit mask
+ new Dimension(256, 256), // JPEG 2000
+ new Dimension(512, 512) // JPEG 2000
+ )
+ );
+ }
+
+ @Override
+ protected ImageReaderSpi createProvider() {
+ return new ICNSImageReaderSpi();
+ }
+
+ @Override
+ protected ImageReader createReader() {
+ return new ICNSImageReader();
+ }
+
+ @Override
+ protected Class getReaderClass() {
+ return ICNSImageReader.class;
+ }
+
+ @Override
+ protected List getFormatNames() {
+ return Arrays.asList("icns");
+ }
+
+
+ @Override
+ protected List getSuffixes() {
+ return Arrays.asList("icns");
+ }
+
+ @Override
+ protected List getMIMETypes() {
+ return Arrays.asList("image/x-apple-icons");
+ }
+}
diff --git a/imageio/imageio-icns/src/test/resources/icns/Apple Retro.icns b/imageio/imageio-icns/src/test/resources/icns/Apple Retro.icns
new file mode 100644
index 00000000..39fcfc0a
Binary files /dev/null and b/imageio/imageio-icns/src/test/resources/icns/Apple Retro.icns differ
diff --git a/imageio/imageio-icns/src/test/resources/icns/GenericJavaApp.icns b/imageio/imageio-icns/src/test/resources/icns/GenericJavaApp.icns
new file mode 100644
index 00000000..8ffb8d7d
Binary files /dev/null and b/imageio/imageio-icns/src/test/resources/icns/GenericJavaApp.icns differ