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: /*
022: * ResIcon.java
023: *
024: * Created on 17 août 2003, 22:51
025: */
026:
027: package net.charabia.jsmoothgen.pe.res;
028:
029: import java.util.*;
030: import java.io.*;
031: import java.nio.*;
032: import java.nio.channels.*;
033: import java.awt.*;
034: import java.awt.image.*;
035:
036: /**
037: * @see
038: */
039: public class ResIcon {
040: public long Size; /* Size of this header in bytes DWORD*/
041: public long Width; /* Image width in pixels LONG*/
042: public long Height; /* Image height in pixels LONG*/
043: public int Planes; /* Number of color planes WORD*/
044: public int BitsPerPixel; /* Number of bits per pixel WORD*/
045: /* Fields added for Windows 3.x follow this line */
046: public long Compression; /* Compression methods used DWORD*/
047: public long SizeOfBitmap; /* Size of bitmap in bytes DWORD*/
048: public long HorzResolution; /* Horizontal resolution in pixels per meter LONG*/
049: public long VertResolution; /* Vertical resolution in pixels per meter LONG*/
050: public long ColorsUsed; /* Number of colors in the image DWORD*/
051: public long ColorsImportant; /* Minimum number of important colors DWORD*/
052:
053: public PaletteElement[] Palette;
054: public short[] BitmapXOR;
055: public short[] BitmapAND;
056:
057: public class PaletteElement {
058: public int Blue;
059: public int Green;
060: public int Red;
061: public int Reserved;
062:
063: public String toString() {
064: return "{" + Blue + "," + Green + "," + Red + ","
065: + Reserved + "}";
066: }
067: }
068:
069: /**
070: * Creates a new instance of ResIcon
071: * @see
072: * @param in
073: */
074: public ResIcon(ByteBuffer in) {
075: Size = in.getInt();
076: Width = in.getInt();
077: Height = in.getInt();
078: Planes = in.getShort();
079: BitsPerPixel = in.getShort();
080: Compression = in.getInt();
081: SizeOfBitmap = in.getInt();
082: HorzResolution = in.getInt();
083: VertResolution = in.getInt();
084: ColorsUsed = in.getInt();
085: ColorsImportant = in.getInt();
086:
087: int cols = (int) ColorsUsed;
088: if (cols == 0)
089: cols = 1 << BitsPerPixel;
090:
091: Palette = new PaletteElement[(int) cols];
092: for (int i = 0; i < Palette.length; i++) {
093: PaletteElement el = new PaletteElement();
094: el.Blue = in.get();
095: el.Green = in.get();
096: el.Red = in.get();
097: el.Reserved = in.get();
098: Palette[i] = el;
099: }
100:
101: // int xorbytes = (((int)Height/2) * (int)Width * (int)BitsPerPixel) / 8;
102: int xorbytes = (((int) Height / 2) * (int) Width);
103: // System.out.println("POSITION " + in.position() + " : xorbitmap = " + xorbytes + " bytes");
104:
105: BitmapXOR = new short[xorbytes];
106: for (int i = 0; i < BitmapXOR.length; i++) {
107: switch (BitsPerPixel) {
108: case 4: {
109: int pix = in.get();
110: BitmapXOR[i] = (short) ((pix >> 4) & 0x0F);
111: i++;
112: BitmapXOR[i] = (short) (pix & 0x0F);
113: }
114: break;
115: case 8: {
116: BitmapXOR[i] = in.get();
117: }
118: break;
119: }
120: }
121:
122: int height = (int) (Height / 2);
123: int rowsize = (int) Width / 8;
124: if ((rowsize % 4) > 0) {
125: rowsize += 4 - (rowsize % 4);
126: }
127:
128: // System.out.println("POSITION " + in.position() + " : andbitmap = " + andbytes + " bytes");
129:
130: int andbytes = height * rowsize; // (((int)Height/2) * (int)Width) / 8;
131:
132: BitmapAND = new short[andbytes];
133: for (int i = 0; i < BitmapAND.length; i++) {
134: BitmapAND[i] = in.get();
135: }
136:
137: }
138:
139: /** Creates a new instance based on the data of the Image argument.
140: * @param img
141: */
142: public ResIcon(Image img) throws Exception {
143: int width = img.getWidth(null);
144: int height = img.getHeight(null);
145:
146: if ((width % 8) != 0)
147: width += (7 - (width % 8));
148:
149: if ((height % 8) != 0)
150: height += (7 - (height % 8));
151:
152: // System.out.println("FOUND WIDTH " + width + " (was " + img.getWidth(null) + ")");
153: // System.out.println("FOUND HEIGHT " + height + " (was " + img.getHeight(null) + ")");
154:
155: // System.out.println("RESICON...");
156: if (img instanceof BufferedImage) {
157: BufferedImage result = (BufferedImage) img;
158:
159: for (int y = 0; y < result.getHeight(); y++) {
160: for (int x = 0; x < result.getWidth(); x++) {
161: int rgb = result.getRGB(x, y);
162: if (((rgb >> 24) & 0xFF) > 0) {
163: // System.out.print(".");
164: }
165: // else
166: // System.out.print("*");
167: }
168: // System.out.println("");
169: }
170:
171: }
172:
173: int[] pixelbuffer = new int[width * height];
174: PixelGrabber grabber = new PixelGrabber(img, 0, 0, width,
175: height, pixelbuffer, 0, width);
176: try {
177: grabber.grabPixels();
178: } catch (InterruptedException e) {
179: System.err.println("interrupted waiting for pixels!");
180: throw new Exception("Can't load the image provided", e);
181: }
182:
183: Hashtable colors = calculateColorCount(pixelbuffer);
184:
185: // FORCE ALWAYS to 8
186: this .BitsPerPixel = 8;
187:
188: Palette = new ResIcon.PaletteElement[1 << BitsPerPixel];
189: // System.out.println("Creating palette of " + Palette.length + " colors (" + colors.size() + ")");
190: for (Enumeration e = colors.keys(); e.hasMoreElements();) {
191: Integer pixi = (Integer) e.nextElement();
192: int pix = pixi.intValue();
193: int index = ((Integer) colors.get(pixi)).intValue();
194: // System.out.println("set pixel " + index);
195:
196: Palette[index] = new ResIcon.PaletteElement();
197: Palette[index].Blue = pix & 0xFF;
198: Palette[index].Green = (pix >> 8) & 0xff;
199: Palette[index].Red = (pix >> 16) & 0xff;
200: }
201: for (int i = 0; i < Palette.length; i++) {
202: if (Palette[i] == null)
203: Palette[i] = new ResIcon.PaletteElement();
204: }
205:
206: this .Size = 40;
207: this .Width = width;
208: this .Height = height * 2;
209: this .Planes = 1;
210: this .Compression = 0;
211:
212: this .SizeOfBitmap = 0;
213: this .HorzResolution = 0;
214: this .VertResolution = 0;
215:
216: this .ColorsUsed = 0;
217: this .ColorsImportant = 0;
218:
219: //
220: // We calculate the rowsize in bytes. It seems that it must be
221: // aligned on a double word, although none of the
222: // documentation I have on the icon format states so.
223: //
224: int rowsize = width / 8;
225: if ((rowsize % 4) > 0) {
226: rowsize += 4 - (rowsize % 4);
227: }
228:
229: BitmapXOR = new short[(((int) Height / 2) * (int) Width * (int) BitsPerPixel) / 8];
230: BitmapAND = new short[((int) Height / 2) * rowsize];
231:
232: int bxl = BitmapXOR.length - 1;
233: int bal = BitmapAND.length - 1;
234:
235: for (int i = 0; i < pixelbuffer.length; i++) {
236: int col = i % width;
237: int line = i / width;
238:
239: bxl = (width * height) - (((i / width) + 1) * width)
240: + (i % width);
241: // bal = ((width * height)/8) - ((line+1)*(width/8)) + (col/8);
242: bal = (rowsize * height) - ((line + 1) * (rowsize))
243: + (col / 8);
244:
245: // if ((pixelbuffer[i] & 0xFF000000) != 0x00000000)
246:
247: //
248: // If the color is transparent, any color will suit
249: // (as it is not supposed to be displayed)
250: //
251: if ((((pixelbuffer[i] >> 24) & 0xFF) == 0)) {
252: BitmapAND[bal] |= 1 << (7 - (i % 8));
253: BitmapXOR[bxl] = 0xFF; // (short)getBrightest(); FF
254:
255: // int pixel = pixelbuffer[i] & 0x00FFFFFF;
256: // pixel = 0x000000;
257: // Integer icol = (Integer)colors.get(new Integer(pixel));
258: // if (icol != null)
259: // {
260: // int palindex = icol.intValue();
261: // BitmapXOR[bxl] = (short)palindex;
262: // }
263: // else
264: // {
265: // BitmapXOR[bxl] = 0; // (short)getBrightest();
266: // System.out.println("Can't find TRANSP BLACK COL " + icol );
267: // }
268: } else {
269: int pixel = pixelbuffer[i] & 0x00FFFFFF;
270: // pixel = 0x000000;
271: Integer icol = (Integer) colors.get(new Integer(pixel));
272: if (icol != null) {
273: int palindex = icol.intValue();
274: BitmapXOR[bxl] = (short) palindex;
275: }
276: }
277: }
278: }
279:
280: private int getBrightest() {
281: int result = 0;
282: int averesult = 0;
283: for (int i = 0; i < Palette.length; i++) {
284: int ave1 = (Palette[0].Red + Palette[0].Green + Palette[0].Blue) / 3;
285: if (ave1 > averesult) {
286: averesult = ave1;
287: result = i;
288: }
289: }
290: return result;
291: }
292:
293: private Hashtable calculateColorCount(int[] pixels) {
294: Hashtable result = new Hashtable();
295: int colorindex = 0;
296: for (int i = 0; i < pixels.length; i++) {
297: int pix = pixels[i];
298: if (((pix >> 24) & 0xFF) > 0) {
299: pix &= 0x00FFFFFF;
300: Integer pixi = new Integer(pix);
301: Object o = result.get(pixi);
302: if (o == null) {
303: result.put(pixi, new Integer(colorindex++));
304: }
305: // if (colorindex > 256)
306: // return result;
307: }
308: }
309: return result;
310: }
311:
312: /** Creates and returns a ByteBuffer containing an image under
313: * the .ico format expected by Windows.
314: * @return a ByteBuffer with the .ico data
315: */
316: public ByteBuffer getData() {
317: int cols = (int) ColorsUsed;
318: if (cols == 0)
319: cols = 1 << BitsPerPixel;
320:
321: int rowsize = (int) Width / 8;
322: if ((rowsize % 4) > 0) {
323: rowsize += 4 - (rowsize % 4);
324: }
325:
326: ByteBuffer buf = ByteBuffer
327: .allocate((int) (40 + (cols * 4)
328: + (Width * (Height / 2) * BitsPerPixel) / 8 + (rowsize * (Height / 2))));
329: buf.order(ByteOrder.LITTLE_ENDIAN);
330: buf.position(0);
331:
332: buf.putInt((int) Size);
333: buf.putInt((int) Width);
334: buf.putInt((int) Height);
335: buf.putShort((short) Planes);
336: buf.putShort((short) BitsPerPixel);
337: buf.putInt((int) Compression);
338: buf.putInt((int) SizeOfBitmap);
339: buf.putInt((int) HorzResolution);
340: buf.putInt((int) VertResolution);
341: buf.putInt((int) ColorsUsed);
342: buf.putInt((int) ColorsImportant);
343:
344: // System.out.println("GET DATA :: Palette.size= "+Palette.length + " // position=" + buf.position());
345: for (int i = 0; i < Palette.length; i++) {
346: PaletteElement el = Palette[i];
347: buf.put((byte) el.Blue);
348: buf.put((byte) el.Green);
349: buf.put((byte) el.Red);
350: buf.put((byte) el.Reserved);
351: }
352:
353: switch (BitsPerPixel) {
354: case 4: {
355: for (int i = 0; i < BitmapXOR.length; i += 2) {
356: int v1 = BitmapXOR[i];
357: int v2 = BitmapXOR[i + 1];
358: buf.put((byte) ((v1 << 4) | v2));
359: }
360: }
361: break;
362:
363: case 8: {
364: // System.out.println("GET DATA :: XORBitmap.size= "+BitmapXOR.length + " // position=" + buf.position());
365: for (int i = 0; i < BitmapXOR.length; i++) {
366: buf.put((byte) BitmapXOR[i]);
367: }
368: }
369: break;
370:
371: default:
372: throw new RuntimeException("BitRes " + BitsPerPixel
373: + " not supported!");
374: }
375:
376: // System.out.println("GET DATA :: AndBitmap.size= "+BitmapAND.length + " // position=" + buf.position());
377: for (int i = 0; i < BitmapAND.length; i++) {
378: buf.put((byte) BitmapAND[i]);
379: }
380:
381: // System.out.println("GET DATA END AT " + buf.position());
382: buf.position(0);
383: return buf;
384: }
385:
386: public String toString() {
387: StringBuffer out = new StringBuffer();
388:
389: out.append("Size: " + Size);
390: out.append("\nWidth: " + Width);
391: out.append("\nHeight: " + Height);
392: out.append("\nPlanes: " + Planes);
393: out.append("\nBitsPerPixel: " + BitsPerPixel);
394: out.append("\nCompression: " + Compression);
395: out.append("\nSizeOfBitmap: " + SizeOfBitmap);
396: out.append("\nHorzResolution: " + HorzResolution);
397: out.append("\nVertResolution: " + VertResolution);
398: out.append("\nColorsUsed: " + ColorsUsed);
399: out.append("\nColorsImportant: " + ColorsImportant);
400:
401: // for (int i = 0; i<Palette.length; i++)
402: // {
403: // out.append("\n");
404: // out.append(Palette[i].toString());
405: // }
406: out.append("\nBitmapXOR[" + BitmapXOR.length + "]={");
407: for (int i = 0; i < BitmapXOR.length; i++) {
408: out.append((byte) BitmapXOR[i]);
409: }
410: out.append("}\nBitmapAnd[" + BitmapAND.length + "]={");
411: for (int i = 0; i < BitmapAND.length; i++) {
412: out.append((byte) BitmapAND[i]);
413: }
414:
415: return out.toString();
416: }
417:
418: public static void main(String[] args) throws Exception {
419: net.charabia.jsmoothgen.pe.PEFile.main(args);
420: }
421: }
|