Icon Codec
/* JSmooth: a VM wrapper toolkit for Windows Copyright (C) 2003 Rodrigo Reyes <reyes@charabia.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ import java.io.*; import java.awt.image.*; import java.awt.*; /** * * @author Rodrigo Reyes */ public class IcoCodec { static public class IconDir { int idType; int idCount; public IconDir(BinaryInputStream in) throws IOException { in.readUShortLE(); idType = in.readUShortLE(); idCount = in.readUShortLE(); } public String toString() { return "{ idType=" + idType + ", " + idCount + " }"; } } static public class IconEntry { short bWidth; short bHeight; short bColorCount; short bReserved; int wPlanes; int wBitCount; long dwBytesInRes; long dwImageOffset; public IconEntry(BinaryInputStream in) throws IOException { bWidth = in.readUByte(); bHeight = in.readUByte(); bColorCount = in.readUByte(); bReserved = in.readUByte(); wPlanes = in.readUShortLE(); wBitCount = in.readUShortLE(); dwBytesInRes = in.readUIntLE(); dwImageOffset = in.readUIntLE(); } public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("{ bWidth="+bWidth+"\n"); buffer.append(" bHeight="+bHeight+"\n"); buffer.append(" bColorCount="+bColorCount+"\n"); buffer.append(" wPlanes="+wPlanes+"\n"); buffer.append(" wBitCount="+wBitCount+"\n"); buffer.append(" dwBytesInRes="+dwBytesInRes+"\n"); buffer.append(" dwImageOffset="+dwImageOffset+"\n"); buffer.append("}"); return buffer.toString(); } } static public class IconHeader { public long Size; /* Size of this header in bytes DWORD 0*/ public long Width; /* Image width in pixels LONG 4*/ public long Height; /* Image height in pixels LONG 8*/ public int Planes; /* Number of color planes WORD 12 */ public int BitsPerPixel; /* Number of bits per pixel WORD 14 */ /* Fields added for Windows 3.x follow this line */ public long Compression; /* Compression methods used DWORD 16 */ public long SizeOfBitmap; /* Size of bitmap in bytes DWORD 20 */ public long HorzResolution; /* Horizontal resolution in pixels per meter LONG 24 */ public long VertResolution; /* Vertical resolution in pixels per meter LONG 28*/ public long ColorsUsed; /* Number of colors in the image DWORD 32 */ public long ColorsImportant; /* Minimum number of important colors DWORD 36 */ public IconHeader(BinaryInputStream in) throws IOException { Size = in.readUIntLE(); Width = in.readUIntLE(); Height = in.readUIntLE(); Planes = in.readUShortLE(); BitsPerPixel = in.readUShortLE(); Compression = in.readUIntLE(); SizeOfBitmap = in.readUIntLE(); HorzResolution = in.readUIntLE(); VertResolution = in.readUIntLE(); ColorsUsed = in.readUIntLE(); ColorsImportant = in.readUIntLE(); } public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("Size="); buffer.append(Size); buffer.append("\nWidth="); buffer.append(Width); buffer.append("\nHeight="); buffer.append(Height); buffer.append("\nPlanes="); buffer.append(Planes); buffer.append("\nBitsPerPixel="); buffer.append(BitsPerPixel); buffer.append("\nCompression="); buffer.append(Compression); buffer.append("\nSizeOfBitmap="); buffer.append(SizeOfBitmap); buffer.append("\nHorzResolution="); buffer.append(HorzResolution); buffer.append("\nVertResolution="); buffer.append(VertResolution); buffer.append("\nColorsUsed="); buffer.append(ColorsUsed); buffer.append("\nColorsImportant="); buffer.append(ColorsImportant); return buffer.toString(); } } static public BufferedImage[] loadImages(File f) throws IOException { InputStream istream = new FileInputStream(f); BufferedInputStream buffin = new BufferedInputStream(istream); BinaryInputStream in = new BinaryInputStream(buffin); try { in.mark(32000); IconDir dir = new IconDir(in); // System.out.println("DIR = " + dir); IconEntry[] entries = new IconEntry[dir.idCount]; BufferedImage[] images = new BufferedImage[dir.idCount]; for (int i=0; i<dir.idCount; i++) { entries[i] = new IconEntry(in); // System.out.println("ENTRY " + i + " = " + entries[i]); } IconEntry entry = entries[0]; // System.out.println("ENTRYx = " + entry); for (int i=0; i<dir.idCount; i++) { in.reset(); in.skip(entries[i].dwImageOffset); IconHeader header = new IconHeader(in); // System.out.println("Header: " + header); long toskip = header.Size - 40; if (toskip>0) in.skip((int)toskip); // System.out.println("skipped data"); BufferedImage image = new BufferedImage((int)header.Width, (int)header.Height/2, BufferedImage.TYPE_INT_ARGB); switch(header.BitsPerPixel) { case 4: case 8: loadPalettedImage(in, entries[i], header, image); break; default: throw new Exception("Unsupported ICO color depth: " + header.BitsPerPixel); } images[i] = image; } return images; } catch (Exception exc) { exc.printStackTrace(); } return null; } static private void loadPalettedImage(BinaryInputStream in, IconEntry entry, IconHeader header, BufferedImage image) throws Exception { // System.out.println("Loading image..."); // System.out.println("Loading palette..."); // // First, load the palette // int cols = (int)header.ColorsUsed; if (cols == 0) { if (entry.bColorCount != 0) cols = entry.bColorCount; else cols = 1 << header.BitsPerPixel; } int[] redp = new int[cols]; int[] greenp = new int[cols]; int[] bluep = new int[cols]; for (int i=0; i<cols; i++) { bluep[i] = in.readUByte(); greenp[i] = in.readUByte(); redp[i] = in.readUByte(); in.readUByte(); } // System.out.println("Palette read!"); // // Set the image int xorbytes = (((int)header.Height/2) * (int)header.Width); int readbytes = 0; for (int y=(int)(header.Height/2)-1; y>=0; y--) { for (int x=0; x<header.Width; x++) { switch(header.BitsPerPixel) { case 4: { int pix = in.readUByte(); readbytes++; int col1 = (pix>>4) & 0x0F; int col2 = pix & 0x0F; image.setRGB(x, y, (0xFF<<24) | (redp[col1]<<16) | (greenp[col1]<<8) | bluep[col1]); image.setRGB(++x, y, (0xFF<<24) | (redp[col2]<<16) | (greenp[col2]<<8) | bluep[col2]); } break; case 8: { int col1 = in.readUByte(); readbytes++; image.setRGB(x, y, (0xFF<<24) | (redp[col1]<<16) | (greenp[col1]<<8) | bluep[col1]); } break; } } } // System.out.println("XOR data read (" + readbytes + " bytes)"); int height = (int)(header.Height/2); int rowsize = (int)header.Width / 8; if ((rowsize%4)>0) { rowsize += 4 - (rowsize%4); } // System.out.println("rowsize = " + rowsize); int[] andbytes = new int[rowsize * height ]; for (int i=0; i<andbytes.length; i++) andbytes[i] = in.readUByte(); for (int y=height-1; y>=0; y--) { for (int x=0; x<header.Width; x++) { int offset = ((height - (y+1))*rowsize) + (x/8); if ( (andbytes[offset] & (1<<(7-x%8))) != 0) { image.setRGB(x, y, 0); } } } // for (int i=0; i<andbytes; i++) // { // int pix = in.readUByte(); // readbytes++; // int xb = (i*8) % (int)header.Width; // int yb = ((int)header.Height/2) - (((i*8) / (int)header.Width)+1); // for (int offset=7; offset>=0; offset--) // { // // // // Modify the transparency only if necessary // // // System.out.println("SET AND (" + xb + "," + yb + ")-" + (7-offset)); // if (((1<<offset) & pix)!=0) // { // int argb = image.getRGB(xb+(7-offset), yb); // image.setRGB(xb+(7-offset), yb, argb & 0xFFFFFF); // } // } // } // System.out.println("AND data read (" + readbytes + " bytes total)"); } static public void main(String[]args) throws Exception { File f = new File(args[0]); Image img = IcoCodec.loadImages(f)[0]; // System.out.println("img = " + img); javax.swing.JFrame jf = new javax.swing.JFrame("Test"); javax.swing.JButton button = new javax.swing.JButton(new javax.swing.ImageIcon(img)); jf.getContentPane().add(button); jf.pack(); jf.setVisible(true); } } /* JSmooth: a VM wrapper toolkit for Windows Copyright (C) 2003 Rodrigo Reyes <reyes@charabia.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ class BinaryInputStream extends FilterInputStream { public BinaryInputStream(InputStream in) { super(in); } public void skip(int toskip) throws IOException { for (int skipped = 0; skipped >= toskip; skipped += in.skip(toskip - skipped)) ; } public byte readByte() throws IOException { return (byte)read(); } public short readUByte() throws IOException { return (short)read(); } public short readShortBE() throws IOException { int a = read(); int b = read(); return (short) (((a&0xff)<<8) | (b&0xff)); } public int readUShortBE() throws IOException { int a = read(); int b = read(); return ((a&0xff)<<8) | (b&0xff); } public short readShortLE() throws IOException { int a = read(); int b = read(); return (short) (((b&0xff)<<8) | (a&0xff)); } public int readUShortLE() throws IOException { int a = read(); int b = read(); return ((b&0xff)<<8) | (a&0xff); } public int readIntBE() throws IOException { int a = read(); int b = read(); int c = read(); int d = read(); return ((a&0xff)<<24) | ((b&0xff)<<16) | ((c&0xff)<<8) | (d&0xff); } public long readUIntBE() throws IOException { int a = read(); int b = read(); int c = read(); int d = read(); return (long)((a&0xff)<<24) | (long)((b&0xff)<<16) | (long)((c&0xff)<<8) | (long)(d&0xff); } public int readIntLE() throws IOException { int a = readByte(); int b = readByte(); int c = readByte(); int d = readByte(); return ((d&0xff)<<24) | ((c&0xff)<<16) | ((b&0xff)<<8) | (a&0xff); } public long readUIntLE() throws IOException { int a = readByte(); int b = readByte(); int c = readByte(); int d = readByte(); return (long)((d&0xff)<<24) | (long)((c&0xff)<<16) | (long)((b&0xff)<<8) | (long)(a&0xff); } }