From e867c2125c039135a8e6714aedf16e9f0ada1924 Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Mon, 7 Nov 2011 18:13:24 +0100 Subject: [PATCH] Now correctly merges masks and color data. Added test case with icnV resource. --- .../imageio/plugins/icns/ICNS.java | 40 +- .../plugins/icns/ICNS1BitColorModel.java | 52 ++ .../plugins/icns/ICNS4BitColorModel.java | 52 ++ .../plugins/icns/ICNS8BitColorModel.java | 82 +++ .../imageio/plugins/icns/ICNSImageReader.java | 490 ++++++++++++------ .../plugins/icns/ICNSImageReaderTest.java | 31 +- .../src/test/resources/icns/7zIcon.icns | Bin 0 -> 72410 bytes 7 files changed, 546 insertions(+), 201 deletions(-) create mode 100644 imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS1BitColorModel.java create mode 100644 imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS4BitColorModel.java create mode 100644 imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS8BitColorModel.java create mode 100755 imageio/imageio-icns/src/test/resources/icns/7zIcon.icns 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 87cb0815..12309f6c 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 @@ -51,7 +51,7 @@ interface ICNS { /** 16×12 8 bit icon */ int icm8 = ('i' << 24) + ('c' << 16) + ('m' << 8) + '8'; - /** 16×16 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 */ int ics4 = ('i' << 24) + ('c' << 16) + ('s' << 8) + '4'; @@ -71,7 +71,7 @@ interface ICNS { /** 32×32 8-bit mask */ int l8mk = ('l' << 24) + ('8' << 16) + ('m' << 8) + 'k'; - /** 48×48 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 */ int ich4 = ('i' << 24) + ('c' << 16) + ('h' << 8) + '4'; @@ -87,41 +87,21 @@ interface ICNS { /** 128×128 8-bit mask */ int t8mk = ('t' << 24) + ('8' << 16) + ('m' << 8) + 'k'; - /** 256×256 JPEG 2000 or PNG icon */ + /** 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 */ + /** 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'; - /* - 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) - */ + /** Unknown */ + int icnV = ('i' << 24) + ('c' << 16) + ('n' << 8) + 'V'; + /** 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 */ 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/ICNS1BitColorModel.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS1BitColorModel.java new file mode 100644 index 00000000..12a42ff4 --- /dev/null +++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS1BitColorModel.java @@ -0,0 +1,52 @@ +/* + * 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 java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; + +/** + * ICNS1BitColorModel + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ICNS1BitColorModel.java,v 1.0 07.11.11 10:49 haraldk Exp$ + */ +final class ICNS1BitColorModel extends IndexColorModel { + private static final int[] CMAP = { + // Inverted from default Java 1 bit... + 0xffffffff, 0xff000000 + }; + + static final IndexColorModel INSTANCE = new ICNS1BitColorModel(); + + private ICNS1BitColorModel() { + super(1, 2, CMAP, 0, false, -1, DataBuffer.TYPE_BYTE); + } +} diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS4BitColorModel.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS4BitColorModel.java new file mode 100644 index 00000000..10a90f18 --- /dev/null +++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS4BitColorModel.java @@ -0,0 +1,52 @@ +/* + * 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 java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; + +/** + * ICNS4BitColorModel + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ICNS4BitColorModel.java,v 1.0 07.11.11 10:49 haraldk Exp$ + */ +final class ICNS4BitColorModel extends IndexColorModel { + private static final int[] CMAP = { + 0xffffffff, 0xfffcf305, 0xffff6402, 0xffdd0806, 0xfff20884, 0xff4600a5, 0xff0000d4, 0xff02abea, + 0xff1fb714, 0xff006411, 0xff562c05, 0xff90713a, 0xffc0c0c0, 0xff808080, 0xff404040, 0xff000000 + }; + + static final IndexColorModel INSTANCE = new ICNS4BitColorModel(); + + private ICNS4BitColorModel() { + super(4, 16, CMAP, 0, false, -1, DataBuffer.TYPE_BYTE); + } +} diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS8BitColorModel.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS8BitColorModel.java new file mode 100644 index 00000000..19d12f86 --- /dev/null +++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNS8BitColorModel.java @@ -0,0 +1,82 @@ +/* + * 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 java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; + +/** + * ICNS8BitColorModel + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ICNS8BitColorModel.java,v 1.0 07.11.11 10:49 haraldk Exp$ + */ +final class ICNS8BitColorModel extends IndexColorModel { + private static final int[] CMAP = { + 0xffffffff, 0xffffffcc, 0xffffff99, 0xffffff66, 0xffffff33, 0xffffff00, 0xffffccff, 0xffffcccc, + 0xffffcc99, 0xffffcc66, 0xffffcc33, 0xffffcc00, 0xffff99ff, 0xffff99cc, 0xffff9999, 0xffff9966, + 0xffff9933, 0xffff9900, 0xffff66ff, 0xffff66cc, 0xffff6699, 0xffff6666, 0xffff6633, 0xffff6600, + 0xffff33ff, 0xffff33cc, 0xffff3399, 0xffff3366, 0xffff3333, 0xffff3300, 0xffff00ff, 0xffff00cc, + 0xffff0099, 0xffff0066, 0xffff0033, 0xffff0000, 0xffccffff, 0xffccffcc, 0xffccff99, 0xffccff66, + 0xffccff33, 0xffccff00, 0xffccccff, 0xffcccccc, 0xffcccc99, 0xffcccc66, 0xffcccc33, 0xffcccc00, + 0xffcc99ff, 0xffcc99cc, 0xffcc9999, 0xffcc9966, 0xffcc9933, 0xffcc9900, 0xffcc66ff, 0xffcc66cc, + 0xffcc6699, 0xffcc6666, 0xffcc6633, 0xffcc6600, 0xffcc33ff, 0xffcc33cc, 0xffcc3399, 0xffcc3366, + 0xffcc3333, 0xffcc3300, 0xffcc00ff, 0xffcc00cc, 0xffcc0099, 0xffcc0066, 0xffcc0033, 0xffcc0000, + 0xff99ffff, 0xff99ffcc, 0xff99ff99, 0xff99ff66, 0xff99ff33, 0xff99ff00, 0xff99ccff, 0xff99cccc, + 0xff99cc99, 0xff99cc66, 0xff99cc33, 0xff99cc00, 0xff9999ff, 0xff9999cc, 0xff999999, 0xff999966, + 0xff999933, 0xff999900, 0xff9966ff, 0xff9966cc, 0xff996699, 0xff996666, 0xff996633, 0xff996600, + 0xff9933ff, 0xff9933cc, 0xff993399, 0xff993366, 0xff993333, 0xff993300, 0xff9900ff, 0xff9900cc, + 0xff990099, 0xff990066, 0xff990033, 0xff990000, 0xff66ffff, 0xff66ffcc, 0xff66ff99, 0xff66ff66, + 0xff66ff33, 0xff66ff00, 0xff66ccff, 0xff66cccc, 0xff66cc99, 0xff66cc66, 0xff66cc33, 0xff66cc00, + 0xff6699ff, 0xff6699cc, 0xff669999, 0xff669966, 0xff669933, 0xff669900, 0xff6666ff, 0xff6666cc, + 0xff666699, 0xff666666, 0xff666633, 0xff666600, 0xff6633ff, 0xff6633cc, 0xff663399, 0xff663366, + 0xff663333, 0xff663300, 0xff6600ff, 0xff6600cc, 0xff660099, 0xff660066, 0xff660033, 0xff660000, + 0xff33ffff, 0xff33ffcc, 0xff33ff99, 0xff33ff66, 0xff33ff33, 0xff33ff00, 0xff33ccff, 0xff33cccc, + 0xff33cc99, 0xff33cc66, 0xff33cc33, 0xff33cc00, 0xff3399ff, 0xff3399cc, 0xff339999, 0xff339966, + 0xff339933, 0xff339900, 0xff3366ff, 0xff3366cc, 0xff336699, 0xff336666, 0xff336633, 0xff336600, + 0xff3333ff, 0xff3333cc, 0xff333399, 0xff333366, 0xff333333, 0xff333300, 0xff3300ff, 0xff3300cc, + 0xff330099, 0xff330066, 0xff330033, 0xff330000, 0xff00ffff, 0xff00ffcc, 0xff00ff99, 0xff00ff66, + 0xff00ff33, 0xff00ff00, 0xff00ccff, 0xff00cccc, 0xff00cc99, 0xff00cc66, 0xff00cc33, 0xff00cc00, + 0xff0099ff, 0xff0099cc, 0xff009999, 0xff009966, 0xff009933, 0xff009900, 0xff0066ff, 0xff0066cc, + 0xff006699, 0xff006666, 0xff006633, 0xff006600, 0xff0033ff, 0xff0033cc, 0xff003399, 0xff003366, + 0xff003333, 0xff003300, 0xff0000ff, 0xff0000cc, 0xff000099, 0xff000066, 0xff000033, 0xffee0000, + 0xffdd0000, 0xffbb0000, 0xffaa0000, 0xff880000, 0xff770000, 0xff550000, 0xff440000, 0xff220000, + 0xff110000, 0xff00ee00, 0xff00dd00, 0xff00bb00, 0xff00aa00, 0xff008800, 0xff007700, 0xff005500, + 0xff004400, 0xff002200, 0xff001100, 0xff0000ee, 0xff0000dd, 0xff0000bb, 0xff0000aa, 0xff000088, + 0xff000077, 0xff000055, 0xff000044, 0xff000022, 0xff000011, 0xffeeeeee, 0xffdddddd, 0xffbbbbbb, + 0xffaaaaaa, 0xff888888, 0xff777777, 0xff555555, 0xff444444, 0xff222222, 0xff111111, 0xff000000 + }; + + static final IndexColorModel INSTANCE = new ICNS8BitColorModel(); + + private ICNS8BitColorModel() { + super(8, 256, CMAP, 0, false, -1, DataBuffer.TYPE_BYTE); + } +} 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 a4e34d80..842f9a41 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 @@ -30,6 +30,8 @@ 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; @@ -57,7 +59,9 @@ 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 List icons = new ArrayList(); + private List masks = new ArrayList(); + private int length; public ICNSImageReader() { @@ -70,6 +74,10 @@ public final class ICNSImageReader extends ImageReaderBase { @Override protected void resetMembers() { + length = 0; + + icons.clear(); + masks.clear(); } @Override @@ -82,31 +90,52 @@ public final class ICNSImageReader extends ImageReaderBase { return readIconHeader(imageIndex).size().height; } + @Override + public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException { + IconHeader header = readIconHeader(imageIndex); + + switch (header.depth()) { + case 1: + return IndexedImageTypeSpecifier.createFromIndexColorModel(ICNS1BitColorModel.INSTANCE); + case 4: + return IndexedImageTypeSpecifier.createFromIndexColorModel(ICNS4BitColorModel.INSTANCE); + case 8: + return IndexedImageTypeSpecifier.createFromIndexColorModel(ICNS8BitColorModel.INSTANCE); + case 32: + int bandLen = header.size().width * header.size().height; + return ImageTypeSpecifier.createBanded(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{0, 1, 2, 3}, new int[]{0, bandLen, 2 * bandLen, 3 * bandLen}, DataBuffer.TYPE_BYTE, true, false); + default: + throw new IllegalStateException(String.format("Unknown bit depth: %d", header.depth())); + } + } + @Override public Iterator getImageTypes(int imageIndex) throws IOException { + ImageTypeSpecifier rawType = getRawImageType(imageIndex); IconHeader header = readIconHeader(imageIndex); List specifiers = new ArrayList(); switch (header.depth()) { case 1: - specifiers.add(ImageTypeSpecifier.createGrayscale(1, DataBuffer.TYPE_BYTE, false)); - // Fall through +// break; + // TODO: Fall through & convert during read? case 4: - specifiers.add(ImageTypeSpecifier.createGrayscale(4, DataBuffer.TYPE_BYTE, false)); - // Fall through +// break; + // TODO: Fall through & convert during read? 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)); +// break; + // TODO: Fall through & convert during read? case 32: - specifiers.add(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{0, 1, 2, 3}, DataBuffer.TYPE_BYTE, true, false)); + specifiers.add(ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB), 0xff0000, 0x00ff00, 0x0000ff, 0xff000000, DataBuffer.TYPE_INT, false)); + 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())); } + specifiers.add(rawType); + return specifiers.iterator(); } @@ -115,21 +144,21 @@ public final class ICNSImageReader extends ImageReaderBase { assertInput(); if (!allowSearch) { + // Return icons.size if we know we have read all? return -1; } - int num = iconHeaders.size(); + int num = icons.size(); while (true) { try { - readIconHeader(num); - num++; + readIconHeader(num++); } catch (IndexOutOfBoundsException expected) { break; } } - return num; + return icons.size(); } @Override @@ -139,86 +168,55 @@ public final class ICNSImageReader extends ImageReaderBase { 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(); - } + return readForeignFormat(param, header); } + return readICNSFormat(imageIndex, param, header); + } + + private BufferedImage readICNSFormat(final int imageIndex, final ImageReadParam param, final IconHeader header) throws IOException { 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()); + if (rawType instanceof IndexedImageTypeSpecifier && rawType.getBufferedImageType() != image.getType()) { + checkReadParamBandSettings(param, 4, image.getSampleModel().getNumBands()); + } + else { + checkReadParamBandSettings(param, rawType.getNumBands(), image.getSampleModel().getNumBands()); + } final Rectangle source = new Rectangle(); final Rectangle dest = new Rectangle(); computeRegions(param, width, height, image, source, dest); + processImageStarted(imageIndex); + // Read image data byte[] data; - if (header.isPackbits()) { + if (header.isCompressed()) { + // Only 32 bit icons may be compressed data = new byte[width * height * header.depth() / 8]; int packedSize = header.length - HEADER_SIZE; + if (width >= 128 && height >= 128) { - imageInput.skipBytes(4); + imageInput.skipBytes(4); // Seems to be 4 byte 0-pad packedSize -= 4; } InputStream input = IIOUtil.createStreamAdapter(imageInput, packedSize); - unpackbits(new DataInputStream(input), data, 0, data.length); - input.close(); + try { + decompress(new DataInputStream(input), data, 0, (data.length * 24) / 32); // 24 bit data + } + finally { + input.close(); + } } else { data = new byte[header.length - HEADER_SIZE]; @@ -232,47 +230,183 @@ public final class ICNSImageReader extends ImageReaderBase { break; case 8: break; - case 24: + case 32: break; default: throw new IllegalStateException(String.format("Unknown bit depth for icon: %d", header.depth())); } - if (header.depth() <= 8) { + if (header.depth() == 1) { + DataBufferByte buffer = new DataBufferByte(data, data.length / 2, 0); + WritableRaster raster = Raster.createPackedRaster(buffer, width, height, header.depth(), null); + + if (image.getType() == rawType.getBufferedImageType() && ((IndexColorModel) image.getColorModel()).getMapSize() == 2) { + image.setData(raster); + } + else { + DataBufferByte maskBuffer = new DataBufferByte(data, data.length / 2, data.length / 2); + WritableRaster mask = Raster.createPackedRaster(maskBuffer, width, height, header.depth(), null); + + Graphics2D graphics = image.createGraphics(); + try { + BufferedImage temp = new BufferedImage(rawType.getColorModel(), raster, false, null); + graphics.drawImage(temp, 0, 0, null); + + temp = new BufferedImage(ICNSBitMaskColorModel.INSTANCE, mask, false, null); + temp.setData(mask); + graphics.setComposite(AlphaComposite.DstIn); + graphics.drawImage(temp, 0, 0, null); + } + finally { + graphics.dispose(); + } + } + } + else if (header.depth() <= 8) { DataBufferByte buffer = new DataBufferByte(data, data.length); - image.setData(Raster.createPackedRaster(buffer, width, height, header.depth(), null)); + WritableRaster raster = Raster.createPackedRaster(buffer, width, height, header.depth(), null); + + if (image.getType() == rawType.getBufferedImageType()) { + image.setData(raster); + } + else { + Graphics2D graphics = image.createGraphics(); + try { + BufferedImage temp = new BufferedImage(rawType.getColorModel(), raster, false, null); + graphics.drawImage(temp, 0, 0, null); + } + finally { + graphics.dispose(); + } + + processImageProgress(50f); + + // Look up/read mask from later IconHeader and apply + Raster mask = readMask(findMask(header)); + image.getAlphaRaster().setRect(mask); + } } 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; + 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}, new int[]{0, bandLen, bandLen * 2}, null); - ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + WritableRaster raster = Raster.createBandedRaster(buffer, width, height, width, new int[]{0, 0, 0, 0}, new int[]{0, bandLen, bandLen * 2, bandLen * 3}, null); + image.setData(raster); - BufferedImage temp = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null); -// showIt(temp, "foo"); + processImageProgress(75f); -// image.setData(raster); - Graphics2D graphics = image.createGraphics(); - try { - graphics.drawImage(temp, 0, 0, null); - } - finally { - graphics.dispose(); - } + // Read mask from later IconHeader and apply + Raster mask = readMask(findMask(header)); + image.getAlphaRaster().setRect(mask); + } + + // For now: Make listener tests happy + // TODO: Implement more sophisticated reading + processImageProgress(100f); + + if (abortRequested()) { + processReadAborted(); + } + else { + processImageComplete(); } return image; } + private Raster readMask(IconHeader header) throws IOException { + Dimension size = header.size(); + + int width = size.width; + int height = size.height; + + byte[] alpha = new byte[header.length - HEADER_SIZE]; + + imageInput.seek(header.start + HEADER_SIZE); + imageInput.readFully(alpha); + + return Raster.createBandedRaster(new DataBufferByte(alpha, alpha.length), width, height, width, new int[]{0}, new int[]{0}, null); + } + + private IconHeader findMask(final IconHeader icon) throws IOException { + try { + int i = 0; + + while (true) { + IconHeader mask = i < masks.size() ? masks.get(i++) : readNextIconHeader(); + + if (mask.isMask() && mask.size().equals(icon.size())) { + return mask; + } + } + } + catch (IndexOutOfBoundsException ignore) { + } + + throw new IIOException(String.format("No mask for icon: %s", icon)); + } + + private BufferedImage readForeignFormat(final ImageReadParam param, final IconHeader header) throws IOException { + ImageInputStream stream = ImageIO.createImageInputStream(IIOUtil.createStreamAdapter(imageInput, header.length)); + + try { + Iterator readers = ImageIO.getImageReaders(stream); + + while (readers.hasNext()) { + ImageReader reader = readers.next(); + reader.setInput(stream); + + try { + return reader.read(0, param); + } + catch (IOException ignore) { + } + finally { + stream.seek(0); + } + } + + // There's no JPEG 2000 reader installed in ImageIO by default (requires JAI ImageIO installed). + // The current implementation is correct, but a bit harsh maybe..? Other options: + // TODO: Return blank icon + issue warning? We know the image dimensions, we just can't read the data... + // TODO: Pretend it's not in the stream + issue warning? + // 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()) + )); + } + finally { + stream.close(); + } + } + + private String getForeignFormat(final ImageInputStream stream) throws IOException { + byte[] magic = new byte[12]; // Length of JPEG 2000 magic + try { + stream.readFully(magic); + } + finally { + stream.seek(0); + } + + 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"; + } + + return format; + } + + // http://www.macdisk.com/maciconen.php // 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 { + static void decompress(final DataInputStream input, final byte[] result, int offset, int length) throws IOException { int resultPos = offset; int remaining = length; @@ -282,11 +416,11 @@ public final class ICNSImageReader extends ImageReaderBase { 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... + 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; + result[resultPos++] = runData; } } else { @@ -301,27 +435,43 @@ public final class ICNSImageReader extends ImageReaderBase { } } - private IconHeader readIconHeader(int imageIndex) throws IOException { + private IconHeader readIconHeader(final 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); - } + while (icons.size() <= imageIndex) { + readNextIconHeader(); } - return iconHeaders.get(imageIndex); + 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 + ); + + imageInput.seek(lastReadPos); + + if (imageInput.getStreamPosition() >= length) { + throw new IndexOutOfBoundsException(); + } + + IconHeader header = IconHeader.read(imageInput); + + // Filter out special case icnV (version?), as this isn't really an icon.. + if (header.isMask() || header.type == ICNS.icnV) { + masks.add(header); + } + else { + icons.add(header); + } + + return header; } private void readeFileHeader() throws IOException { @@ -359,64 +509,51 @@ public final class ICNSImageReader extends ImageReaderBase { private void validate(int type, int length) { switch (type) { case ICNS.ICON: - if (length == 128) { - return; - } + validateLengthForType(type, length, 128); + break; case ICNS.ICN_: - if (length == 256) { - return; - } + validateLengthForType(type, length, 256); + break; case ICNS.icm_: - if (length == 24) { - return; - } + validateLengthForType(type, length, 24); + break; case ICNS.icm4: - if (length == 96) { - return; - } + validateLengthForType(type, length, 96); + break; case ICNS.icm8: - if (length == 192) { - return; - } + validateLengthForType(type, length, 192); + break; case ICNS.ics_: - if (length == 32) { - return; - } + validateLengthForType(type, length, 64); + break; case ICNS.ics4: - if (length == 128) { - return; - } + validateLengthForType(type, length, 128); + break; case ICNS.ics8: case ICNS.s8mk: - if (length == 256) { - return; - } + validateLengthForType(type, length, 256); + break; case ICNS.icl4: - if (length == 512) { - return; - } + validateLengthForType(type, length, 512); + break; case ICNS.icl8: case ICNS.l8mk: - if (length == 1024) { - return; - } + validateLengthForType(type, length, 1024); + break; case ICNS.ich_: - if (length == 288) { - return; - } +// validateLengthForType(type, length, 288); + validateLengthForType(type, length, 576); + break; case ICNS.ich4: - if (length == 1152) { - return; - } + validateLengthForType(type, length, 1152); + break; case ICNS.ich8: case ICNS.h8mk: - if (length == 2034) { - return; - } + validateLengthForType(type, length, 2304); + break; case ICNS.t8mk: - if (length == 16384) { - return; - } + validateLengthForType(type, length, 16384); + break; case ICNS.ih32: case ICNS.is32: case ICNS.il32: @@ -425,12 +562,26 @@ public final class ICNSImageReader extends ImageReaderBase { case ICNS.ic09: case ICNS.ic10: if (length > 0) { - return; + 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; default: throw new IllegalStateException(String.format("Unknown icon type: '%s'", ICNSUtil.intToStr(type))); } + + } + + private void validateLengthForType(int type, int length, final int expectedLength) { + Validate.isTrue( + length == expectedLength + 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 + ) + ); } public Dimension size() { @@ -502,17 +653,29 @@ public final class ICNSImageReader extends ImageReaderBase { case ICNS.ic08: case ICNS.ic09: case ICNS.ic10: - return 24; + return 32; default: throw new IllegalStateException(String.format("Unknown icon type: '%s'", ICNSUtil.intToStr(type))); } } - public boolean isPackbits() { + public boolean isMask() { + 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.ih32: - case ICNS.il32: case ICNS.is32: + case ICNS.il32: + case ICNS.ih32: case ICNS.it32: return true; } @@ -550,6 +713,7 @@ public final class ICNSImageReader extends ImageReaderBase { } } + @SuppressWarnings({"UnusedAssignment"}) public static void main(String[] args) throws IOException { int argIndex = 0; @@ -568,7 +732,7 @@ public final class ICNSImageReader extends ImageReaderBase { for (int i = start; i < numImages; i++) { try { BufferedImage image = reader.read(i); - System.err.println("image: " + image); +// System.err.println("image: " + image); showIt(image, String.format("%s - %d", input.getName(), i)); } catch (IIOException e) { @@ -576,4 +740,12 @@ public final class ICNSImageReader extends ImageReaderBase { } } } + + 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/test/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderTest.java b/imageio/imageio-icns/src/test/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReaderTest.java index c8534631..c31da2bd 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 @@ -49,20 +49,28 @@ public class ICNSImageReaderTest extends ImageReaderAbstractTestCase { 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 Dimension(16, 16), // 1 bit + 1 bit mask + new Dimension(16, 16), new Dimension(16, 16), // 8 bit CMAP, 32 bit + 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 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 + new Dimension(16, 16), // 24 bit + 8 bit mask + 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 TestData( + 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 ) ); } @@ -87,7 +95,6 @@ public class ICNSImageReaderTest extends ImageReaderAbstractTestCase { return Arrays.asList("icns"); } - @Override protected List getSuffixes() { return Arrays.asList("icns"); diff --git a/imageio/imageio-icns/src/test/resources/icns/7zIcon.icns b/imageio/imageio-icns/src/test/resources/icns/7zIcon.icns new file mode 100755 index 0000000000000000000000000000000000000000..32f1cd6a28265beeba5205de5dbd42fc624ca159 GIT binary patch literal 72410 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 #V=n~UOlI(=St5FuJ11|hbWrhm(m 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 Zf7|E1F{U{GhwJK~`LBl-Ui1b4_%8)UF>?R_ literal 0 HcmV?d00001