diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java
index 68519bf5..c58aca14 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java
+++ b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java
@@ -39,13 +39,11 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: CURImageReader.java,v 1.0 Apr 20, 2009 11:54:28 AM haraldk Exp$
*
- * @see com.twelvemonkeys.imageio.plugins.ico.ICOImageReader
+ * @see ICOImageReader
*/
-public class CURImageReader extends ICOImageReader {
- // NOTE: All implementation is part of the ICOImageReader
-
+public final class CURImageReader extends DIBImageReader {
public CURImageReader() {
- super(DIB.TYPE_CUR);
+ super(new CURImageReaderSpi());
}
protected CURImageReader(final ImageReaderSpi pProvider) {
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java
index 9b38ea85..43f127e8 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java
+++ b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java
@@ -43,7 +43,7 @@ import java.util.Locale;
* @author Harald Kuhr
* @version $Id: CURImageReaderSpi.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
-public class CURImageReaderSpi extends ImageReaderSpi {
+public final class CURImageReaderSpi extends ImageReaderSpi {
public CURImageReaderSpi() {
this(IIOUtil.getProviderInfo(CURImageReaderSpi.class));
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBImageReader.java b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBImageReader.java
new file mode 100644
index 00000000..a39ad143
--- /dev/null
+++ b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBImageReader.java
@@ -0,0 +1,689 @@
+/*
+ * Copyright (c) 2009, 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.ico;
+
+import com.twelvemonkeys.image.ImageUtil;
+import com.twelvemonkeys.imageio.ImageReaderBase;
+import com.twelvemonkeys.imageio.util.IIOUtil;
+import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
+import com.twelvemonkeys.util.WeakWeakMap;
+
+import javax.imageio.*;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.color.ColorSpace;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteOrder;
+import java.util.*;
+import java.util.List;
+
+/**
+ * ImageReader for Microsoft Windows ICO (icon) format.
+ * 1, 4, 8 bit palette support with bitmask transparency, and 16, 24 and 32 bit
+ * true color support with alpha. Also supports Windows Vista PNG encoded icons.
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: ICOImageReader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
+ *
+ * @see BMP file format (Wikipedia)
+ * @see ICO file format (Wikipedia)
+ */
+// SEE http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)
+// TODO: Decide wether DirectoryEntry or DIBHeader should be primary source for color count/bit count
+// TODO: Support loading icons from DLLs, see
+// MSDN
+// Known issue: 256x256 PNG encoded icons does not have IndexColorModel even if stated in DirectoryEntry (seem impossible as the PNGs are all true color)
+abstract class DIBImageReader extends ImageReaderBase {
+ // TODO: Consider moving the reading to inner classes (subclasses of BitmapDescriptor)
+ private Directory directory;
+
+ // TODO: Review these, make sure we don't have a memory leak
+ private Map headers = new WeakHashMap();
+ private Map descriptors = new WeakWeakMap();
+
+ private ImageReader pngImageReader;
+
+ protected DIBImageReader(final ImageReaderSpi pProvider) {
+ super(pProvider);
+ }
+
+ protected void resetMembers() {
+ directory = null;
+
+ headers.clear();
+ descriptors.clear();
+
+ if (pngImageReader != null) {
+ pngImageReader.dispose();
+ pngImageReader = null;
+ }
+ }
+
+ public Iterator getImageTypes(final int pImageIndex) throws IOException {
+ DirectoryEntry entry = getEntry(pImageIndex);
+
+ // NOTE: Delegate to PNG reader
+ if (isPNG(entry)) {
+ return getImageTypesPNG(entry);
+ }
+
+ List types = new ArrayList();
+ DIBHeader header = getHeader(entry);
+
+ // Use data from header to create specifier
+ ImageTypeSpecifier specifier;
+ switch (header.getBitCount()) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ // TODO: This is slightly QnD...
+ int offset = entry.getOffset() + header.getSize();
+ if (offset != imageInput.getStreamPosition()) {
+ imageInput.seek(offset);
+ }
+ BitmapIndexed indexed = new BitmapIndexed(entry, header);
+ readColorMap(indexed);
+ specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(indexed.createColorModel());
+ break;
+ case 16:
+ specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
+ break;
+ case 24:
+ specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
+ break;
+ case 32:
+ specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
+ break;
+ default:
+ throw new IIOException(String.format("Unknown bit depth: %d", header.getBitCount()));
+ }
+
+ types.add(specifier);
+
+ return types.iterator();
+ }
+
+ @Override
+ public int getNumImages(final boolean allowSearch) throws IOException {
+ return getDirectory().count();
+ }
+
+ public int getWidth(final int pImageIndex) throws IOException {
+ return getEntry(pImageIndex).getWidth();
+ }
+
+ public int getHeight(final int pImageIndex) throws IOException {
+ return getEntry(pImageIndex).getHeight();
+ }
+
+ public BufferedImage read(final int pImageIndex, final ImageReadParam pParam) throws IOException {
+ checkBounds(pImageIndex);
+
+ processImageStarted(pImageIndex);
+
+ DirectoryEntry entry = getEntry(pImageIndex);
+
+ BufferedImage destination;
+
+ if (isPNG(entry)) {
+ // NOTE: Special case for Windows Vista, 256x256 PNG encoded images, with no DIB header...
+ destination = readPNG(entry, pParam);
+ }
+ else {
+ // NOTE: If param does not have explicit destination, we'll try to create a BufferedImage later,
+ // to allow for storing the cursor hotspot for CUR images
+ destination = hasExplicitDestination(pParam) ?
+ getDestination(pParam, getImageTypes(pImageIndex), getWidth(pImageIndex), getHeight(pImageIndex)) : null;
+
+ BufferedImage image = readBitmap(entry);
+
+ // TODO: Handle AOI and subsampling inline, probably not of big importance...
+ if (pParam != null) {
+ image = fakeAOI(image, pParam);
+ image = ImageUtil.toBuffered(fakeSubsampling(image, pParam));
+ }
+
+ if (destination == null) {
+ // This is okay, as long as the client did not request explicit destination image/type
+ destination = image;
+ }
+ else {
+ Graphics2D g = destination.createGraphics();
+ try {
+ g.setComposite(AlphaComposite.Src);
+ g.drawImage(image, 0, 0, null);
+ }
+ finally {
+ g.dispose();
+ }
+ }
+ }
+
+ processImageProgress(100);
+ processImageComplete();
+
+ return destination;
+ }
+
+ private boolean isPNG(final DirectoryEntry pEntry) throws IOException {
+ long magic;
+
+ imageInput.seek(pEntry.getOffset());
+ imageInput.setByteOrder(ByteOrder.BIG_ENDIAN);
+
+ try {
+ magic = imageInput.readLong();
+ }
+ finally {
+ imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ }
+
+ return magic == DIB.PNG_MAGIC;
+ }
+
+ private BufferedImage readPNG(final DirectoryEntry pEntry, final ImageReadParam pParam) throws IOException {
+ // TODO: Consider delegating listener calls
+ return initPNGReader(pEntry).read(0, pParam);
+ }
+
+ private Iterator getImageTypesPNG(final DirectoryEntry pEntry) throws IOException {
+ return initPNGReader(pEntry).getImageTypes(0);
+ }
+
+ private ImageReader initPNGReader(final DirectoryEntry pEntry) throws IOException {
+ ImageReader pngReader = getPNGReader();
+
+ imageInput.seek(pEntry.getOffset());
+ InputStream inputStream = IIOUtil.createStreamAdapter(imageInput, pEntry.getSize());
+ ImageInputStream stream = ImageIO.createImageInputStream(inputStream);
+
+ // NOTE: Will throw IOException on later reads if input is not PNG
+ pngReader.setInput(stream);
+
+ return pngReader;
+ }
+
+ private ImageReader getPNGReader() throws IIOException {
+ // TODO: Prefer Sun's std JDK PNGImagerReader, because it has known behaviour?
+ if (pngImageReader == null) {
+ Iterator readers = ImageIO.getImageReadersByFormatName("PNG");
+
+ if (readers.hasNext()) {
+ pngImageReader = readers.next();
+ }
+ else {
+ throw new IIOException("No PNGImageReader found using ImageIO, can't read PNG encoded ICO format.");
+ }
+ }
+ else {
+ pngImageReader.reset();
+ }
+
+ return pngImageReader;
+ }
+
+ private DIBHeader getHeader(final DirectoryEntry pEntry) throws IOException {
+ if (!headers.containsKey(pEntry)) {
+ imageInput.seek(pEntry.getOffset());
+ DIBHeader header = DIBHeader.read(imageInput);
+ headers.put(pEntry, header);
+ }
+
+ return headers.get(pEntry);
+ }
+
+ private BufferedImage readBitmap(final DirectoryEntry pEntry) throws IOException {
+ // TODO: Get rid of the caching, as the images are mutable
+ BitmapDescriptor descriptor = descriptors.get(pEntry);
+
+ if (descriptor == null || !descriptors.containsKey(pEntry)) {
+ DIBHeader header = getHeader(pEntry);
+
+ int offset = pEntry.getOffset() + header.getSize();
+ if (offset != imageInput.getStreamPosition()) {
+ imageInput.seek(offset);
+ }
+
+ // TODO: Support this, it's already in the BMP reader, spec allows RLE4 and RLE8
+ if (header.getCompression() != 0) {
+ descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported compression: %d", header.getCompression()));
+ }
+ else {
+ int bitCount = header.getBitCount();
+
+ switch (bitCount) {
+ // Palette style
+ case 1:
+ case 4:
+ case 8:
+ descriptor = new BitmapIndexed(pEntry, header);
+ readBitmapIndexed((BitmapIndexed) descriptor);
+ break;
+ // RGB style
+ case 16:
+ descriptor = new BitmapRGB(pEntry, header);
+ readBitmap16(descriptor);
+ break;
+ case 24:
+ descriptor = new BitmapRGB(pEntry, header);
+ readBitmap24(descriptor);
+ break;
+ case 32:
+ descriptor = new BitmapRGB(pEntry, header);
+ readBitmap32(descriptor);
+ break;
+
+ default:
+ descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported bit count %d", bitCount));
+ }
+ }
+
+ descriptors.put(pEntry, descriptor);
+ }
+
+ return descriptor.getImage();
+ }
+
+ private void readBitmapIndexed(final BitmapIndexed pBitmap) throws IOException {
+ readColorMap(pBitmap);
+
+ switch (pBitmap.getBitCount()) {
+ case 1:
+ readBitmapIndexed1(pBitmap, false);
+ break;
+ case 4:
+ readBitmapIndexed4(pBitmap);
+ break;
+ case 8:
+ readBitmapIndexed8(pBitmap);
+ break;
+ }
+
+ BitmapMask mask = new BitmapMask(pBitmap.entry, pBitmap.header);
+ readBitmapIndexed1(mask.mask, true);
+ pBitmap.setMask(mask);
+ }
+
+ private void readColorMap(final BitmapIndexed pBitmap) throws IOException {
+ int colorCount = pBitmap.getColorCount();
+
+ for (int i = 0; i < colorCount; i++) {
+ // aRGB (a is "Reserved")
+ pBitmap.colors[i] = (imageInput.readInt() & 0xffffff) | 0xff000000;
+ }
+ }
+
+ private void readBitmapIndexed1(final BitmapIndexed pBitmap, final boolean pAsMask) throws IOException {
+ int width = adjustToPadding(pBitmap.getWidth() >> 3);
+ byte[] row = new byte[width];
+
+ for (int y = 0; y < pBitmap.getHeight(); y++) {
+ imageInput.readFully(row, 0, width);
+ int rowPos = 0;
+ int xOrVal = 0x80;
+ int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
+
+ for (int x = 0; x < pBitmap.getWidth(); x++) {
+ pBitmap.bits[pos++] = ((row[rowPos] & xOrVal) / xOrVal) & 0xFF;
+
+ if (xOrVal == 1) {
+ xOrVal = 0x80;
+ rowPos++;
+ }
+ else {
+ xOrVal >>= 1;
+ }
+ }
+
+ // NOTE: If we are reading the mask, we don't abort or progress
+ if (!pAsMask) {
+ if (abortRequested()) {
+ processReadAborted();
+ break;
+ }
+
+ processImageProgress(100 * y / (float) pBitmap.getHeight());
+ }
+ }
+ }
+
+ private void readBitmapIndexed4(final BitmapIndexed pBitmap) throws IOException {
+ int width = adjustToPadding(pBitmap.getWidth() >> 1);
+ byte[] row = new byte[width];
+
+ for (int y = 0; y < pBitmap.getHeight(); y++) {
+ imageInput.readFully(row, 0, width);
+ int rowPos = 0;
+ boolean high4 = true;
+ int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
+
+ for (int x = 0; x < pBitmap.getWidth(); x++) {
+ int value;
+
+ if (high4) {
+ value = (row[rowPos] & 0xF0) >> 4;
+ }
+ else {
+ value = row[rowPos] & 0x0F;
+ rowPos++;
+ }
+
+ pBitmap.bits[pos++] = value & 0xFF;
+ high4 = !high4;
+ }
+
+ if (abortRequested()) {
+ processReadAborted();
+ break;
+ }
+
+ processImageProgress(100 * y / (float) pBitmap.getHeight());
+ }
+ }
+
+ private void readBitmapIndexed8(final BitmapIndexed pBitmap) throws IOException {
+ int width = adjustToPadding(pBitmap.getWidth());
+
+ byte[] row = new byte[width];
+
+ for (int y = 0; y < pBitmap.getHeight(); y++) {
+ imageInput.readFully(row, 0, width);
+ int rowPos = 0;
+ int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
+
+ for (int x = 0; x < pBitmap.getWidth(); x++) {
+ pBitmap.bits[pos++] = row[rowPos++] & 0xFF;
+ }
+
+ if (abortRequested()) {
+ processReadAborted();
+ break;
+ }
+
+ processImageProgress(100 * y / (float) pBitmap.getHeight());
+ }
+ }
+
+ /**
+ * @param pWidth Bytes per scan line (i.e., 1BPP, width = 9 -> bytes = 1)
+ * @return padded width
+ */
+ private static int adjustToPadding(final int pWidth) {
+ if ((pWidth & 0x03) != 0) {
+ return (pWidth & ~0x03) + 4;
+ }
+ return pWidth;
+ }
+
+ private void readBitmap16(final BitmapDescriptor pBitmap) throws IOException {
+ // TODO: No idea if this actually works..
+ short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
+
+ // Will create TYPE_USHORT_555;
+ DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
+ DataBuffer buffer = new DataBufferShort(pixels, pixels.length);
+ WritableRaster raster = Raster.createPackedRaster(
+ buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
+ );
+ pBitmap.image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
+
+ for (int y = 0; y < pBitmap.getHeight(); y++) {
+ int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
+ imageInput.readFully(pixels, offset, pBitmap.getWidth());
+
+
+ // Skip to 32 bit boundary
+ if (pBitmap.getWidth() % 2 != 0) {
+ imageInput.readShort();
+ }
+
+ if (abortRequested()) {
+ processReadAborted();
+ break;
+ }
+
+ processImageProgress(100 * y / (float) pBitmap.getHeight());
+ }
+ }
+
+ private void readBitmap24(final BitmapDescriptor pBitmap) throws IOException {
+ byte[] pixels = new byte[pBitmap.getWidth() * pBitmap.getHeight() * 3];
+
+ // Create TYPE_3BYTE_BGR
+ DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ int[] nBits = {8, 8, 8};
+ int[] bOffs = {2, 1, 0};
+ ComponentColorModel cm = new ComponentColorModel(
+ cs, nBits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE
+ );
+
+ WritableRaster raster = Raster.createInterleavedRaster(
+ buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), 3, bOffs, null
+ );
+ pBitmap.image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
+
+ for (int y = 0; y < pBitmap.getHeight(); y++) {
+ int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
+ imageInput.readFully(pixels, offset, pBitmap.getWidth() * 3);
+
+ // TODO: Possibly read padding byte here!
+
+ if (abortRequested()) {
+ processReadAborted();
+ break;
+ }
+
+ processImageProgress(100 * y / (float) pBitmap.getHeight());
+ }
+ }
+
+ private void readBitmap32(final BitmapDescriptor pBitmap) throws IOException {
+ int[] pixels = new int[pBitmap.getWidth() * pBitmap.getHeight()];
+
+ // Will create TYPE_INT_ARGB
+ DirectColorModel cm = (DirectColorModel) ColorModel.getRGBdefault();
+ DataBuffer buffer = new DataBufferInt(pixels, pixels.length);
+ WritableRaster raster = Raster.createPackedRaster(
+ buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
+ );
+ pBitmap.image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
+
+ for (int y = 0; y < pBitmap.getHeight(); y++) {
+ int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
+ imageInput.readFully(pixels, offset, pBitmap.getWidth());
+
+ if (abortRequested()) {
+ processReadAborted();
+ break;
+ }
+ processImageProgress(100 * y / (float) pBitmap.getHeight());
+ }
+ }
+
+ private Directory getDirectory() throws IOException {
+ assertInput();
+
+ if (directory == null) {
+ readFileHeader();
+ }
+
+ return directory;
+ }
+
+ private void readFileHeader() throws IOException {
+ imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+
+ // Read file header
+ imageInput.readUnsignedShort(); // Reserved
+
+ // Should be same as type as the provider
+ int type = imageInput.readUnsignedShort();
+ int imageCount = imageInput.readUnsignedShort();
+
+ // Read directory
+ directory = Directory.read(type, imageCount, imageInput);
+ }
+
+ final DirectoryEntry getEntry(final int pImageIndex) throws IOException {
+ Directory directory = getDirectory();
+ if (pImageIndex < 0 || pImageIndex >= directory.count()) {
+ throw new IndexOutOfBoundsException(String.format("Index: %d, ImageCount: %d", pImageIndex, directory.count()));
+ }
+
+ return directory.getEntry(pImageIndex);
+ }
+
+ /// Test code below, ignore.. :-)
+ public static void main(final String[] pArgs) throws IOException {
+ if (pArgs.length == 0) {
+ System.err.println("Please specify the icon file name");
+ System.exit(1);
+ }
+
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ }
+ catch (Exception e) {
+ // Ignore
+ }
+
+ String title = new File(pArgs[0]).getName();
+ JFrame frame = createWindow(title);
+ JPanel root = new JPanel(new FlowLayout());
+ JScrollPane scroll =
+ new JScrollPane(root, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ scroll.setBorder(BorderFactory.createEmptyBorder());
+ frame.setContentPane(scroll);
+
+ Iterator readers = ImageIO.getImageReadersByFormatName("ico");
+ if (!readers.hasNext()) {
+ System.err.println("No reader for format 'ico' found");
+ System.exit(1);
+ }
+
+ ImageReader reader = readers.next();
+
+ for (String arg : pArgs) {
+ JPanel panel = new JPanel(null);
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ readImagesInFile(arg, reader, panel);
+ root.add(panel);
+ }
+
+ frame.pack();
+ frame.setVisible(true);
+ }
+
+ private static void readImagesInFile(String pFileName, ImageReader pReader, final Container pContainer) throws IOException {
+ File file = new File(pFileName);
+ if (!file.isFile()) {
+ System.err.println(pFileName + " not found, or is no file");
+ }
+
+ pReader.setInput(ImageIO.createImageInputStream(file));
+ int imageCount = pReader.getNumImages(true);
+ for (int i = 0; i < imageCount; i++) {
+ try {
+ addImage(pContainer, pReader, i);
+ }
+ catch (Exception e) {
+ System.err.println("FileName: " + pFileName);
+ System.err.println("Icon: " + i);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static JFrame createWindow(final String pTitle) {
+ JFrame frame = new JFrame(pTitle);
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosed(WindowEvent e) {
+ System.exit(0);
+ }
+ });
+ return frame;
+ }
+
+ private static void addImage(final Container pParent, final ImageReader pReader, final int pImageNo) throws IOException {
+ final JButton button = new JButton();
+
+ BufferedImage image = pReader.read(pImageNo);
+ button.setIcon(new ImageIcon(image) {
+ TexturePaint texture;
+
+ private void createTexture(final GraphicsConfiguration pGraphicsConfiguration) {
+ BufferedImage pattern = pGraphicsConfiguration.createCompatibleImage(20, 20);
+ Graphics2D g = pattern.createGraphics();
+ try {
+ g.setColor(Color.LIGHT_GRAY);
+ g.fillRect(0, 0, pattern.getWidth(), pattern.getHeight());
+ g.setColor(Color.GRAY);
+ g.fillRect(0, 0, pattern.getWidth() / 2, pattern.getHeight() / 2);
+ g.fillRect(pattern.getWidth() / 2, pattern.getHeight() / 2, pattern.getWidth() / 2, pattern.getHeight() / 2);
+ }
+ finally {
+ g.dispose();
+ }
+
+ texture = new TexturePaint(pattern, new Rectangle(pattern.getWidth(), pattern.getHeight()));
+ }
+
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ if (texture == null) {
+ createTexture(c.getGraphicsConfiguration());
+ }
+
+ Graphics2D gr = (Graphics2D) g;
+ gr.setPaint(texture);
+ gr.fillRect(x, y, getIconWidth(), getIconHeight());
+ super.paintIcon(c, g, x, y);
+ }
+ });
+
+ button.setText("" + image.getWidth() + "x" +
+ image.getHeight() + ": "
+ + ((image.getColorModel() instanceof IndexColorModel) ?
+ "" + ((IndexColorModel) image.getColorModel()).getMapSize() :
+ "TrueColor"));
+
+ pParent.add(button);
+ }
+}
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java
index 41e49f19..6b023762 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java
+++ b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java
@@ -136,23 +136,23 @@ abstract class DirectoryEntry {
* Cursor directory entry.
*/
static class CUREntry extends DirectoryEntry {
- private int mXHotspot;
- private int mYHotspot;
+ private int xHotspot;
+ private int yHotspot;
@Override
protected void read(final DataInput pStream) throws IOException {
super.read(pStream);
// NOTE: This is a hack...
- mXHotspot = planes;
- mYHotspot = bitCount;
+ xHotspot = planes;
+ yHotspot = bitCount;
planes = 1; // Always 1 for all BMP types
bitCount = 0;
}
public Point getHotspot() {
- return new Point(mXHotspot, mYHotspot);
+ return new Point(xHotspot, yHotspot);
}
}
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java
index 33c1a842..9d228565 100644
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java
+++ b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, Harald Kuhr
+ * Copyright (c) 2011, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,678 +28,24 @@
package com.twelvemonkeys.imageio.plugins.ico;
-import com.twelvemonkeys.image.ImageUtil;
-import com.twelvemonkeys.imageio.ImageReaderBase;
-import com.twelvemonkeys.imageio.util.IIOUtil;
-import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
-import com.twelvemonkeys.util.WeakWeakMap;
-
-import javax.imageio.*;
import javax.imageio.spi.ImageReaderSpi;
-import javax.imageio.stream.ImageInputStream;
-import javax.swing.*;
-import java.awt.*;
-import java.awt.color.ColorSpace;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.awt.image.*;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteOrder;
-import java.util.*;
-import java.util.List;
/**
- * ImageReader for Microsoft Windows ICO (icon) format.
- * 1, 4, 8 bit palette support with bitmask transparency, and 16, 24 and 32 bit
- * true color support with alpha. Also supports Windows Vista PNG encoded icons.
- *
+ * ImageReader for Microsoft Windows CUR (cursor) format.
*
* @author Harald Kuhr
- * @version $Id: ICOImageReader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
+ * @author last modified by $Author: haraldk$
+ * @version $Id: CURImageReader.java,v 1.0 Apr 20, 2009 11:54:28 AM haraldk Exp$
*
- * @see BMP file format (Wikipedia)
- * @see ICO file format (Wikipedia)
+ * @see CURImageReader
*/
-// SEE http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)
-// TODO: Decide wether DirectoryEntry or DIBHeader should be primary source for color count/bit count
-// TODO: Support loading icons from DLLs, see
-// MSDN
-// Known issue: 256x256 PNG encoded icons does not have IndexColorModel even if stated in DirectoryEntry (seem impossible as the PNGs are all true color)
-public class ICOImageReader extends ImageReaderBase {
- // TODO: Consider moving the reading to inner classes (subclasses of BitmapDescriptor)
- private Directory directory;
-
- // TODO: Review these, make sure we don't have a memory leak
- private Map headers = new WeakHashMap();
- private Map descriptors = new WeakWeakMap();
-
- private ImageReader pngImageReader;
-
+public final class ICOImageReader extends DIBImageReader {
public ICOImageReader() {
- this(DIB.TYPE_ICO);
- }
-
- ICOImageReader(final int pType) {
- this(createProviderForConstructor(pType));
+ super(new ICOImageReaderSpi());
}
protected ICOImageReader(final ImageReaderSpi pProvider) {
super(pProvider);
}
- private static ImageReaderSpi createProviderForConstructor(final int pType) {
- switch (pType) {
- case DIB.TYPE_ICO:
- return new ICOImageReaderSpi();
- case DIB.TYPE_CUR:
- return new CURImageReaderSpi();
- default:
- throw new IllegalArgumentException(String.format("Unsupported ICO/CUR type: %d", pType));
- }
- }
-
- protected void resetMembers() {
- directory = null;
-
- headers.clear();
- descriptors.clear();
-
- if (pngImageReader != null) {
- pngImageReader.dispose();
- pngImageReader = null;
- }
- }
-
- public Iterator getImageTypes(final int pImageIndex) throws IOException {
- DirectoryEntry entry = getEntry(pImageIndex);
-
- // NOTE: Delegate to PNG reader
- if (isPNG(entry)) {
- return getImageTypesPNG(entry);
- }
-
- List types = new ArrayList();
- DIBHeader header = getHeader(entry);
-
- // Use data from header to create specifier
- ImageTypeSpecifier specifier;
- switch (header.getBitCount()) {
- case 1:
- case 2:
- case 4:
- case 8:
- // TODO: This is slightly QnD...
- int offset = entry.getOffset() + header.getSize();
- if (offset != imageInput.getStreamPosition()) {
- imageInput.seek(offset);
- }
- BitmapIndexed indexed = new BitmapIndexed(entry, header);
- readColorMap(indexed);
- specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(indexed.createColorModel());
- break;
- case 16:
- specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
- break;
- case 24:
- specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
- break;
- case 32:
- specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
- break;
- default:
- throw new IIOException(String.format("Unknown bit depth: %d", header.getBitCount()));
- }
-
- types.add(specifier);
-
- return types.iterator();
- }
-
- @Override
- public int getNumImages(final boolean allowSearch) throws IOException {
- return getDirectory().count();
- }
-
- public int getWidth(final int pImageIndex) throws IOException {
- return getEntry(pImageIndex).getWidth();
- }
-
- public int getHeight(final int pImageIndex) throws IOException {
- return getEntry(pImageIndex).getHeight();
- }
-
- public BufferedImage read(final int pImageIndex, final ImageReadParam pParam) throws IOException {
- checkBounds(pImageIndex);
-
- processImageStarted(pImageIndex);
-
- DirectoryEntry entry = getEntry(pImageIndex);
-
- BufferedImage destination;
-
- if (isPNG(entry)) {
- // NOTE: Special case for Windows Vista, 256x256 PNG encoded images, with no DIB header...
- destination = readPNG(entry, pParam);
- }
- else {
- // NOTE: If param does not have explicit destination, we'll try to create a BufferedImage later,
- // to allow for storing the cursor hotspot for CUR images
- destination = hasExplicitDestination(pParam) ?
- getDestination(pParam, getImageTypes(pImageIndex), getWidth(pImageIndex), getHeight(pImageIndex)) :
- null;
-
- BufferedImage image = readBitmap(entry);
-
- // TODO: Handle AOI and subsampling inline, probably not of big importance...
- if (pParam != null) {
- image = fakeAOI(image, pParam);
- image = ImageUtil.toBuffered(fakeSubsampling(image, pParam));
- }
-
- if (destination == null) {
- // This is okay, as long as the client did not request explicit destination image/type
- destination = image;
- }
- else {
- Graphics2D g = destination.createGraphics();
- try {
- g.setComposite(AlphaComposite.Src);
- g.drawImage(image, 0, 0, null);
- }
- finally {
- g.dispose();
- }
- }
- }
-
- processImageProgress(100);
- processImageComplete();
-
- return destination;
- }
-
- private boolean isPNG(final DirectoryEntry pEntry) throws IOException {
- long magic;
-
- imageInput.seek(pEntry.getOffset());
- imageInput.setByteOrder(ByteOrder.BIG_ENDIAN);
-
- try {
- magic = imageInput.readLong();
- }
- finally {
- imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
- }
-
- return magic == DIB.PNG_MAGIC;
- }
-
- private BufferedImage readPNG(final DirectoryEntry pEntry, final ImageReadParam pParam) throws IOException {
- // TODO: Consider delegating listener calls
- return initPNGReader(pEntry).read(0, pParam);
- }
-
- private Iterator getImageTypesPNG(final DirectoryEntry pEntry) throws IOException {
- return initPNGReader(pEntry).getImageTypes(0);
- }
-
- private ImageReader initPNGReader(final DirectoryEntry pEntry) throws IOException {
- ImageReader pngReader = getPNGReader();
-
- imageInput.seek(pEntry.getOffset());
- InputStream inputStream = IIOUtil.createStreamAdapter(imageInput, pEntry.getSize());
- ImageInputStream stream = ImageIO.createImageInputStream(inputStream);
-
- // NOTE: Will throw IOException on later reads if input is not PNG
- pngReader.setInput(stream);
-
- return pngReader;
- }
-
- private ImageReader getPNGReader() throws IIOException {
- // TODO: Prefer Sun's std JDK PNGImagerReader, because it has known behaviour?
- if (pngImageReader == null) {
- Iterator readers = ImageIO.getImageReadersByFormatName("PNG");
-
- if (readers.hasNext()) {
- pngImageReader = readers.next();
- }
- else {
- throw new IIOException("No PNGImageReader found using ImageIO, can't read PNG encoded ICO format.");
- }
- }
- else {
- pngImageReader.reset();
- }
-
- return pngImageReader;
- }
-
- private DIBHeader getHeader(final DirectoryEntry pEntry) throws IOException {
- if (!headers.containsKey(pEntry)) {
- imageInput.seek(pEntry.getOffset());
- DIBHeader header = DIBHeader.read(imageInput);
- headers.put(pEntry, header);
- }
-
- return headers.get(pEntry);
- }
-
- private BufferedImage readBitmap(final DirectoryEntry pEntry) throws IOException {
- // TODO: Get rid of the caching, as the images are mutable
- BitmapDescriptor descriptor = descriptors.get(pEntry);
-
- if (descriptor == null || !descriptors.containsKey(pEntry)) {
- DIBHeader header = getHeader(pEntry);
-
- int offset = pEntry.getOffset() + header.getSize();
- if (offset != imageInput.getStreamPosition()) {
- imageInput.seek(offset);
- }
-
- // TODO: Support this, it's already in the BMP reader, spec allows RLE4 and RLE8
- if (header.getCompression() != 0) {
- descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported compression: %d", header.getCompression()));
- }
- else {
- int bitCount = header.getBitCount();
-
- switch (bitCount) {
- // Palette style
- case 1:
- case 4:
- case 8:
- descriptor = new BitmapIndexed(pEntry, header);
- readBitmapIndexed((BitmapIndexed) descriptor);
- break;
- // RGB style
- case 16:
- descriptor = new BitmapRGB(pEntry, header);
- readBitmap16(descriptor);
- break;
- case 24:
- descriptor = new BitmapRGB(pEntry, header);
- readBitmap24(descriptor);
- break;
- case 32:
- descriptor = new BitmapRGB(pEntry, header);
- readBitmap32(descriptor);
- break;
-
- default:
- descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported bit count %d", bitCount));
- }
- }
-
- descriptors.put(pEntry, descriptor);
- }
-
- return descriptor.getImage();
- }
-
- private void readBitmapIndexed(final BitmapIndexed pBitmap) throws IOException {
- readColorMap(pBitmap);
-
- switch (pBitmap.getBitCount()) {
- case 1:
- readBitmapIndexed1(pBitmap, false);
- break;
- case 4:
- readBitmapIndexed4(pBitmap);
- break;
- case 8:
- readBitmapIndexed8(pBitmap);
- break;
- }
-
- BitmapMask mask = new BitmapMask(pBitmap.entry, pBitmap.header);
- readBitmapIndexed1(mask.mask, true);
- pBitmap.setMask(mask);
- }
-
- private void readColorMap(final BitmapIndexed pBitmap) throws IOException {
- int colorCount = pBitmap.getColorCount();
-
- for (int i = 0; i < colorCount; i++) {
- // aRGB (a is "Reserved")
- pBitmap.colors[i] = (imageInput.readInt() & 0xffffff) | 0xff000000;
- }
- }
-
- private void readBitmapIndexed1(final BitmapIndexed pBitmap, final boolean pAsMask) throws IOException {
- int width = adjustToPadding(pBitmap.getWidth() >> 3);
- byte[] row = new byte[width];
-
- for (int y = 0; y < pBitmap.getHeight(); y++) {
- imageInput.readFully(row, 0, width);
- int rowPos = 0;
- int xOrVal = 0x80;
- int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
-
- for (int x = 0; x < pBitmap.getWidth(); x++) {
- pBitmap.bits[pos++] = ((row[rowPos] & xOrVal) / xOrVal) & 0xFF;
-
- if (xOrVal == 1) {
- xOrVal = 0x80;
- rowPos++;
- }
- else {
- xOrVal >>= 1;
- }
- }
-
- // NOTE: If we are reading the mask, we don't abort or progress
- if (!pAsMask) {
- if (abortRequested()) {
- processReadAborted();
- break;
- }
-
- processImageProgress(100 * y / (float) pBitmap.getHeight());
- }
- }
- }
-
- private void readBitmapIndexed4(final BitmapIndexed pBitmap) throws IOException {
- int width = adjustToPadding(pBitmap.getWidth() >> 1);
- byte[] row = new byte[width];
-
- for (int y = 0; y < pBitmap.getHeight(); y++) {
- imageInput.readFully(row, 0, width);
- int rowPos = 0;
- boolean high4 = true;
- int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
-
- for (int x = 0; x < pBitmap.getWidth(); x++) {
- int value;
-
- if (high4) {
- value = (row[rowPos] & 0xF0) >> 4;
- }
- else {
- value = row[rowPos] & 0x0F;
- rowPos++;
- }
-
- pBitmap.bits[pos++] = value & 0xFF;
- high4 = !high4;
- }
-
- if (abortRequested()) {
- processReadAborted();
- break;
- }
-
- processImageProgress(100 * y / (float) pBitmap.getHeight());
- }
- }
-
- private void readBitmapIndexed8(final BitmapIndexed pBitmap) throws IOException {
- int width = adjustToPadding(pBitmap.getWidth());
-
- byte[] row = new byte[width];
-
- for (int y = 0; y < pBitmap.getHeight(); y++) {
- imageInput.readFully(row, 0, width);
- int rowPos = 0;
- int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
-
- for (int x = 0; x < pBitmap.getWidth(); x++) {
- pBitmap.bits[pos++] = row[rowPos++] & 0xFF;
- }
-
- if (abortRequested()) {
- processReadAborted();
- break;
- }
-
- processImageProgress(100 * y / (float) pBitmap.getHeight());
- }
- }
-
- /**
- * @param pWidth Bytes per scan line (i.e., 1BPP, width = 9 -> bytes = 1)
- * @return padded width
- */
- private static int adjustToPadding(final int pWidth) {
- if ((pWidth & 0x03) != 0) {
- return (pWidth & ~0x03) + 4;
- }
- return pWidth;
- }
-
- private void readBitmap16(final BitmapDescriptor pBitmap) throws IOException {
- // TODO: No idea if this actually works..
- short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
-
- // Will create TYPE_USHORT_555;
- DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
- DataBuffer buffer = new DataBufferShort(pixels, pixels.length);
- WritableRaster raster = Raster.createPackedRaster(
- buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
- );
- pBitmap.image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
-
- for (int y = 0; y < pBitmap.getHeight(); y++) {
- int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
- imageInput.readFully(pixels, offset, pBitmap.getWidth());
-
-
- // Skip to 32 bit boundary
- if (pBitmap.getWidth() % 2 != 0) {
- imageInput.readShort();
- }
-
- if (abortRequested()) {
- processReadAborted();
- break;
- }
-
- processImageProgress(100 * y / (float) pBitmap.getHeight());
- }
- }
-
- private void readBitmap24(final BitmapDescriptor pBitmap) throws IOException {
- byte[] pixels = new byte[pBitmap.getWidth() * pBitmap.getHeight() * 3];
-
- // Create TYPE_3BYTE_BGR
- DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
- ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
- int[] nBits = {8, 8, 8};
- int[] bOffs = {2, 1, 0};
- ComponentColorModel cm = new ComponentColorModel(
- cs, nBits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE
- );
-
- WritableRaster raster = Raster.createInterleavedRaster(
- buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), 3, bOffs, null
- );
- pBitmap.image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
-
- for (int y = 0; y < pBitmap.getHeight(); y++) {
- int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
- imageInput.readFully(pixels, offset, pBitmap.getWidth() * 3);
-
- // TODO: Possibly read padding byte here!
-
- if (abortRequested()) {
- processReadAborted();
- break;
- }
-
- processImageProgress(100 * y / (float) pBitmap.getHeight());
- }
- }
-
- private void readBitmap32(final BitmapDescriptor pBitmap) throws IOException {
- int[] pixels = new int[pBitmap.getWidth() * pBitmap.getHeight()];
-
- // Will create TYPE_INT_ARGB
- DirectColorModel cm = (DirectColorModel) ColorModel.getRGBdefault();
- DataBuffer buffer = new DataBufferInt(pixels, pixels.length);
- WritableRaster raster = Raster.createPackedRaster(
- buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
- );
- pBitmap.image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
-
- for (int y = 0; y < pBitmap.getHeight(); y++) {
- int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
- imageInput.readFully(pixels, offset, pBitmap.getWidth());
-
- if (abortRequested()) {
- processReadAborted();
- break;
- }
- processImageProgress(100 * y / (float) pBitmap.getHeight());
- }
- }
-
- private Directory getDirectory() throws IOException {
- assertInput();
-
- if (directory == null) {
- readFileHeader();
- }
-
- return directory;
- }
-
- private void readFileHeader() throws IOException {
- imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
-
- // Read file header
- imageInput.readUnsignedShort(); // Reserved
-
- // Should be same as type as the provider
- int type = imageInput.readUnsignedShort();
- int imageCount = imageInput.readUnsignedShort();
-
- // Read directory
- directory = Directory.read(type, imageCount, imageInput);
- }
-
- final DirectoryEntry getEntry(final int pImageIndex) throws IOException {
- Directory directory = getDirectory();
- if (pImageIndex < 0 || pImageIndex >= directory.count()) {
- throw new IndexOutOfBoundsException(String.format("Index: %d, ImageCount: %d", pImageIndex, directory.count()));
- }
-
- return directory.getEntry(pImageIndex);
- }
-
- /// Test code below, ignore.. :-)
- public static void main(final String[] pArgs) throws IOException {
- if (pArgs.length == 0) {
- System.err.println("Please specify the icon file name");
- System.exit(1);
- }
-
- try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- }
- catch (Exception e) {
- // Ignore
- }
-
- String title = new File(pArgs[0]).getName();
- JFrame frame = createWindow(title);
- JPanel root = new JPanel(new FlowLayout());
- JScrollPane scroll =
- new JScrollPane(root, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
- scroll.setBorder(BorderFactory.createEmptyBorder());
- frame.setContentPane(scroll);
-
- Iterator readers = ImageIO.getImageReadersByFormatName("ico");
- if (!readers.hasNext()) {
- System.err.println("No reader for format 'ico' found");
- System.exit(1);
- }
-
- ImageReader reader = readers.next();
-
- for (String arg : pArgs) {
- JPanel panel = new JPanel(null);
- panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
- readImagesInFile(arg, reader, panel);
- root.add(panel);
- }
-
- frame.pack();
- frame.setVisible(true);
- }
-
- private static void readImagesInFile(String pFileName, ImageReader pReader, final Container pContainer) throws IOException {
- File file = new File(pFileName);
- if (!file.isFile()) {
- System.err.println(pFileName + " not found, or is no file");
- }
-
- pReader.setInput(ImageIO.createImageInputStream(file));
- int imageCount = pReader.getNumImages(true);
- for (int i = 0; i < imageCount; i++) {
- try {
- addImage(pContainer, pReader, i);
- }
- catch (Exception e) {
- System.err.println("FileName: " + pFileName);
- System.err.println("Icon: " + i);
- e.printStackTrace();
- }
- }
- }
-
- private static JFrame createWindow(final String pTitle) {
- JFrame frame = new JFrame(pTitle);
- frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
- frame.addWindowListener(new WindowAdapter() {
- public void windowClosed(WindowEvent e) {
- System.exit(0);
- }
- });
- return frame;
- }
-
- private static void addImage(final Container pParent, final ImageReader pReader, final int pImageNo) throws IOException {
- final JButton button = new JButton();
- BufferedImage image = pReader.read(pImageNo);
- button.setIcon(new ImageIcon(image) {
- TexturePaint mTexture;
-
- private void createTexture(final GraphicsConfiguration pGraphicsConfiguration) {
- BufferedImage pattern = pGraphicsConfiguration.createCompatibleImage(20, 20);
- Graphics2D g = pattern.createGraphics();
- try {
- g.setColor(Color.LIGHT_GRAY);
- g.fillRect(0, 0, pattern.getWidth(), pattern.getHeight());
- g.setColor(Color.GRAY);
- g.fillRect(0, 0, pattern.getWidth() / 2, pattern.getHeight() / 2);
- g.fillRect(pattern.getWidth() / 2, pattern.getHeight() / 2, pattern.getWidth() / 2, pattern.getHeight() / 2);
- }
- finally {
- g.dispose();
- }
-
- mTexture = new TexturePaint(pattern, new Rectangle(pattern.getWidth(), pattern.getHeight()));
- }
-
- @Override
- public void paintIcon(Component c, Graphics g, int x, int y) {
- if (mTexture == null) {
- createTexture(c.getGraphicsConfiguration());
- }
- Graphics2D gr = (Graphics2D) g;
- gr.setPaint(mTexture);
- gr.fillRect(x, y, getIconWidth(), getIconHeight());
- super.paintIcon(c, g, x, y);
- }
- });
- button.setText("" + image.getWidth() + "x" +
- image.getHeight() + ": "
- + ((image.getColorModel() instanceof IndexColorModel) ?
- "" + ((IndexColorModel) image.getColorModel()).getMapSize() :
- "TrueColor"));
- pParent.add(button);
- }
}
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java
index f75cbfb9..eebeda17 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java
+++ b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java
@@ -43,7 +43,7 @@ import java.util.Locale;
* @author Harald Kuhr
* @version $Id: ICOImageReaderSpi.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
-public class ICOImageReaderSpi extends ImageReaderSpi {
+public final class ICOImageReaderSpi extends ImageReaderSpi {
public ICOImageReaderSpi() {
this(IIOUtil.getProviderInfo(ICOImageReaderSpi.class));
diff --git a/imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java b/imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java
index d8aea766..6de82c79 100755
--- a/imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java
+++ b/imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java
@@ -55,20 +55,23 @@ public class CURImageReaderTestCase extends ImageReaderAbstractTestCase