It all works

This commit is contained in:
Erlend Hamnaberg
2009-11-08 19:52:30 +01:00
parent b8faa6e36f
commit 0786949c1c
319 changed files with 0 additions and 0 deletions
@@ -0,0 +1,72 @@
/*
* 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.lang.Validate;
import java.awt.image.BufferedImage;
/**
* Describes a bitmap structure.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: Bitmap.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
abstract class BitmapDescriptor {
protected final DirectoryEntry mEntry;
protected final DIBHeader mHeader;
protected BufferedImage mImage;
public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) {
Validate.notNull(pEntry, "entry");
Validate.notNull(pHeader, "header");
mEntry = pEntry;
mHeader = pHeader;
}
abstract public BufferedImage getImage();
public final int getWidth() {
return mEntry.getWidth();
}
public final int getHeight() {
return mEntry.getHeight();
}
protected final int getColorCount() {
return mEntry.getColorCount() != 0 ? mEntry.getColorCount() : 1 << getBitCount();
}
protected final int getBitCount() {
return mEntry.getBitCount() != 0 ? mEntry.getBitCount() : mHeader.getBitCount();
}
}
@@ -0,0 +1,182 @@
/*
* 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.InverseColorMapIndexColorModel;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.util.Hashtable;
/**
* Describes an indexed bitmap structure (1, 4, or 8 bits per pixes).
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BitmapIndexed.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class BitmapIndexed extends BitmapDescriptor {
protected final int[] mBits;
protected final int[] mColors;
private BitmapMask mMask;
public BitmapIndexed(final DirectoryEntry pEntry, final DIBHeader pHeader) {
super(pEntry, pHeader);
mBits = new int[getWidth() * getHeight()];
// NOTE: We're adding space for one extra color, for transparency
mColors = new int[getColorCount() + 1];
}
public BufferedImage createImageIndexed() {
// TODO: This is very stupid, maybe we need a TYPE_CUSTOM image, with separate alphaRaster?!
// As ICO has a separate bitmask, not related to palette index (allows 256 colors + trans) :-P
IndexColorModel icm = createColorModel();
// This is slightly obscure, and should probably be moved..
Hashtable<String, Object> properties = null;
if (mEntry instanceof DirectoryEntry.CUREntry) {
DirectoryEntry.CUREntry entry = (DirectoryEntry.CUREntry) mEntry;
properties = new Hashtable<String, Object>(1);
properties.put("cursor_hotspot", entry.getHotspot());
}
BufferedImage image = new BufferedImage(
icm,
icm.createCompatibleWritableRaster(getWidth(), getHeight()),
icm.isAlphaPremultiplied(), properties
);
WritableRaster raster = image.getRaster();
// Make pixels transparant according to mask
final int trans = icm.getTransparentPixel();
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
if (mMask.isTransparent(x, y)) {
mBits[x + getWidth() * y] = trans;
}
}
}
raster.setSamples(0, 0, getWidth(), getHeight(), 0, mBits);
//System.out.println("Image: " + image);
return image;
}
/**
* @return Color model created from color palette in entry
*/
IndexColorModel createColorModel() {
// NOTE: This is a hack to make room for transparent pixel for mask
int bits = getBitCount();
int colors = mColors.length;
int trans = -1;
// Try to avoid USHORT transfertype, as it results in BufferedImage TYPE_CUSTOM
// NOTE: This code assumes icons are small, and is NOT optimized for performance...
if (colors > (1 << getBitCount())) {
int index = BitmapIndexed.findTransIndexMaybeRemap(mColors, mBits);
if (index == -1) {
// No duplicate found, increase bitcount
bits++;
trans = mColors.length - 1;
}
else {
// Found a duplicate, use it as trans
trans = index;
colors--;
}
}
// NOTE: Setting hasAlpha to true, makes things work on 1.2
return new InverseColorMapIndexColorModel(
bits, colors, mColors, 0, true, trans,
bits <= 8 ? DataBuffer.TYPE_BYTE : DataBuffer.TYPE_USHORT
);
}
private static int findTransIndexMaybeRemap(final int[] pColors, final int[] pBits) {
// Look for unused colors, to use as transparent
final boolean[] used = new boolean[pColors.length - 1];
for (int pBit : pBits) {
if (!used[pBit]) {
used[pBit] = true;
}
}
for (int i = 0; i < used.length; i++) {
if (!used[i]) {
return i;
}
}
// Try to find duplicates in colormap, and remap
int trans = -1;
int duplicate = -1;
for (int i = 0; trans == -1 && i < pColors.length - 1; i++) {
for (int j = i + 1; j < pColors.length - 1; j++) {
if (pColors[i] == pColors[j]) {
trans = j;
duplicate = i;
break;
}
}
}
if (trans != -1) {
// Remap duplicate
for (int i = 0; i < pBits.length; i++) {
if (pBits[i] == trans) {
pBits[i] = duplicate;
}
}
}
return trans;
}
public BufferedImage getImage() {
if (mImage == null) {
mImage = createImageIndexed();
}
return mImage;
}
public void setMask(final BitmapMask pMask) {
mMask = pMask;
}
}
@@ -0,0 +1,56 @@
/*
* 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 java.awt.image.BufferedImage;
/**
* Describes a transparency mask structure (1 bit).
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BitmapMask.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class BitmapMask extends BitmapDescriptor {
protected final BitmapIndexed mMask;
public BitmapMask(final DirectoryEntry pParent, final DIBHeader pHeader) {
super(pParent, pHeader);
mMask = new BitmapIndexed(pParent, pHeader);
}
boolean isTransparent(final int pX, final int pY) {
// NOTE: 1: Fully transparent, 0: Opaque...
return mMask.mBits[pX + pY * getWidth()] != 0;
}
public BufferedImage getImage() {
return mMask.getImage();
}
}
@@ -0,0 +1,48 @@
/*
* 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 java.awt.image.BufferedImage;
/**
* Describes an RGB/true color bitmap structure (16, 24 and 32 bits per pixel).
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BitmapRGB.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class BitmapRGB extends BitmapDescriptor {
public BitmapRGB(final DirectoryEntry pEntry, final DIBHeader pHeader) {
super(pEntry, pHeader);
}
public BufferedImage getImage() {
return mImage;
}
}
@@ -0,0 +1,52 @@
/*
* 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 java.awt.image.BufferedImage;
/**
* Represents bitmap structures we can't read.
* Allows for deferred exception handling, and allowing clients to read all images that can be read.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BitmapUnsupported.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class BitmapUnsupported extends BitmapDescriptor {
private String mMessage;
public BitmapUnsupported(final DirectoryEntry pEntry, final String pMessage) {
super(pEntry, null);
mMessage = pMessage;
}
public BufferedImage getImage() {
throw new IllegalStateException(mMessage);
}
}
@@ -0,0 +1,69 @@
/*
* 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 javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.io.IOException;
/**
* ImageReader for Microsoft Windows CUR (cursor) format.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @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
*/
public class CURImageReader extends ICOImageReader {
// NOTE: All implementation is part of the ICOImageReader
public CURImageReader() {
super(DIB.TYPE_CUR);
}
protected CURImageReader(final ImageReaderSpi pProvider) {
super(pProvider);
}
/**
* Returns the hot spot location for the cursor.
*
* @param pImageIndex the index of the cursor in the current input.
* @return the hot spot location for the cursor
*
* @throws IOException if an I/O exception occurs during reading of image meta data
* @throws IndexOutOfBoundsException if {@code pImageIndex} is less than {@code 0} or greater than/equal to
* the number of cursors in the file
*/
public final Point getHotSpot(final int pImageIndex) throws IOException {
DirectoryEntry.CUREntry entry = (DirectoryEntry.CUREntry) getEntry(pImageIndex);
return entry.getHotspot();
}
}
@@ -0,0 +1,84 @@
/*
* 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.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* CURImageReaderSpi
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CURImageReaderSpi.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
public class CURImageReaderSpi extends ImageReaderSpi {
public CURImageReaderSpi() {
this(IIOUtil.getProviderInfo(CURImageReaderSpi.class));
}
private CURImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(),
pProviderInfo.getVersion(),
new String[]{"cur", "CUR"},
new String[]{"cur"},
new String[]{
"image/vnd.microsoft.cursor", // Official IANA MIME
"image/x-cursor", // Common extension MIME
"image/cursor" // Unofficial, but common
},
"com.twelvemonkeys.imageio.plugins.ico.CURImageReader",
STANDARD_INPUT_TYPE,
null,
true, null, null, null, null,
true,
null, null,
null, null
);
}
public boolean canDecodeInput(final Object pSource) throws IOException {
return pSource instanceof ImageInputStream && ICOImageReaderSpi.canDecode((ImageInputStream) pSource, DIB.TYPE_CUR);
}
public ImageReader createReaderInstance(final Object pExtension) throws IOException {
return new CURImageReader(this);
}
public String getDescription(final Locale pLocale) {
return "Windows Cursor Format (CUR) Reader";
}
}
@@ -0,0 +1,66 @@
/*
* 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;
/**
* DIB
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: DIB.java,v 1.0 Apr 8, 2008 1:43:04 PM haraldk Exp$
*
* @see <a href="http://en.wikipedia.org/wiki/BMP_file_format">BMP file format (Wikipedia)</a>
* @see <a href="http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)">ICO file format (Wikipedia)</a>
*/
interface DIB {
int TYPE_UNKNOWN = 0;
int TYPE_ICO = 1;
int TYPE_CUR = 2;
/** BITMAPCOREHEADER size, OS/2 V1 */
int OS2_V1_HEADER_SIZE = 12;
/** BITMAPCOREHEADER size, OS/2 V2 */
int OS2_V2_HEADER_SIZE = 64;
/**
* BITMAPINFOHEADER size, Windows 3.0 and later.
* This is the most commonly used header for persistent bitmaps
*/
int WINDOWS_V3_HEADER_SIZE = 40;
/** BITMAPV4HEADER size, Windows 95/NT4 and later */
int WINDOWS_V4_HEADER_SIZE = 108;
/** BITMAPV5HEADER size, Windows 98/2000 and later */
int WINDOWS_V5_HEADER_SIZE = 124;
/** PNG "magic" identifier */
long PNG_MAGIC = 0x89l << 56 | (long) 'P' << 48 | (long) 'N' << 40 | (long) 'G' << 32 | 0x0dl << 24 | 0x0al << 16 | 0x1al << 8 | 0x0al;
}
@@ -0,0 +1,197 @@
/*
* 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 javax.imageio.IIOException;
import java.io.DataInput;
import java.io.IOException;
/**
* Represents the DIB (Device Independent Bitmap) Information header structure.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: DIBHeader.java,v 1.0 May 5, 2009 10:45:31 AM haraldk Exp$
* @see <a href="http://en.wikipedia.org/wiki/BMP_file_format">BMP file format (Wikipedia)</a>
*/
abstract class DIBHeader {
protected int mSize;
protected int mWidth;
// NOTE: If a bitmask is present, this value includes the height of the mask
// (so often header.height = entry.height * 2)
protected int mHeight;
protected int mPlanes;
protected int mBitCount;
/**
* 0 = BI_RGB: No compression
* 1 = BI_RLE8: 8 bit RLE Compression (8 bit only)
* 2 = BI_RLE4: 4 bit RLE Compression (4 bit only)
* 3 = BI_BITFIELDS: No compression (16 & 32 bit only)
*/
protected int mCompression;
// May be 0 if not known
protected int mImageSize;
protected int mXPixelsPerMeter;
protected int mYPixelsPerMeter;
protected int mColorsUsed;
// 0 means all colors are important
protected int mColorsImportant;
protected DIBHeader() {
}
public static DIBHeader read(final DataInput pStream) throws IOException {
int size = pStream.readInt();
// ICO always uses the Microsoft Windows V3 DIB header, which is 40 bytes
DIBHeader header = createHeader(size);
header.read(size, pStream);
return header;
}
private static DIBHeader createHeader(final int pSize) throws IOException {
switch (pSize) {
case DIB.OS2_V1_HEADER_SIZE:
case DIB.OS2_V2_HEADER_SIZE:
throw new IIOException(String.format("OS/2 Bitmap Information Header (size: %s) not supported", pSize));
case DIB.WINDOWS_V3_HEADER_SIZE:
return new WindowsV3DIBHeader();
case DIB.WINDOWS_V4_HEADER_SIZE:
case DIB.WINDOWS_V5_HEADER_SIZE:
throw new IIOException(String.format("Windows Bitmap Information Header (size: %s) not supported", pSize));
default:
throw new IIOException(String.format("Unknown Bitmap Information Header (size: %s)", pSize));
}
}
protected abstract void read(int pSize, DataInput pStream) throws IOException;
public final int getSize() {
return mSize;
}
public final int getWidth() {
return mWidth;
}
public final int getHeight() {
return mHeight;
}
public final int getPlanes() {
return mPlanes;
}
public final int getBitCount() {
return mBitCount;
}
public int getCompression() {
return mCompression;
}
public int getImageSize() {
return mImageSize;
}
public int getXPixelsPerMeter() {
return mXPixelsPerMeter;
}
public int getYPixelsPerMeter() {
return mYPixelsPerMeter;
}
public int getColorsUsed() {
return mColorsUsed;
}
public int getColorsImportant() {
return mColorsImportant;
}
public String toString() {
return String.format(
"%s: size: %d bytes, " +
"width: %d, height: %d, planes: %d, bit count: %d, compression: %d, " +
"image size: %d%s, " +
"X pixels per m: %d, Y pixels per m: %d, " +
"colors used: %d, colors important: %d%s",
getClass().getSimpleName(),
getSize(), getWidth(), getHeight(), getPlanes(), getBitCount(), getCompression(),
getImageSize(), (getImageSize() == 0 ? " (unknown)" : ""),
getXPixelsPerMeter(), getYPixelsPerMeter(),
getColorsUsed(), getColorsImportant(), (getColorsImportant() == 0 ? " (all)" : "")
);
}
/**
* Represents the DIB (Device Independent Bitmap) Windows V3 Bitmap Information header structure.
* This is the common format for persistent DIB structures, even if Windows
* may use the later versions at run-time.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: DIBHeader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
* @see <a href="http://en.wikipedia.org/wiki/BMP_file_format">BMP file format (Wikipedia)</a>
*/
static final class WindowsV3DIBHeader extends DIBHeader {
protected void read(final int pSize, final DataInput pStream) throws IOException {
if (pSize != DIB.WINDOWS_V3_HEADER_SIZE) {
throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.WINDOWS_V3_HEADER_SIZE));
}
mSize = pSize;
mWidth = pStream.readInt();
mHeight = pStream.readInt();
mPlanes = pStream.readUnsignedShort();
mBitCount = pStream.readUnsignedShort();
mCompression = pStream.readInt();
mImageSize = pStream.readInt();
mXPixelsPerMeter = pStream.readInt();
mYPixelsPerMeter = pStream.readInt();
mColorsUsed = pStream.readInt();
mColorsImportant = pStream.readInt();
}
}
}
@@ -0,0 +1,74 @@
/*
* 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 java.io.DataInput;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* Directory
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: Directory.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class Directory {
private final List<DirectoryEntry> mEntries;
private Directory(int pImageCount) {
mEntries = Arrays.asList(new DirectoryEntry[pImageCount]);
}
public static Directory read(final int pType, final int pImageCount, final DataInput pStream) throws IOException {
Directory directory = new Directory(pImageCount);
directory.readEntries(pType, pStream);
return directory;
}
private void readEntries(final int pType, final DataInput pStream) throws IOException {
for (int i = 0; i < mEntries.size(); i++) {
mEntries.set(i, DirectoryEntry.read(pType, pStream));
}
}
public DirectoryEntry getEntry(final int pEntryIndex) {
return mEntries.get(pEntryIndex);
}
public int count() {
return mEntries.size();
}
@Override
public String toString() {
return String.format("%s%s", getClass().getSimpleName(), mEntries);
}
}
@@ -0,0 +1,165 @@
/*
* 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 javax.imageio.IIOException;
import java.io.DataInput;
import java.io.IOException;
import java.awt.image.BufferedImage;
import java.awt.*;
/**
* DirectoryEntry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: DirectoryEntry.java,v 1.0 Apr 4, 2009 4:29:53 PM haraldk Exp$
* @see <a href="http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)#Directory">Wikipedia</a>
*/
abstract class DirectoryEntry {
private int mWidth;
private int mHeight;
private int mColorCount;
int mPlanes;
int mBitCount;
private int mSize;
private int mOffset;
private DirectoryEntry() {
}
public static DirectoryEntry read(final int pType, final DataInput pStream) throws IOException {
DirectoryEntry entry = createEntry(pType);
entry.read(pStream);
return entry;
}
private static DirectoryEntry createEntry(int pType) throws IIOException {
switch (pType) {
case DIB.TYPE_ICO:
return new ICOEntry();
case DIB.TYPE_CUR:
return new CUREntry();
default:
throw new IIOException(
String.format(
"Unknown DIB type: %s, expected: %s (ICO) or %s (CUR)",
pType, DIB.TYPE_ICO, DIB.TYPE_CUR
)
);
}
}
protected void read(final DataInput pStream) throws IOException {
// Width/height = 0, means 256
int w = pStream.readUnsignedByte();
mWidth = w == 0 ? 256 : w;
int h = pStream.readUnsignedByte();
mHeight = h == 0 ? 256 : h;
// Color count = 0, means 256 or more colors
mColorCount = pStream.readUnsignedByte();
// Ignore. Should be 0, but .NET (System.Drawing.Icon.Save) sets this value to 255, according to Wikipedia
pStream.readUnsignedByte();
mPlanes = pStream.readUnsignedShort(); // Should be 0 or 1 for ICO, x hotspot for CUR
mBitCount = pStream.readUnsignedShort(); // bit count for ICO, y hotspot for CUR
// Size of bitmap in bytes
mSize = pStream.readInt();
mOffset = pStream.readInt();
}
public String toString() {
return String.format(
"%s: width: %d, height: %d, colors: %d, planes: %d, bit count: %d, size: %d, offset: %d",
getClass().getSimpleName(),
mWidth, mHeight, mColorCount, mPlanes, mBitCount, mSize, mOffset
);
}
public int getBitCount() {
return mBitCount;
}
public int getColorCount() {
return mColorCount;
}
public int getHeight() {
return mHeight;
}
public int getOffset() {
return mOffset;
}
public int getPlanes() {
return mPlanes;
}
public int getSize() {
return mSize;
}
public int getWidth() {
return mWidth;
}
/**
* Cursor directory entry.
*/
static class CUREntry extends DirectoryEntry {
private int mXHotspot;
private int mYHotspot;
@Override
protected void read(final DataInput pStream) throws IOException {
super.read(pStream);
// NOTE: This is a hack...
mXHotspot = mPlanes;
mYHotspot = mBitCount;
mPlanes = 1; // Always 1 for all BMP types
mBitCount = 0;
}
public Point getHotspot() {
return new Point(mXHotspot, mYHotspot);
}
}
/**
* Icon directory entry.
*/
static final class ICOEntry extends DirectoryEntry {
}
}
@@ -0,0 +1,709 @@
/*
* 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.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ICOImageReader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*
* @see <a href="http://en.wikipedia.org/wiki/BMP_file_format">BMP file format (Wikipedia)</a>
* @see <a href="http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)">ICO file format (Wikipedia)</a>
*/
// 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
// <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwui/html/msdn_icons.asp">MSDN</a>
// 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 mDirectory;
// TODO: Review these, make sure we don't have a memory leak
private Map<DirectoryEntry, DIBHeader> mHeaders = new WeakHashMap<DirectoryEntry, DIBHeader>();
private Map<DirectoryEntry, BitmapDescriptor> mDescriptors = new WeakWeakMap<DirectoryEntry, BitmapDescriptor>();
private ImageReader mPNGImageReader;
public ICOImageReader() {
this(DIB.TYPE_ICO);
}
ICOImageReader(final int pType) {
this(createProviderForConstructor(pType));
}
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() {
mDirectory = null;
mHeaders.clear();
mDescriptors.clear();
if (mPNGImageReader != null) {
mPNGImageReader.dispose();
mPNGImageReader = null;
}
}
public Iterator<ImageTypeSpecifier> getImageTypes(final int pImageIndex) throws IOException {
DirectoryEntry entry = getEntry(pImageIndex);
// NOTE: Delegate to PNG reader
if (isPNG(entry)) {
return getImageTypesPNG(entry);
}
List<ImageTypeSpecifier> types = new ArrayList<ImageTypeSpecifier>();
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 != mImageInput.getStreamPosition()) {
mImageInput.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 pAllowSearch) 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 hasExplicitDestination(final ImageReadParam pParam) {
return (pParam != null && (pParam.getDestination() != null || pParam.getDestinationType() != null || pParam.getDestinationOffset() != null));
}
private boolean isPNG(final DirectoryEntry pEntry) throws IOException {
long magic;
mImageInput.seek(pEntry.getOffset());
mImageInput.setByteOrder(ByteOrder.BIG_ENDIAN);
try {
magic = mImageInput.readLong();
}
finally {
mImageInput.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<ImageTypeSpecifier> getImageTypesPNG(final DirectoryEntry pEntry) throws IOException {
return initPNGReader(pEntry).getImageTypes(0);
}
private ImageReader initPNGReader(final DirectoryEntry pEntry) throws IOException {
ImageReader pngReader = getPNGReader();
mImageInput.seek(pEntry.getOffset());
InputStream inputStream = IIOUtil.createStreamAdapter(mImageInput, 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 (mPNGImageReader == null) {
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("PNG");
if (readers.hasNext()) {
mPNGImageReader = readers.next();
}
else {
throw new IIOException("No PNGImageReader found using ImageIO, can't read PNG encoded ICO format.");
}
}
else {
mPNGImageReader.reset();
}
return mPNGImageReader;
}
private DIBHeader getHeader(final DirectoryEntry pEntry) throws IOException {
if (!mHeaders.containsKey(pEntry)) {
mImageInput.seek(pEntry.getOffset());
DIBHeader header = DIBHeader.read(mImageInput);
mHeaders.put(pEntry, header);
}
return mHeaders.get(pEntry);
}
private BufferedImage readBitmap(final DirectoryEntry pEntry) throws IOException {
// TODO: Get rid of the caching, as the images are mutable
BitmapDescriptor descriptor = mDescriptors.get(pEntry);
if (descriptor == null || !mDescriptors.containsKey(pEntry)) {
DIBHeader header = getHeader(pEntry);
int offset = pEntry.getOffset() + header.getSize();
if (offset != mImageInput.getStreamPosition()) {
mImageInput.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));
}
}
mDescriptors.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.mEntry, pBitmap.mHeader);
readBitmapIndexed1(mask.mMask, 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.mColors[i] = (mImageInput.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++) {
mImageInput.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.mBits[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++) {
mImageInput.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.mBits[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++) {
mImageInput.readFully(row, 0, width);
int rowPos = 0;
int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
for (int x = 0; x < pBitmap.getWidth(); x++) {
pBitmap.mBits[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.mImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
for (int y = 0; y < pBitmap.getHeight(); y++) {
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
mImageInput.readFully(pixels, offset, pBitmap.getWidth());
// Skip to 32 bit boundary
if (pBitmap.getWidth() % 2 != 0) {
mImageInput.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.mImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
for (int y = 0; y < pBitmap.getHeight(); y++) {
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
mImageInput.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.mImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
for (int y = 0; y < pBitmap.getHeight(); y++) {
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
mImageInput.readFully(pixels, offset, pBitmap.getWidth());
if (abortRequested()) {
processReadAborted();
break;
}
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
}
private Directory getDirectory() throws IOException {
assertInput();
if (mDirectory == null) {
readFileHeader();
}
return mDirectory;
}
private void readFileHeader() throws IOException {
mImageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
// Read file header
mImageInput.readUnsignedShort(); // Reserved
// Should be same as type as the provider
int type = mImageInput.readUnsignedShort();
int imageCount = mImageInput.readUnsignedShort();
// Read directory
mDirectory = Directory.read(type, imageCount, mImageInput);
}
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<ImageReader> 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);
}
}
@@ -0,0 +1,101 @@
/*
* 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.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* ICOImageReaderSpi
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ICOImageReaderSpi.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
public class ICOImageReaderSpi extends ImageReaderSpi {
public ICOImageReaderSpi() {
this(IIOUtil.getProviderInfo(ICOImageReaderSpi.class));
}
private ICOImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(),
pProviderInfo.getVersion(),
new String[]{"ico", "ICO"},
new String[]{"ico"},
new String[]{
"image/vnd.microsoft.icon", // Official IANA MIME
"image/x-icon", // Common extension MIME
"image/ico" // Unofficial, but common
},
"com.twelvemonkeys.imageio.plugins.ico.ICOImageReader",
STANDARD_INPUT_TYPE,
null,
true, null, null, null, null,
true,
null, null,
null, null
);
}
public boolean canDecodeInput(final Object pSource) throws IOException {
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource, DIB.TYPE_ICO);
}
static boolean canDecode(final ImageInputStream pInput, final int pType) throws IOException {
byte[] signature = new byte[4];
try {
pInput.mark();
pInput.readFully(signature);
int count = pInput.readByte() + (pInput.readByte() << 8);
return (signature[0] == 0x0 && signature[1] == 0x0 && signature[2] == pType
&& signature[3] == 0x0 && count > 0);
}
finally {
pInput.reset();
}
}
public ImageReader createReaderInstance(final Object pExtension) throws IOException {
return new ICOImageReader(this);
}
public String getDescription(final Locale pLocale) {
return "Windows Icon Format (ICO) Reader";
}
}
@@ -0,0 +1,2 @@
com.twelvemonkeys.imageio.plugins.ico.ICOImageReaderSpi
com.twelvemonkeys.imageio.plugins.ico.CURImageReaderSpi