001: /*
002: JSmooth: a VM wrapper toolkit for Windows
003: Copyright (C) 2003 Rodrigo Reyes <reyes@charabia.net>
004:
005: This program is free software; you can redistribute it and/or modify
006: it under the terms of the GNU General Public License as published by
007: the Free Software Foundation; either version 2 of the License, or
008: (at your option) any later version.
009:
010: This program is distributed in the hope that it will be useful,
011: but WITHOUT ANY WARRANTY; without even the implied warranty of
012: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013: GNU General Public License for more details.
014:
015: You should have received a copy of the GNU General Public License
016: along with this program; if not, write to the Free Software
017: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
018:
019: */
020:
021: package net.charabia.util.codec;
022:
023: import java.io.*;
024: import net.charabia.util.io.BinaryInputStream;
025: import java.awt.image.*;
026: import java.awt.*;
027:
028: /**
029: *
030: * @author Rodrigo Reyes
031: */
032: public class IcoCodec {
033: static public class IconDir {
034: int idType;
035: int idCount;
036:
037: public IconDir(BinaryInputStream in) throws IOException {
038: in.readUShortLE();
039: idType = in.readUShortLE();
040: idCount = in.readUShortLE();
041: }
042:
043: public String toString() {
044: return "{ idType=" + idType + ", " + idCount + " }";
045: }
046: }
047:
048: static public class IconEntry {
049: short bWidth;
050: short bHeight;
051: short bColorCount;
052: short bReserved;
053: int wPlanes;
054: int wBitCount;
055: long dwBytesInRes;
056: long dwImageOffset;
057:
058: public IconEntry(BinaryInputStream in) throws IOException {
059: bWidth = in.readUByte();
060: bHeight = in.readUByte();
061: bColorCount = in.readUByte();
062: bReserved = in.readUByte();
063: wPlanes = in.readUShortLE();
064: wBitCount = in.readUShortLE();
065: dwBytesInRes = in.readUIntLE();
066: dwImageOffset = in.readUIntLE();
067: }
068:
069: public String toString() {
070: StringBuffer buffer = new StringBuffer();
071: buffer.append("{ bWidth=" + bWidth + "\n");
072: buffer.append(" bHeight=" + bHeight + "\n");
073: buffer.append(" bColorCount=" + bColorCount + "\n");
074: buffer.append(" wPlanes=" + wPlanes + "\n");
075: buffer.append(" wBitCount=" + wBitCount + "\n");
076: buffer.append(" dwBytesInRes=" + dwBytesInRes + "\n");
077: buffer.append(" dwImageOffset=" + dwImageOffset + "\n");
078: buffer.append("}");
079:
080: return buffer.toString();
081: }
082:
083: }
084:
085: static public class IconHeader {
086: public long Size; /* Size of this header in bytes DWORD 0*/
087: public long Width; /* Image width in pixels LONG 4*/
088: public long Height; /* Image height in pixels LONG 8*/
089: public int Planes; /* Number of color planes WORD 12 */
090: public int BitsPerPixel; /* Number of bits per pixel WORD 14 */
091: /* Fields added for Windows 3.x follow this line */
092: public long Compression; /* Compression methods used DWORD 16 */
093: public long SizeOfBitmap; /* Size of bitmap in bytes DWORD 20 */
094: public long HorzResolution; /* Horizontal resolution in pixels per meter LONG 24 */
095: public long VertResolution; /* Vertical resolution in pixels per meter LONG 28*/
096: public long ColorsUsed; /* Number of colors in the image DWORD 32 */
097: public long ColorsImportant; /* Minimum number of important colors DWORD 36 */
098:
099: public IconHeader(BinaryInputStream in) throws IOException {
100: Size = in.readUIntLE();
101: Width = in.readUIntLE();
102: Height = in.readUIntLE();
103: Planes = in.readUShortLE();
104: BitsPerPixel = in.readUShortLE();
105: Compression = in.readUIntLE();
106: SizeOfBitmap = in.readUIntLE();
107: HorzResolution = in.readUIntLE();
108: VertResolution = in.readUIntLE();
109: ColorsUsed = in.readUIntLE();
110: ColorsImportant = in.readUIntLE();
111: }
112:
113: public String toString() {
114: StringBuffer buffer = new StringBuffer();
115: buffer.append("Size=");
116: buffer.append(Size);
117: buffer.append("\nWidth=");
118: buffer.append(Width);
119: buffer.append("\nHeight=");
120: buffer.append(Height);
121: buffer.append("\nPlanes=");
122: buffer.append(Planes);
123: buffer.append("\nBitsPerPixel=");
124: buffer.append(BitsPerPixel);
125: buffer.append("\nCompression=");
126: buffer.append(Compression);
127: buffer.append("\nSizeOfBitmap=");
128: buffer.append(SizeOfBitmap);
129: buffer.append("\nHorzResolution=");
130: buffer.append(HorzResolution);
131: buffer.append("\nVertResolution=");
132: buffer.append(VertResolution);
133: buffer.append("\nColorsUsed=");
134: buffer.append(ColorsUsed);
135: buffer.append("\nColorsImportant=");
136: buffer.append(ColorsImportant);
137:
138: return buffer.toString();
139: }
140:
141: }
142:
143: static public BufferedImage[] loadImages(File f) throws IOException {
144: InputStream istream = new FileInputStream(f);
145: BufferedInputStream buffin = new BufferedInputStream(istream);
146: BinaryInputStream in = new BinaryInputStream(buffin);
147:
148: try {
149: in.mark(32000);
150:
151: IconDir dir = new IconDir(in);
152: // System.out.println("DIR = " + dir);
153:
154: IconEntry[] entries = new IconEntry[dir.idCount];
155: BufferedImage[] images = new BufferedImage[dir.idCount];
156:
157: for (int i = 0; i < dir.idCount; i++) {
158: entries[i] = new IconEntry(in);
159: // System.out.println("ENTRY " + i + " = " + entries[i]);
160: }
161:
162: IconEntry entry = entries[0];
163: // System.out.println("ENTRYx = " + entry);
164:
165: for (int i = 0; i < dir.idCount; i++) {
166: in.reset();
167: in.skip(entries[i].dwImageOffset);
168:
169: IconHeader header = new IconHeader(in);
170: // System.out.println("Header: " + header);
171:
172: long toskip = header.Size - 40;
173: if (toskip > 0)
174: in.skip((int) toskip);
175:
176: // System.out.println("skipped data");
177:
178: BufferedImage image = new BufferedImage(
179: (int) header.Width, (int) header.Height / 2,
180: BufferedImage.TYPE_INT_ARGB);
181:
182: switch (header.BitsPerPixel) {
183: case 4:
184: case 8:
185: loadPalettedImage(in, entries[i], header, image);
186: break;
187:
188: default:
189: throw new Exception("Unsupported ICO color depth: "
190: + header.BitsPerPixel);
191: }
192:
193: images[i] = image;
194: }
195:
196: return images;
197:
198: } catch (Exception exc) {
199: exc.printStackTrace();
200: }
201:
202: return null;
203: }
204:
205: static private void loadPalettedImage(BinaryInputStream in,
206: IconEntry entry, IconHeader header, BufferedImage image)
207: throws Exception {
208: // System.out.println("Loading image...");
209:
210: // System.out.println("Loading palette...");
211:
212: //
213: // First, load the palette
214: //
215: int cols = (int) header.ColorsUsed;
216: if (cols == 0) {
217: if (entry.bColorCount != 0)
218: cols = entry.bColorCount;
219: else
220: cols = 1 << header.BitsPerPixel;
221: }
222:
223: int[] redp = new int[cols];
224: int[] greenp = new int[cols];
225: int[] bluep = new int[cols];
226:
227: for (int i = 0; i < cols; i++) {
228: bluep[i] = in.readUByte();
229: greenp[i] = in.readUByte();
230: redp[i] = in.readUByte();
231: in.readUByte();
232: }
233:
234: // System.out.println("Palette read!");
235:
236: //
237: // Set the image
238:
239: int xorbytes = (((int) header.Height / 2) * (int) header.Width);
240: int readbytes = 0;
241:
242: for (int y = (int) (header.Height / 2) - 1; y >= 0; y--) {
243: for (int x = 0; x < header.Width; x++) {
244: switch (header.BitsPerPixel) {
245: case 4: {
246: int pix = in.readUByte();
247: readbytes++;
248:
249: int col1 = (pix >> 4) & 0x0F;
250: int col2 = pix & 0x0F;
251: image.setRGB(x, y, (0xFF << 24)
252: | (redp[col1] << 16) | (greenp[col1] << 8)
253: | bluep[col1]);
254: image.setRGB(++x, y, (0xFF << 24)
255: | (redp[col2] << 16) | (greenp[col2] << 8)
256: | bluep[col2]);
257: }
258: break;
259: case 8: {
260: int col1 = in.readUByte();
261: readbytes++;
262:
263: image.setRGB(x, y, (0xFF << 24)
264: | (redp[col1] << 16) | (greenp[col1] << 8)
265: | bluep[col1]);
266: }
267: break;
268: }
269: }
270: }
271: // System.out.println("XOR data read (" + readbytes + " bytes)");
272:
273: int height = (int) (header.Height / 2);
274:
275: int rowsize = (int) header.Width / 8;
276: if ((rowsize % 4) > 0) {
277: rowsize += 4 - (rowsize % 4);
278: }
279:
280: // System.out.println("rowsize = " + rowsize);
281: int[] andbytes = new int[rowsize * height];
282:
283: for (int i = 0; i < andbytes.length; i++)
284: andbytes[i] = in.readUByte();
285:
286: for (int y = height - 1; y >= 0; y--) {
287: for (int x = 0; x < header.Width; x++) {
288: int offset = ((height - (y + 1)) * rowsize) + (x / 8);
289: if ((andbytes[offset] & (1 << (7 - x % 8))) != 0) {
290: image.setRGB(x, y, 0);
291: }
292: }
293: }
294:
295: // for (int i=0; i<andbytes; i++)
296: // {
297: // int pix = in.readUByte();
298: // readbytes++;
299:
300: // int xb = (i*8) % (int)header.Width;
301: // int yb = ((int)header.Height/2) - (((i*8) / (int)header.Width)+1);
302:
303: // for (int offset=7; offset>=0; offset--)
304: // {
305: // //
306: // // Modify the transparency only if necessary
307: // //
308: // System.out.println("SET AND (" + xb + "," + yb + ")-" + (7-offset));
309:
310: // if (((1<<offset) & pix)!=0)
311: // {
312: // int argb = image.getRGB(xb+(7-offset), yb);
313: // image.setRGB(xb+(7-offset), yb, argb & 0xFFFFFF);
314: // }
315: // }
316: // }
317:
318: // System.out.println("AND data read (" + readbytes + " bytes total)");
319: }
320:
321: static public void main(String[] args) throws Exception {
322: File f = new File(args[0]);
323: Image img = IcoCodec.loadImages(f)[0];
324: // System.out.println("img = " + img);
325:
326: javax.swing.JFrame jf = new javax.swing.JFrame("Test");
327: javax.swing.JButton button = new javax.swing.JButton(
328: new javax.swing.ImageIcon(img));
329: jf.getContentPane().add(button);
330: jf.pack();
331: jf.setVisible(true);
332: }
333:
334: }
|