From 7546a9d2abdeb5883313e8d82d6810ed57dbebca Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Fri, 28 Oct 2011 17:23:13 +0200 Subject: [PATCH] Clean up. Moved implementation to abstract class, and made exposed readers final. --- .../imageio/plugins/ico/CURImageReader.java | 8 +- .../plugins/ico/CURImageReaderSpi.java | 2 +- .../imageio/plugins/ico/DIBImageReader.java | 689 ++++++++++++++++++ .../imageio/plugins/ico/DirectoryEntry.java | 10 +- .../imageio/plugins/ico/ICOImageReader.java | 668 +---------------- .../plugins/ico/ICOImageReaderSpi.java | 2 +- .../plugins/ico/CURImageReaderTestCase.java | 25 +- 7 files changed, 720 insertions(+), 684 deletions(-) create mode 100644 imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBImageReader.java 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