KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > pde > internal > swt > tools > IconExe


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.pde.internal.swt.tools;
12
13 import java.io.*;
14 import java.util.*;
15
16 /**
17  * Customize the icon of a Windows exe
18  *
19  * WARNING! This class is not part of SWT API. It is NOT API. It is an internal
20  * tool that may be changed or removed at anytime.
21  *
22  * Based on MSDN "An In-Depth Look into the Win32 Portable Executable File Format"
23  */

24 public class IconExe {
25     
26      /**
27      * Replace the Desktop icons provided in the Windows executable program
28      * with matching icons provided by the user.
29      *
30      * Takes 2 arguments
31      * argument 0: the Windows executable e.g c:/eclipse/eclipse.exe
32      * argument 1: The .ico file to write to the given executable e.g. c:/myApp.ico
33      *
34      * Note 1. Write access to the executable program is required. As a result, that
35      * program must not be currently running or edited elsewhere.
36      *
37      * Note 2. The Eclipse 3.1 launcher requires a .ico file with the following 6 images
38      * 1. 32x32, 4 bit (Windows 16 colors palette)
39      * 2. 16x16, 4 bit (Windows 16 colors palette)
40      * 3. 16x16, 8 bit (256 colors)
41      * 4. 32x32, 8 bit (256 colors)
42      * 5. 48x48, 4 bit (Windows 16 colors palette)
43      * 6. 48x48, 8 bit (256 colors)
44      * A user icon matching exactly the width/height/depth of an executable icon will be written
45      * to the executable and will replace that executable icon. If an executable icon
46      * does not match a user icon, it is silently left as is.
47      *
48      * Note 3. This function modifies the content of the executable program and may cause
49      * its corruption.
50      */

51     public static void main(String JavaDoc[] args) throws Exception JavaDoc {
52         if (args.length < 2) {
53             System.err.println("Usage: IconExe <windows executable> <ico file>"); //$NON-NLS-1$
54
return;
55         }
56         ImageLoader loader = new ImageLoader();
57
58         List images = new ArrayList();
59         for (int i = 1; i < args.length; i++) {
60             try {
61                 //An ICO should contain 7 images, a BMP will contain 1
62
ImageData[] current = loader.load(args[i]);
63                 for (int j = 0; j < current.length; j++) {
64                     images.add(current[j]);
65                 }
66             } catch (RuntimeException JavaDoc e) {
67                 //ignore so that we process the other images
68
}
69         }
70         ImageData[] data = new ImageData[images.size()];
71         data = (ImageData[]) images.toArray(data);
72         
73         int nMissing = unloadIcons(args[0], data);
74         if (nMissing != 0)
75             System.err.println("Error - " + nMissing + " icon(s) not replaced in " + args[0] + " using " + args[1]); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
76
}
77     
78     /* Implementation */
79
80     /**
81      * Retrieve the Desktop icons provided in the Windows executable program.
82      * These icons are typically shown in various places of the Windows desktop.
83      *
84      * Note. The Eclipse 3.1 launcher returns the following 6 images
85      * 1. 32x32, 4 bit (Windows 16 colors palette)
86      * 2. 16x16, 4 bit (Windows 16 colors palette)
87      * 3. 16x16, 8 bit (256 colors)
88      * 4. 32x32, 8 bit (256 colors)
89      * 5. 48x48, 4 bit (Windows 16 colors palette)
90      * 6. 48x48, 8 bit (256 colors)
91      *
92      * @param program the Windows executable e.g c:/eclipse/eclipse.exe
93      */

94     static ImageData[] loadIcons(String JavaDoc program) throws FileNotFoundException, IOException {
95         RandomAccessFile raf = new RandomAccessFile(program, "r"); //$NON-NLS-1$
96
IconExe iconExe = new IconExe();
97         IconResInfo[] iconInfo = iconExe.getIcons(raf);
98         ImageData[] data = new ImageData[iconInfo.length];
99         for (int i = 0; i < data.length; i++) data[i] = iconInfo[i].data;
100         raf.close();
101         return data;
102     }
103     
104     /**
105      * Replace the Desktop icons provided in the Windows executable program
106      * with icons provided by the user.
107      *
108      * Note 1. Write access to the executable program is required. As a result, that
109      * program must not be currently running or edited elsewhere.
110      *
111      * Note 2. Use loadIcons to determine which set of icons (width, height, depth)
112      * is required to replace the icons in the executable program. A user icon
113      * matching exactly the width/height/depth of an executable icon will be written
114      * to the executable and will replace that executable icon. If an executable icon
115      * does not match a user icon, it is left as is. Verify the return value matches
116      * the number of icons to write. Finally, use loadIcons after this operation
117      * to verify the icons have changed as expected.
118      *
119      * Note 3. The Eclipse 3.1 launcher requires the following 6 images (in any order).
120      * 1. 32x32, 4 bit (Windows 16 colors palette)
121      * 2. 16x16, 4 bit (Windows 16 colors palette)
122      * 3. 16x16, 8 bit (256 colors)
123      * 4. 32x32, 8 bit (256 colors)
124      * 5. 48x48, 4 bit (Windows 16 colors palette)
125      * 6. 48x48, 8 bit (256 colors)
126      *
127      * Note 4. This function modifies the content of the executable program and may cause
128      * its corruption.
129      *
130      * @param program the Windows executable e.g c:/eclipse/eclipse.exe
131      * @param icons to write to the given executable
132      * @return the number of icons from the original program that were not successfully replaced (0 if success)
133      */

134     static int unloadIcons(String JavaDoc program, ImageData[] icons) throws FileNotFoundException, IOException {
135         RandomAccessFile raf = new RandomAccessFile(program, "rw"); //$NON-NLS-1$
136
IconExe iconExe = new IconExe();
137         IconResInfo[] iconInfo = iconExe.getIcons(raf);
138         int cnt = 0;
139         for (int i = 0; i < iconInfo.length; i++) {
140             for (int j = 0; j < icons.length; j++) {
141                 if (icons[j] == null)
142                     continue;
143                 if (iconInfo[i].data.width == icons[j].width && iconInfo[i].data.height == icons[j].height && iconInfo[i].data.depth == icons[j].depth) {
144                     raf.seek(iconInfo[i].offset);
145                     unloadIcon(raf, icons[j]);
146                     cnt++;
147                     break;
148                 }
149             }
150         }
151         raf.close();
152         return iconInfo.length - cnt;
153     }
154     
155     public static final String JavaDoc VERSION = "20050124"; //$NON-NLS-1$
156

157     static final boolean DEBUG = false;
158     public static class IconResInfo {
159         ImageData data;
160         int offset;
161         int size;
162     }
163     
164     IconResInfo[] iconInfo = null;
165     int iconCnt;
166     
167     IconResInfo[] getIcons(RandomAccessFile raf) throws IOException {
168         iconInfo = new IconResInfo[4];
169         iconCnt = 0;
170         IMAGE_DOS_HEADER imageDosHeader = new IMAGE_DOS_HEADER();
171         read(raf, imageDosHeader);
172         if (imageDosHeader.e_magic != IMAGE_DOS_SIGNATURE) return null;
173         int imageNtHeadersOffset = imageDosHeader.e_lfanew;
174         raf.seek(imageNtHeadersOffset);
175         IMAGE_NT_HEADERS imageNtHeaders = new IMAGE_NT_HEADERS();
176         read(raf, imageNtHeaders);
177         if (imageNtHeaders.Signature != IMAGE_NT_SIGNATURE) return null;
178         
179         // DumpResources
180
int resourcesRVA = imageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
181         if (resourcesRVA == 0) return null;
182         if (DEBUG) System.out.println("* Resources (RVA= "+resourcesRVA+")"); //$NON-NLS-1$ //$NON-NLS-2$
183
IMAGE_SECTION_HEADER imageSectionHeader = new IMAGE_SECTION_HEADER();
184         int firstSectionOffset = imageNtHeadersOffset + IMAGE_NT_HEADERS.FIELD_OFFSET_OptionalHeader + imageNtHeaders.FileHeader.SizeOfOptionalHeader;
185         raf.seek(firstSectionOffset);
186         boolean found = false;
187         for (int i = 0; i < imageNtHeaders.FileHeader.NumberOfSections; i++) {
188             read(raf, imageSectionHeader);
189             if (resourcesRVA >= imageSectionHeader.VirtualAddress && resourcesRVA < imageSectionHeader.VirtualAddress + imageSectionHeader.Misc_VirtualSize) {
190                 // could check the imageSectionHeader name is .rsrc
191
found = true;
192                 break;
193             }
194         }
195         if (!found) return null;
196         int delta = imageSectionHeader.VirtualAddress - imageSectionHeader.PointerToRawData;
197         int imageResourceDirectoryOffset = resourcesRVA - delta;
198         dumpResourceDirectory(raf, imageResourceDirectoryOffset, imageResourceDirectoryOffset, delta, 0, 0, false);
199         if (iconCnt < iconInfo.length) {
200             IconResInfo[] newArray = new IconResInfo[iconCnt];
201             System.arraycopy(iconInfo, 0, newArray, 0, iconCnt);
202             iconInfo = newArray;
203         }
204         return iconInfo;
205     }
206
207 void dumpResourceDirectory(RandomAccessFile raf, int imageResourceDirectoryOffset, int resourceBase, int delta, int type, int level, boolean rt_icon_root) throws IOException {
208     if (DEBUG) System.out.println("** LEVEL "+level); //$NON-NLS-1$
209

210     IMAGE_RESOURCE_DIRECTORY imageResourceDirectory = new IMAGE_RESOURCE_DIRECTORY();
211     raf.seek(imageResourceDirectoryOffset);
212     read(raf, imageResourceDirectory);
213
214     if (DEBUG) {
215         String JavaDoc sType = ""+type; //$NON-NLS-1$
216
// level 1 resources are resource types
217
if (level == 1) {
218             System.out.println("___________________________"); //$NON-NLS-1$
219
if (type == RT_ICON) sType = "RT_ICON"; //$NON-NLS-1$
220
if (type == RT_GROUP_ICON) sType = "RT_GROUP_ICON"; //$NON-NLS-1$
221
}
222         System.out.println("Resource Directory ["+sType+"]"+" (Named "+imageResourceDirectory.NumberOfNamedEntries+", ID "+imageResourceDirectory.NumberOfIdEntries+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
223
}
224
225     IMAGE_RESOURCE_DIRECTORY_ENTRY[] imageResourceDirectoryEntries = new IMAGE_RESOURCE_DIRECTORY_ENTRY[imageResourceDirectory.NumberOfIdEntries];
226     for (int i = 0; i < imageResourceDirectoryEntries.length; i++) {
227         imageResourceDirectoryEntries[i] = new IMAGE_RESOURCE_DIRECTORY_ENTRY();
228         read(raf, imageResourceDirectoryEntries[i]);
229     }
230     for (int i = 0; i < imageResourceDirectoryEntries.length; i++) {
231         if (imageResourceDirectoryEntries[i].DataIsDirectory) {
232             dumpResourceDirectory(raf, imageResourceDirectoryEntries[i].OffsetToDirectory + resourceBase, resourceBase, delta, imageResourceDirectoryEntries[i].Id, level + 1, rt_icon_root ? true : type == RT_ICON);
233         } else {
234             // Resource found
235
/// pResDirEntry->Name
236
IMAGE_RESOURCE_DIRECTORY_ENTRY irde = imageResourceDirectoryEntries[i];
237             IMAGE_RESOURCE_DATA_ENTRY data = new IMAGE_RESOURCE_DATA_ENTRY();
238             raf.seek(imageResourceDirectoryEntries[i].OffsetToData + resourceBase);
239             read(raf, data);
240             if (DEBUG) System.out.println("Resource Id "+irde.Id+" Data Offset RVA "+data.OffsetToData+", Size "+data.Size); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
241
if (rt_icon_root) {
242                 if (DEBUG) System.out.println("iconcnt "+iconCnt+" |"+iconInfo.length); //$NON-NLS-1$ //$NON-NLS-2$
243
iconInfo[iconCnt] = new IconResInfo();
244                 iconInfo[iconCnt].data = parseIcon(raf, data.OffsetToData - delta, data.Size);
245                 iconInfo[iconCnt].offset = data.OffsetToData - delta;
246                 iconInfo[iconCnt].size = data.Size;
247                 iconCnt++;
248                 if (iconCnt == iconInfo.length) {
249                     IconResInfo[] newArray = new IconResInfo[iconInfo.length + 4];
250                     System.arraycopy(iconInfo, 0, newArray, 0, iconInfo.length);
251                     iconInfo = newArray;
252                 }
253             }
254         }
255     }
256 }
257
258 static ImageData parseIcon(RandomAccessFile raf, int offset, int size) throws IOException {
259     raf.seek(offset);
260     BITMAPINFO bitmapInfo = new BITMAPINFO();
261     read(raf, bitmapInfo);
262     bitmapInfo.bmiHeader.biHeight /= 2;
263     int width = bitmapInfo.bmiHeader.biWidth;
264     int height = bitmapInfo.bmiHeader.biHeight;
265     int depth = bitmapInfo.bmiHeader.biBitCount;
266
267     PaletteData palette = loadPalette(bitmapInfo.bmiHeader, raf);
268     byte[] shapeData = loadData(bitmapInfo.bmiHeader, raf);
269     bitmapInfo.bmiHeader.biBitCount = 1;
270     byte[] maskData = loadData(bitmapInfo.bmiHeader, raf);
271     maskData = convertPad(maskData, width, height, 1, 4, 2);
272     bitInvertData(maskData, 0, maskData.length);
273     return ImageData.internal_new(
274         width,
275         height,
276         depth,
277         palette,
278         4,
279         shapeData,
280         2,
281         maskData,
282         null,
283         -1,
284         -1,
285         SWT.IMAGE_ICO,
286         0,
287         0,
288         0,
289         0);
290 }
291
292 static byte[] bitInvertData(byte[] data, int startIndex, int endIndex) {
293     // Destructively bit invert data in the given byte array.
294
for (int i = startIndex; i < endIndex; i++) {
295         data[i] = (byte)(255 - data[i - startIndex]);
296     }
297     return data;
298 }
299
300 static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
301     if (pad == newPad) return data;
302     int stride = (width * depth + 7) / 8;
303     int bpl = (stride + (pad - 1)) / pad * pad;
304     int newBpl = (stride + (newPad - 1)) / newPad * newPad;
305     byte[] newData = new byte[height * newBpl];
306     int srcIndex = 0, destIndex = 0;
307     for (int y = 0; y < height; y++) {
308         System.arraycopy(data, srcIndex, newData, destIndex, newBpl);
309         srcIndex += bpl;
310         destIndex += newBpl;
311     }
312     return newData;
313 }
314 static PaletteData loadPalette(BITMAPINFOHEADER bih, RandomAccessFile raf) throws IOException {
315     int depth = bih.biBitCount;
316     if (depth <= 8) {
317         int numColors = bih.biClrUsed;
318         if (numColors == 0) {
319             numColors = 1 << depth;
320         } else {
321             if (numColors > 256)
322                 numColors = 256;
323         }
324         byte[] buf = new byte[numColors * 4];
325         raf.read(buf);
326         return paletteFromBytes(buf, numColors);
327     }
328     if (depth == 16) return new PaletteData(0x7C00, 0x3E0, 0x1F);
329     if (depth == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000);
330     return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
331 }
332 static PaletteData paletteFromBytes(byte[] bytes, int numColors) {
333     int bytesOffset = 0;
334     RGB[] colors = new RGB[numColors];
335     for (int i = 0; i < numColors; i++) {
336         colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
337             bytes[bytesOffset + 1] & 0xFF,
338             bytes[bytesOffset] & 0xFF);
339         bytesOffset += 4;
340     }
341     return new PaletteData(colors);
342 }
343 static byte[] loadData(BITMAPINFOHEADER bih, RandomAccessFile raf) throws IOException {
344     int stride = (bih.biWidth * bih.biBitCount + 7) / 8;
345     stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
346
byte[] data = loadData(bih, raf, stride);
347     flipScanLines(data, stride, bih.biHeight);
348     return data;
349 }
350 static void flipScanLines(byte[] data, int stride, int height) {
351     int i1 = 0;
352     int i2 = (height - 1) * stride;
353     for (int i = 0; i < height / 2; i++) {
354         for (int index = 0; index < stride; index++) {
355             byte b = data[index + i1];
356             data[index + i1] = data[index + i2];
357             data[index + i2] = b;
358         }
359         i1 += stride;
360         i2 -= stride;
361     }
362 }
363 static byte[] loadData(BITMAPINFOHEADER bih, RandomAccessFile raf, int stride) throws IOException {
364     int dataSize = bih.biHeight * stride;
365     byte[] data = new byte[dataSize];
366     int cmp = bih.biCompression;
367     if (cmp == 0) { // BMP_NO_COMPRESSION
368
raf.read(data);
369     } else {
370         if (DEBUG) System.out.println("ICO cannot be compressed?"); //$NON-NLS-1$
371
}
372     return data;
373 }
374
375 static void unloadIcon(RandomAccessFile raf, ImageData icon) throws IOException {
376     int sizeImage = (((icon.width * icon.depth + 31) / 32 * 4) +
377         ((icon.width + 31) / 32 * 4)) * icon.height;
378     write4(raf, BMPHeaderFixedSize);
379     write4(raf, icon.width);
380     write4(raf, icon.height * 2);
381     writeU2(raf, 1);
382     writeU2(raf, icon.depth);
383     write4(raf, 0);
384     write4(raf, sizeImage);
385     write4(raf, 0);
386     write4(raf, 0);
387     write4(raf, icon.palette.colors != null ? icon.palette.colors.length : 0);
388     write4(raf, 0);
389     
390     byte[] rgbs = paletteToBytes(icon.palette);
391     raf.write(rgbs);
392     unloadShapeData(raf, icon);
393     unloadMaskData(raf, icon);
394 }
395 static byte[] paletteToBytes(PaletteData pal) {
396     int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256);
397     byte[] bytes = new byte[n * 4];
398     int offset = 0;
399     for (int i = 0; i < n; i++) {
400         RGB col = pal.colors[i];
401         bytes[offset] = (byte)col.blue;
402         bytes[offset + 1] = (byte)col.green;
403         bytes[offset + 2] = (byte)col.red;
404         offset += 4;
405     }
406     return bytes;
407 }
408 static void unloadMaskData(RandomAccessFile raf, ImageData icon) {
409     ImageData mask = icon.getTransparencyMask();
410     int bpl = (icon.width + 7) / 8;
411     int pad = mask.scanlinePad;
412     int srcBpl = (bpl + pad - 1) / pad * pad;
413     int destBpl = (bpl + 3) / 4 * 4;
414     byte[] buf = new byte[destBpl];
415     int offset = (icon.height - 1) * srcBpl;
416     byte[] data = mask.data;
417     try {
418         for (int i = 0; i < icon.height; i++) {
419             System.arraycopy(data, offset, buf, 0, bpl);
420             bitInvertData(buf, 0, bpl);
421             raf.write(buf, 0, destBpl);
422             offset -= srcBpl;
423         }
424     } catch (IOException e) {
425         SWT.error(SWT.ERROR_IO, e);
426     }
427 }
428 static void unloadShapeData(RandomAccessFile raf, ImageData icon) {
429     int bpl = (icon.width * icon.depth + 7) / 8;
430     int pad = icon.scanlinePad;
431     int srcBpl = (bpl + pad - 1) / pad * pad;
432     int destBpl = (bpl + 3) / 4 * 4;
433     byte[] buf = new byte[destBpl];
434     int offset = (icon.height - 1) * srcBpl;
435     byte[] data = icon.data;
436     try {
437         for (int i = 0; i < icon.height; i++) {
438             System.arraycopy(data, offset, buf, 0, bpl);
439             raf.write(buf, 0, destBpl);
440             offset -= srcBpl;
441         }
442     } catch (IOException e) {
443         SWT.error(SWT.ERROR_IO, e);
444     }
445 }
446 static boolean readIconGroup(RandomAccessFile raf, int offset, int size) throws IOException {
447     raf.seek(offset);
448     NEWHEADER newHeader = new NEWHEADER();
449     read(raf, newHeader);
450     if (newHeader.ResType != RES_ICON) return false;
451     RESDIR[] resDir = new RESDIR[newHeader.ResCount];
452     for (int i = 0; i < newHeader.ResCount; i++) {
453         resDir[i] = new RESDIR();
454         read(raf, resDir[i]);
455     }
456     return true;
457 }
458
459 static void copyFile(String JavaDoc src, String JavaDoc dst) throws FileNotFoundException, IOException {
460     File srcFile = new File(src);
461     File dstFile = new File(dst);
462     InputStream in = new BufferedInputStream(new FileInputStream(srcFile));
463     OutputStream out = new BufferedOutputStream(new FileOutputStream(dstFile));
464     int c;
465     while ((c = in.read()) != -1) out.write(c);
466     in.close();
467     out.close();
468 }
469
470 /* IO utilities to parse Windows executable */
471 static final int IMAGE_DOS_SIGNATURE = 0x5a4d;
472 static final int IMAGE_NT_SIGNATURE = 0x00004550;
473 static final int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2;
474 static final int RES_ICON = 1;
475 static final int RT_ICON = 3;
476 static final int RT_GROUP_ICON = 14;
477 static final int BMPHeaderFixedSize = 40;
478     
479 public static class IMAGE_DOS_HEADER {
480     int e_magic; // WORD
481
int e_cblp; // WORD
482
int e_cp; // WORD
483
int e_crlc; // WORD
484
int e_cparhdr; // WORD
485
int e_minalloc; // WORD
486
int e_maxalloc; // WORD
487
int e_ss; // WORD
488
int e_sp; // WORD
489
int e_csum; // WORD
490
int e_ip; // WORD
491
int e_cs; // WORD
492
int e_lfarlc; // WORD
493
int e_ovno; // WORD
494
int[] e_res = new int[4]; // WORD[4]
495
int e_oemid; // WORD
496
int e_oeminfo; // WORD
497
int[] e_res2 = new int[10]; // WORD[10]
498
int e_lfanew; // LONG
499
}
500
501 public static class IMAGE_FILE_HEADER {
502     int Machine; // WORD
503
int NumberOfSections; // WORD
504
int TimeDateStamp; // DWORD
505
int PointerToSymbolTable; // DWORD
506
int NumberOfSymbols; // DWORD
507
int SizeOfOptionalHeader; // WORD
508
int Characteristics; // WORD
509
}
510
511 public static class IMAGE_DATA_DIRECTORY {
512     int VirtualAddress; // DWORD
513
int Size; // DWORD
514
}
515
516 public static class IMAGE_OPTIONAL_HEADER {
517     int Magic; // WORD
518
int MajorLinkerVersion; // BYTE
519
int MinorLinkerVersion; // BYTE
520
int SizeOfCode; // DWORD
521
int SizeOfInitializedData; // DWORD
522
int SizeOfUninitializedData; // DWORD
523
int AddressOfEntryPoint; // DWORD
524
int BaseOfCode; // DWORD
525
int BaseOfData; // DWORD
526
int ImageBase; // DWORD
527
int SectionAlignment; // DWORD
528
int FileAlignment; // DWORD
529
int MajorOperatingSystemVersion; // WORD
530
int MinorOperatingSystemVersion; // WORD
531
int MajorImageVersion; // WORD
532
int MinorImageVersion; // WORD
533
int MajorSubsystemVersion; // WORD
534
int MinorSubsystemVersion; // WORD
535
int Win32VersionValue; // DWORD
536
int SizeOfImage; // DWORD
537
int SizeOfHeaders; // DWORD
538
int CheckSum; // DWORD
539
int Subsystem; // WORD
540
int DllCharacteristics; // WORD
541
int SizeOfStackReserve; // DWORD
542
int SizeOfStackCommit; // DWORD
543
int SizeOfHeapReserve; // DWORD
544
int SizeOfHeapCommit; // DWORD
545
int LoaderFlags; // DWORD
546
int NumberOfRvaAndSizes; // DWORD
547
IMAGE_DATA_DIRECTORY[] DataDirectory = new IMAGE_DATA_DIRECTORY[16];
548 }
549 public static class IMAGE_NT_HEADERS {
550     int Signature; // DWORD
551
IMAGE_FILE_HEADER FileHeader = new IMAGE_FILE_HEADER();
552     IMAGE_OPTIONAL_HEADER OptionalHeader = new IMAGE_OPTIONAL_HEADER();
553     final static int FIELD_OFFSET_OptionalHeader = 24;
554 }
555     
556 public static class IMAGE_SECTION_HEADER {
557     int[] Name = new int[8]; // BYTE[8]
558
int Misc_VirtualSize; // DWORD (union Misc { DWORD PhysicalAddress; DWORD VirtualSize }
559
int VirtualAddress; // DWORD
560
int SizeOfRawData; // DWORD
561
int PointerToRawData; // DWORD
562
int PointerToRelocations; // DWORD
563
int PointerToLinenumbers; // DWORD
564
int NumberOfRelocations; // WORD
565
int NumberOfLinenumbers; // WORD
566
int Characteristics; // DWORD
567
}
568
569 public static class IMAGE_RESOURCE_DIRECTORY {
570     int Characteristics; // DWORD
571
int TimeDateStamp; // DWORD
572
int MajorVersion; // WORD
573
int MinorVersion; // WORD
574
int NumberOfNamedEntries; // WORD - used
575
int NumberOfIdEntries; // WORD - used
576
final static int SIZEOF = 16;
577 }
578
579 public static class IMAGE_RESOURCE_DIRECTORY_ENTRY {
580     // union
581
int NameOffset; // DWORD 31 bits
582
boolean NameIsString; // DWORD 1 bit
583
int Name; // DWORD
584
int Id; // WORD
585
// union
586
int OffsetToData; // DWORD
587
int OffsetToDirectory; // DWORD 31 bits
588
boolean DataIsDirectory; // DWORD 1 bit
589
}
590
591 public static class IMAGE_RESOURCE_DATA_ENTRY {
592     int OffsetToData; // DWORD
593
int Size; // DWORD
594
int CodePage; // DWORD
595
int Reserved; // DWORD
596
}
597
598 public static class NEWHEADER {
599     int Reserved; // WORD
600
int ResType; // WORD
601
int ResCount; // WORD
602
}
603
604 public static class ICONRESDIR {
605     int Width; // BYTE
606
int Height; // BYTE
607
int ColorCount; // BYTE
608
int reserved; // BYTE
609
}
610
611 public static class CURSORDIR {
612     int Width; // WORD
613
int Height; // WORD
614
}
615
616 public static class RESDIR {
617     // union
618
ICONRESDIR Icon = new ICONRESDIR();
619     CURSORDIR Cursor = new CURSORDIR();
620     int Planes; // WORD
621
int BitCount; // WORD
622
int BytesInRes; // DWORD
623
int IconCursorId; // WORD
624
}
625
626 public static class BITMAPINFOHEADER {
627     int biSize; // DWORD
628
int biWidth; // LONG
629
int biHeight; // LONG
630
int biPlanes; // WORD
631
int biBitCount; // WORD
632
int biCompression; // DWORD
633
int biSizeImage; // DWORD
634
int biXPelsPerMeter; // LONG
635
int biYPelsPerMeter; // LONG
636
int biClrUsed; // DWORD
637
int biClrImportant; // DWORD
638
}
639
640 static class RGBQUAD {
641     int rgBlue; // BYTE
642
int rgbGreen; // BYTE
643
int rgbRed; // BYTE
644
int rgbReserved; // BYTE
645
}
646 static class BITMAPINFO {
647     BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
648     RGBQUAD[] bmiColors = null;
649 }
650 static void read(RandomAccessFile raf, BITMAPINFOHEADER bih) throws IOException {
651     bih.biSize = read4(raf);
652     bih.biWidth = read4(raf);
653     bih.biHeight = read4(raf);
654     bih.biPlanes = readU2(raf);
655     bih.biBitCount = readU2(raf);
656     bih.biCompression = read4(raf);
657     bih.biSizeImage = read4(raf);
658     bih.biXPelsPerMeter = read4(raf);
659     bih.biYPelsPerMeter = read4(raf);
660     bih.biClrUsed = read4(raf);
661     bih.biClrImportant = read4(raf);
662 }
663 static void read(RandomAccessFile raf, BITMAPINFO bi) throws IOException {
664     read(raf, bi.bmiHeader);
665 }
666 /* Little Endian helpers */
667 static int readU2(RandomAccessFile raf) throws IOException {
668     int b0 = raf.readByte() & 0xFF;
669     int b1 = raf.readByte() & 0xFF;
670     return (b1 << 8 | b0);
671 }
672 static int read4(RandomAccessFile raf) throws IOException {
673     int b0 = raf.readByte() & 0xFF;
674     int b1 = raf.readByte() & 0xFF;
675     int b2 = raf.readByte() & 0xFF;
676     int b3 = raf.readByte() & 0xFF;
677     return b3 << 24 | b2 << 16 | b1 << 8 | b0;
678 }
679 static void write4(RandomAccessFile raf, int value) throws IOException {
680     raf.write(value & 0xFF);
681     raf.write((value >> 8) & 0xFF);
682     raf.write((value >> 16) & 0xFF);
683     raf.write((value >> 24) & 0xFF);
684 }
685 static void writeU2(RandomAccessFile raf, int value) throws IOException {
686     raf.write(value & 0xFF);
687     raf.write((value >> 8) & 0xFF);
688 }
689 static void read(RandomAccessFile raf, IMAGE_DOS_HEADER idh) throws IOException {
690     idh.e_magic = readU2(raf);
691     idh.e_cblp = readU2(raf);
692     idh.e_cp = readU2(raf);
693     idh.e_crlc = readU2(raf);
694     idh.e_cparhdr = readU2(raf);
695     idh.e_minalloc = readU2(raf);
696     idh.e_maxalloc = readU2(raf);
697     idh.e_ss = readU2(raf);
698     idh.e_sp = readU2(raf);
699     idh.e_csum = readU2(raf);
700     idh.e_ip = readU2(raf);
701     idh.e_cs = readU2(raf);
702     idh.e_lfarlc = readU2(raf);
703     idh.e_ovno = readU2(raf);
704     for (int i = 0; i < idh.e_res.length; i++) idh.e_res[i] = readU2(raf);
705     idh.e_oemid = readU2(raf);
706     idh.e_oeminfo = readU2(raf);
707     for (int i = 0; i < idh.e_res2.length; i++) idh.e_res2[i] = readU2(raf);
708     idh.e_lfanew = read4(raf);
709 }
710 static void read(RandomAccessFile raf, IMAGE_FILE_HEADER ifh) throws IOException {
711     ifh.Machine = readU2(raf);
712     ifh.NumberOfSections = readU2(raf);
713     ifh.TimeDateStamp = read4(raf);
714     ifh.PointerToSymbolTable = read4(raf);
715     ifh.NumberOfSymbols = read4(raf);
716     ifh.SizeOfOptionalHeader = readU2(raf);
717     ifh.Characteristics = readU2(raf);
718 }
719 static void read(RandomAccessFile raf, IMAGE_DATA_DIRECTORY idd) throws IOException {
720     idd.VirtualAddress = read4(raf);
721     idd.Size = read4(raf);
722 }
723 static void read(RandomAccessFile raf, IMAGE_OPTIONAL_HEADER ioh) throws IOException {
724     ioh.Magic = readU2(raf);
725     ioh.MajorLinkerVersion = raf.read();
726     ioh.MinorLinkerVersion = raf.read();
727     ioh.SizeOfCode = read4(raf);
728     ioh.SizeOfInitializedData = read4(raf);
729     ioh.SizeOfUninitializedData = read4(raf);
730     ioh.AddressOfEntryPoint = read4(raf);
731     ioh.BaseOfCode = read4(raf);
732     ioh.BaseOfData = read4(raf);
733     ioh.ImageBase = read4(raf);
734     ioh.SectionAlignment = read4(raf);
735     ioh.FileAlignment = read4(raf);
736     ioh.MajorOperatingSystemVersion = readU2(raf);
737     ioh.MinorOperatingSystemVersion = readU2(raf);
738     ioh.MajorImageVersion = readU2(raf);
739     ioh.MinorImageVersion = readU2(raf);
740     ioh.MajorSubsystemVersion = readU2(raf);
741     ioh.MinorSubsystemVersion = readU2(raf);
742     ioh.Win32VersionValue = read4(raf);
743     ioh.SizeOfImage = read4(raf);
744     ioh.SizeOfHeaders = read4(raf);
745     ioh.CheckSum = read4(raf);
746     ioh.Subsystem = readU2(raf);
747     ioh.DllCharacteristics = readU2(raf);
748     ioh.SizeOfStackReserve = read4(raf);
749     ioh.SizeOfStackCommit = read4(raf);
750     ioh.SizeOfHeapReserve = read4(raf);
751     ioh.SizeOfHeapCommit = read4(raf);
752     ioh.LoaderFlags = read4(raf);
753     ioh.NumberOfRvaAndSizes = read4(raf);
754     for (int i = 0 ; i < ioh.DataDirectory.length; i++) {
755         ioh.DataDirectory[i] = new IMAGE_DATA_DIRECTORY();
756         read(raf, ioh.DataDirectory[i]);
757     }
758 }
759 static void read(RandomAccessFile raf, IMAGE_NT_HEADERS inh) throws IOException {
760     inh.Signature = read4(raf);
761     read(raf, inh.FileHeader);
762     read(raf, inh.OptionalHeader);
763 }
764 static void read(RandomAccessFile raf, IMAGE_SECTION_HEADER ish) throws IOException {
765     for (int i = 0 ; i < ish.Name.length; i++) ish.Name[i] = raf.read();
766     ish.Misc_VirtualSize = read4(raf);
767     ish.VirtualAddress = read4(raf);
768     ish.SizeOfRawData = read4(raf);
769     ish.PointerToRawData = read4(raf);
770     ish.PointerToRelocations = read4(raf);
771     ish.PointerToLinenumbers = read4(raf);
772     ish.NumberOfRelocations = readU2(raf);
773     ish.NumberOfLinenumbers = readU2(raf);
774     ish.Characteristics = read4(raf);
775 }
776 static void read(RandomAccessFile raf, IMAGE_RESOURCE_DIRECTORY ird) throws IOException {
777     ird.Characteristics = read4(raf);
778     ird.TimeDateStamp = read4(raf);
779     ird.MajorVersion = readU2(raf);
780     ird.MinorVersion = readU2(raf);
781     ird.NumberOfNamedEntries = readU2(raf);
782     ird.NumberOfIdEntries = readU2(raf);
783 }
784 static void read(RandomAccessFile raf, IMAGE_RESOURCE_DIRECTORY_ENTRY irde) throws IOException {
785     irde.Name = read4(raf);
786     irde.OffsetToData = read4(raf);
787     // construct other union members
788
irde.NameOffset = irde.Name & ~ (1 << 31);
789     irde.NameIsString = (irde.Name & (1 << 31)) != 0;
790     irde.Id = irde.Name & 0xFFFF;
791     irde.OffsetToDirectory = irde.OffsetToData & ~ (1 << 31);
792     irde.DataIsDirectory = (irde.OffsetToData & (1 << 31)) != 0;
793 }
794 static void read(RandomAccessFile raf, IMAGE_RESOURCE_DATA_ENTRY irde) throws IOException {
795     irde.OffsetToData = read4(raf);
796     irde.Size = read4(raf);
797     irde.CodePage = read4(raf);
798     irde.Reserved = read4(raf);
799 }
800 static void read(RandomAccessFile raf, NEWHEADER nh) throws IOException {
801     nh.Reserved = readU2(raf);
802     nh.ResType = readU2(raf);
803     nh.ResCount = readU2(raf);
804 }
805 static void read(RandomAccessFile raf, ICONRESDIR i) throws IOException {
806     i.Width = raf.read();
807     i.Height = raf.read();
808     i.ColorCount = raf.read();
809     i.reserved = raf.read();
810 }
811 static void read(RandomAccessFile raf, CURSORDIR c) throws IOException {
812     c.Width = readU2(raf);
813     c.Height = readU2(raf);
814 }
815 static void read(RandomAccessFile raf, RESDIR rs) throws IOException {
816     long start = raf.getFilePointer();
817     read(raf, rs.Icon);
818     raf.seek(start);
819     read(raf, rs.Cursor);
820     rs.Planes = readU2(raf);
821     rs.BitCount = readU2(raf);
822     rs.BytesInRes = read4(raf);
823     rs.IconCursorId = readU2(raf);
824 }
825
826 /* ImageData and Image Decoder inlining to avoid dependency on SWT
827  * The following section can be entirely removed if SWT can be used.
828  */

829
830 static class RGB {
831     
832     /**
833      * the red component of the RGB
834      */

835     public int red;
836     
837     /**
838      * the green component of the RGB
839      */

840     public int green;
841     
842     /**
843      * the blue component of the RGB
844      */

845     public int blue;
846     
847     static final long serialVersionUID = 3258415023461249074L;
848     
849 /**
850  * Constructs an instance of this class with the given
851  * red, green and blue values.
852  *
853  * @param red the red component of the new instance
854  * @param green the green component of the new instance
855  * @param blue the blue component of the new instance
856  *
857  * @exception IllegalArgumentException <ul>
858  * <li>ERROR_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li>
859  * </ul>
860  */

861 public RGB(int red, int green, int blue) {
862     if ((red > 255) || (red < 0) ||
863         (green > 255) || (green < 0) ||
864         (blue > 255) || (blue < 0))
865             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
866     this.red = red;
867     this.green = green;
868     this.blue = blue;
869 }
870
871 /**
872  * Compares the argument to the receiver, and returns true
873  * if they represent the <em>same</em> object using a class
874  * specific comparison.
875  *
876  * @param object the object to compare with this object
877  * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
878  *
879  * @see #hashCode()
880  */

881 public boolean equals (Object JavaDoc object) {
882     if (object == this) return true;
883     if (!(object instanceof RGB)) return false;
884     RGB rgb = (RGB)object;
885     return (rgb.red == this.red) && (rgb.green == this.green) && (rgb.blue == this.blue);
886 }
887
888 /**
889  * Returns an integer hash code for the receiver. Any two
890  * objects which return <code>true</code> when passed to
891  * <code>equals</code> must return the same value for this
892  * method.
893  *
894  * @return the receiver's hash
895  *
896  * @see #equals(Object)
897  */

898 public int hashCode () {
899     return (blue << 16) | (green << 8) | red;
900 }
901
902 /**
903  * Returns a string containing a concise, human-readable
904  * description of the receiver.
905  *
906  * @return a string representation of the <code>RGB</code>
907  */

908 public String JavaDoc toString () {
909     return "RGB {" + red + ", " + green + ", " + blue + "}"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
910

911 //$NON-NLS-4$
912
}
913
914 }
915 static class PaletteData {
916     
917     /**
918      * true if the receiver is a direct palette,
919      * and false otherwise
920      */

921     public boolean isDirect;
922     
923     /**
924      * the RGB values for an indexed palette, where the
925      * indices of the array correspond to pixel values
926      */

927     public RGB[] colors;
928     
929     /**
930      * the red mask for a direct palette
931      */

932     public int redMask;
933     
934     /**
935      * the green mask for a direct palette
936      */

937     public int greenMask;
938     
939     /**
940      * the blue mask for a direct palette
941      */

942     public int blueMask;
943     
944     /**
945      * the red shift for a direct palette
946      */

947     public int redShift;
948     
949     /**
950      * the green shift for a direct palette
951      */

952     public int greenShift;
953     
954     /**
955      * the blue shift for a direct palette
956      */

957     public int blueShift;
958
959 /**
960  * Constructs a new indexed palette given an array of RGB values.
961  *
962  * @param colors the array of <code>RGB</code>s for the palette
963  *
964  * @exception IllegalArgumentException <ul>
965  * <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
966  * </ul>
967  */

968 public PaletteData(RGB[] colors) {
969     if (colors == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
970     this.colors = colors;
971     this.isDirect = false;
972 }
973
974 /**
975  * Constructs a new direct palette given the red, green and blue masks.
976  *
977  * @param redMask the red mask
978  * @param greenMask the green mask
979  * @param blueMask the blue mask
980  */

981 public PaletteData(int redMask, int greenMask, int blueMask) {
982     this.redMask = redMask;
983     this.greenMask = greenMask;
984     this.blueMask = blueMask;
985     this.isDirect = true;
986     this.redShift = shiftForMask(redMask);
987     this.greenShift = shiftForMask(greenMask);
988     this.blueShift = shiftForMask(blueMask);
989 }
990
991 /**
992  * Returns the pixel value corresponding to the given <code>RBG</code>.
993  *
994  * @param rgb the RGB to get the pixel value for
995  * @return the pixel value for the given RGB
996  *
997  * @exception IllegalArgumentException <ul>
998  * <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
999  * <li>ERROR_INVALID_ARGUMENT - if the RGB is not found in the palette</li>
1000 * </ul>
1001 */

1002public int getPixel(RGB rgb) {
1003    if (rgb == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1004    if (isDirect) {
1005        int pixel = 0;
1006        pixel |= (redShift < 0 ? rgb.red << -redShift : rgb.red >>> redShift) & redMask;
1007        pixel |= (greenShift < 0 ? rgb.green << -greenShift : rgb.green >>> greenShift) & greenMask;
1008        pixel |= (blueShift < 0 ? rgb.blue << -blueShift : rgb.blue >>> blueShift) & blueMask;
1009        return pixel;
1010    }
1011
1012    for (int i = 0; i < colors.length; i++) {
1013        if (colors[i].equals(rgb)) return i;
1014    }
1015    /* The RGB did not exist in the palette */
1016    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1017    return 0;
1018}
1019
1020/**
1021 * Returns an <code>RGB</code> corresponding to the given pixel value.
1022 *
1023 * @param pixel the pixel to get the RGB value for
1024 * @return the RGB value for the given pixel
1025 *
1026 * @exception IllegalArgumentException <ul>
1027 * <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
1028 * <li>ERROR_INVALID_ARGUMENT - if the pixel does not exist in the palette</li>
1029 * </ul>
1030 */

1031public RGB getRGB(int pixel) {
1032    if (isDirect) {
1033        int r = pixel & redMask;
1034        r = (redShift < 0) ? r >>> -redShift : r << redShift;
1035        int g = pixel & greenMask;
1036        g = (greenShift < 0) ? g >>> -greenShift : g << greenShift;
1037        int b = pixel & blueMask;
1038        b = (blueShift < 0) ? b >>> -blueShift : b << blueShift;
1039        return new RGB(r, g, b);
1040    }
1041    if (pixel < 0 || pixel >= colors.length) {
1042        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1043    }
1044    return colors[pixel];
1045}
1046
1047/**
1048 * Returns all the RGB values in the receiver if it is an
1049 * indexed palette, or null if it is a direct palette.
1050 *
1051 * @return the <code>RGB</code>s for the receiver or null
1052 */

1053public RGB[] getRGBs() {
1054    return colors;
1055}
1056
1057/**
1058 * Computes the shift value for a given mask.
1059 *
1060 * @param mask the mask to compute the shift for
1061 * @return the shift amount
1062 *
1063 * @see PaletteData
1064 */

1065int shiftForMask(int mask) {
1066    for (int i = 31; i >= 0; i--) {
1067        if (((mask >> i) & 0x1) != 0) return 7 - i;
1068    }
1069    return 32;
1070}
1071
1072}
1073static class ImageLoader {
1074    
1075    /**
1076     * the array of ImageData objects in this ImageLoader.
1077     * This array is read in when the load method is called,
1078     * and it is written out when the save method is called
1079     */

1080    public ImageData[] data;
1081    
1082    /**
1083     * the width of the logical screen on which the images
1084     * reside, in pixels (this corresponds to the GIF89a
1085     * Logical Screen Width value)
1086     */

1087    public int logicalScreenWidth;
1088
1089    /**
1090     * the height of the logical screen on which the images
1091     * reside, in pixels (this corresponds to the GIF89a
1092     * Logical Screen Height value)
1093     */

1094    public int logicalScreenHeight;
1095
1096    /**
1097     * the background pixel for the logical screen (this
1098     * corresponds to the GIF89a Background Color Index value).
1099     * The default is -1 which means 'unspecified background'
1100     *
1101     */

1102    public int backgroundPixel;
1103
1104    /**
1105     * the number of times to repeat the display of a sequence
1106     * of animated images (this corresponds to the commonly-used
1107     * GIF application extension for "NETSCAPE 2.0 01")
1108     */

1109    public int repeatCount;
1110        
1111    /*
1112     * the set of ImageLoader event listeners, created on demand
1113     */

1114    Vector imageLoaderListeners;
1115
1116/**
1117 * Construct a new empty ImageLoader.
1118 */

1119public ImageLoader() {
1120    reset();
1121}
1122
1123/**
1124 * Resets the fields of the ImageLoader, except for the
1125 * <code>imageLoaderListeners</code> field.
1126 */

1127void reset() {
1128    data = null;
1129    logicalScreenWidth = 0;
1130    logicalScreenHeight = 0;
1131    backgroundPixel = -1;
1132    repeatCount = 1;
1133}
1134
1135/**
1136 * Loads an array of <code>ImageData</code> objects from the
1137 * specified input stream. Throws an error if either an error
1138 * occurs while loading the images, or if the images are not
1139 * of a supported type. Returns the loaded image data array.
1140 *
1141 * @param stream the input stream to load the images from
1142 * @return an array of <code>ImageData</code> objects loaded from the specified input stream
1143 *
1144 * @exception IllegalArgumentException <ul>
1145 * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
1146 * </ul>
1147 * @exception SWTException <ul>
1148 * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
1149 * <li>ERROR_IO - if an input/output error occurs while reading data</li>
1150 * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
1151 * </ul>
1152 */

1153public ImageData[] load(InputStream stream) {
1154    if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1155    reset();
1156    data = FileFormat.load(stream, this);
1157    return data;
1158}
1159
1160/**
1161 * Loads an array of <code>ImageData</code> objects from the
1162 * file with the specified name. Throws an error if either
1163 * an error occurs while loading the images, or if the images are
1164 * not of a supported type. Returns the loaded image data array.
1165 *
1166 * @param filename the name of the file to load the images from
1167 * @return an array of <code>ImageData</code> objects loaded from the specified file
1168 *
1169 * @exception IllegalArgumentException <ul>
1170 * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
1171 * </ul>
1172 * @exception SWTException <ul>
1173 * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
1174 * <li>ERROR_IO - if an IO error occurs while reading data</li>
1175 * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
1176 * </ul>
1177 */

1178public ImageData[] load(String JavaDoc filename) {
1179    if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1180    InputStream stream = null;
1181    try {
1182        stream = new BufferedInputStream(new FileInputStream(filename));
1183        return load(stream);
1184    } catch (IOException e) {
1185        SWT.error(SWT.ERROR_IO, e);
1186    } finally {
1187        try {
1188            if (stream != null) stream.close();
1189        } catch (IOException e) {
1190            // Ignore error
1191
}
1192    }
1193    return null;
1194}
1195}
1196static class ImageData {
1197    
1198    /**
1199     * The width of the image, in pixels.
1200     */

1201    public int width;
1202
1203    /**
1204     * The height of the image, in pixels.
1205     */

1206    public int height;
1207
1208    /**
1209     * The color depth of the image, in bits per pixel.
1210     * <p>
1211     * Note that a depth of 8 or less does not necessarily
1212     * mean that the image is palette indexed, or
1213     * conversely that a depth greater than 8 means that
1214     * the image is direct color. Check the associated
1215     * PaletteData's isDirect field for such determinations.
1216     */

1217    public int depth;
1218
1219    /**
1220     * The scanline padding.
1221     * <p>
1222     * If one scanline of the image is not a multiple of
1223     * this number, it will be padded with zeros until it is.
1224     * </p>
1225     */

1226    public int scanlinePad;
1227
1228    /**
1229     * The number of bytes per scanline.
1230     * <p>
1231     * This is a multiple of the scanline padding.
1232     * </p>
1233     */

1234    public int bytesPerLine;
1235
1236    /**
1237     * The pixel data of the image.
1238     * <p>
1239     * Note that for 16 bit depth images the pixel data is stored
1240     * in least significant byte order; however, for 24bit and
1241     * 32bit depth images the pixel data is stored in most
1242     * significant byte order.
1243     * </p>
1244     */

1245    public byte[] data;
1246
1247    /**
1248     * The color table for the image.
1249     */

1250    public PaletteData palette;
1251
1252    /**
1253     * The transparent pixel.
1254     * <p>
1255     * Pixels with this value are transparent.
1256     * </p><p>
1257     * The default is -1 which means 'no transparent pixel'.
1258     * </p>
1259     */

1260    public int transparentPixel;
1261
1262    /**
1263     * An icon-specific field containing the data from the icon mask.
1264     * <p>
1265     * This is a 1 bit bitmap stored with the most significant
1266     * bit first. The number of bytes per scanline is
1267     * '((width + 7) / 8 + (maskPad - 1)) / maskPad * maskPad'.
1268     * </p><p>
1269     * The default is null which means 'no transparency mask'.
1270     * </p>
1271     */

1272    public byte[] maskData;
1273
1274    /**
1275     * An icon-specific field containing the scanline pad of the mask.
1276     * <p>
1277     * If one scanline of the transparency mask is not a
1278     * multiple of this number, it will be padded with zeros until
1279     * it is.
1280     * </p>
1281     */

1282    public int maskPad;
1283    
1284    /**
1285     * The alpha data of the image.
1286     * <p>
1287     * Every pixel can have an <em>alpha blending</em> value that
1288     * varies from 0, meaning fully transparent, to 255 meaning
1289     * fully opaque. The number of bytes per scanline is
1290     * 'width'.
1291     * </p>
1292     */

1293    public byte[] alphaData;
1294    
1295    /**
1296     * The global alpha value to be used for every pixel.
1297     * <p>
1298     * If this value is set, the <code>alphaData</code> field
1299     * is ignored and when the image is rendered each pixel
1300     * will be blended with the background an amount
1301     * proportional to this value.
1302     * </p><p>
1303     * The default is -1 which means 'no global alpha value'
1304     * </p>
1305     */

1306    public int alpha;
1307
1308    /**
1309     * The type of file from which the image was read.
1310     *
1311     * It is expressed as one of the following values:
1312     * <dl>
1313     * <dt><code>IMAGE_BMP</code></dt>
1314     * <dd>Windows BMP file format, no compression</dd>
1315     * <dt><code>IMAGE_BMP_RLE</code></dt>
1316     * <dd>Windows BMP file format, RLE compression if appropriate</dd>
1317     * <dt><code>IMAGE_GIF</code></dt>
1318     * <dd>GIF file format</dd>
1319     * <dt><code>IMAGE_ICO</code></dt>
1320     * <dd>Windows ICO file format</dd>
1321     * <dt><code>IMAGE_JPEG</code></dt>
1322     * <dd>JPEG file format</dd>
1323     * <dt><code>IMAGE_PNG</code></dt>
1324     * <dd>PNG file format</dd>
1325     * </dl>
1326     */

1327    public int type;
1328
1329    /**
1330     * The x coordinate of the top left corner of the image
1331     * within the logical screen (this field corresponds to
1332     * the GIF89a Image Left Position value).
1333     */

1334    public int x;
1335
1336    /**
1337     * The y coordinate of the top left corner of the image
1338     * within the logical screen (this field corresponds to
1339     * the GIF89a Image Top Position value).
1340     */

1341    public int y;
1342
1343    /**
1344     * A description of how to dispose of the current image
1345     * before displaying the next.
1346     *
1347     * It is expressed as one of the following values:
1348     * <dl>
1349     * <dt><code>DM_UNSPECIFIED</code></dt>
1350     * <dd>disposal method not specified</dd>
1351     * <dt><code>DM_FILL_NONE</code></dt>
1352     * <dd>do nothing - leave the image in place</dd>
1353     * <dt><code>DM_FILL_BACKGROUND</code></dt>
1354     * <dd>fill with the background color</dd>
1355     * <dt><code>DM_FILL_PREVIOUS</code></dt>
1356     * <dd>restore the previous picture</dd>
1357     * </dl>
1358     * (this field corresponds to the GIF89a Disposal Method value)
1359     */

1360    public int disposalMethod;
1361
1362    /**
1363     * The time to delay before displaying the next image
1364     * in an animation (this field corresponds to the GIF89a
1365     * Delay Time value).
1366     */

1367    public int delayTime;
1368
1369    /**
1370     * Arbitrary channel width data to 8-bit conversion table.
1371     */

1372    static final byte[][] ANY_TO_EIGHT = new byte[9][];
1373    static {
1374        for (int b = 0; b < 9; ++b) {
1375            byte[] data = ANY_TO_EIGHT[b] = new byte[1 << b];
1376            if (b == 0) continue;
1377            int inc = 0;
1378            for (int bit = 0x10000; (bit >>= b) != 0;) inc |= bit;
1379            for (int v = 0, p = 0; v < 0x10000; v+= inc) data[p++] = (byte)(v >> 8);
1380        }
1381    }
1382    static final byte[] ONE_TO_ONE_MAPPING = ANY_TO_EIGHT[8];
1383
1384    /**
1385     * Scaled 8x8 Bayer dither matrix.
1386     */

1387    static final int[][] DITHER_MATRIX = {
1388        { 0xfc0000, 0x7c0000, 0xdc0000, 0x5c0000, 0xf40000, 0x740000, 0xd40000, 0x540000 },
1389        { 0x3c0000, 0xbc0000, 0x1c0000, 0x9c0000, 0x340000, 0xb40000, 0x140000, 0x940000 },
1390        { 0xcc0000, 0x4c0000, 0xec0000, 0x6c0000, 0xc40000, 0x440000, 0xe40000, 0x640000 },
1391        { 0x0c0000, 0x8c0000, 0x2c0000, 0xac0000, 0x040000, 0x840000, 0x240000, 0xa40000 },
1392        { 0xf00000, 0x700000, 0xd00000, 0x500000, 0xf80000, 0x780000, 0xd80000, 0x580000 },
1393        { 0x300000, 0xb00000, 0x100000, 0x900000, 0x380000, 0xb80000, 0x180000, 0x980000 },
1394        { 0xc00000, 0x400000, 0xe00000, 0x600000, 0xc80000, 0x480000, 0xe80000, 0x680000 },
1395        { 0x000000, 0x800000, 0x200000, 0xa00000, 0x080000, 0x880000, 0x280000, 0xa80000 }
1396    };
1397
1398/**
1399 * Constructs a new, empty ImageData with the given width, height,
1400 * depth and palette. The data will be initialized to an (all zero)
1401 * array of the appropriate size.
1402 *
1403 * @param width the width of the image
1404 * @param height the height of the image
1405 * @param depth the depth of the image
1406 * @param palette the palette of the image (must not be null)
1407 *
1408 * @exception IllegalArgumentException <ul>
1409 * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
1410 * one of 1, 2, 4, 8, 16, 24 or 32</li>
1411 * <li>ERROR_NULL_ARGUMENT - if the palette is null</li>
1412 * </ul>
1413 */

1414public ImageData(int width, int height, int depth, PaletteData palette) {
1415    this(width, height, depth, palette,
1416        4, null, 0, null,
1417        null, -1, -1, SWT.IMAGE_UNDEFINED,
1418        0, 0, 0, 0);
1419}
1420
1421/**
1422 * Constructs a new, empty ImageData with the given width, height,
1423 * depth, palette, scanlinePad and data.
1424 *
1425 * @param width the width of the image
1426 * @param height the height of the image
1427 * @param depth the depth of the image
1428 * @param palette the palette of the image
1429 * @param scanlinePad the padding of each line, in bytes
1430 * @param data the data of the image
1431 *
1432 * @exception IllegalArgumentException <ul>
1433 * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
1434 * one of 1, 2, 4, 8, 16, 24 or 32</li>
1435 * <li>ERROR_NULL_ARGUMENT - if the palette or data is null</li>
1436 * <li>ERROR_CANNOT_BE_ZERO - if the scanlinePad is zero</li>
1437 * </ul>
1438 */

1439public ImageData(int width, int height, int depth, PaletteData palette, int scanlinePad, byte[] data) {
1440    this(width, height, depth, palette,
1441        scanlinePad, checkData(data), 0, null,
1442        null, -1, -1, SWT.IMAGE_UNDEFINED,
1443        0, 0, 0, 0);
1444}
1445
1446/**
1447 * Constructs an <code>ImageData</code> loaded from a file with the
1448 * specified name. Throws an error if an error occurs loading the
1449 * image, or if the image has an unsupported type.
1450 * <p>
1451 * This constructor is provided for convenience when loading a single
1452 * image only. If the file contains multiple images, only the first
1453 * one will be loaded. To load multiple images, use
1454 * <code>ImageLoader.load()</code>.
1455 * </p>
1456 *
1457 * @param filename the name of the file to load the image from (must not be null)
1458 *
1459 * @exception IllegalArgumentException <ul>
1460 * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
1461 * </ul>
1462 * @exception SWTException <ul>
1463 * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
1464 * <li>ERROR_IO if an IO error occurs while reading data</li>
1465 * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
1466 * </ul>
1467 */

1468public ImageData(String JavaDoc filename) {
1469    ImageData[] data = new ImageLoader().load(filename);
1470    if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
1471    ImageData i = data[0];
1472    setAllFields(
1473        i.width,
1474        i.height,
1475        i.depth,
1476        i.scanlinePad,
1477        i.bytesPerLine,
1478        i.data,
1479        i.palette,
1480        i.transparentPixel,
1481        i.maskData,
1482        i.maskPad,
1483        i.alphaData,
1484        i.alpha,
1485        i.type,
1486        i.x,
1487        i.y,
1488        i.disposalMethod,
1489        i.delayTime);
1490}
1491
1492/**
1493 * Prevents uninitialized instances from being created outside the package.
1494 */

1495ImageData() {
1496    //empty constructor
1497
}
1498
1499/**
1500 * Constructs an image data by giving values for all non-computable fields.
1501 * <p>
1502 * This method is for internal use, and is not described further.
1503 * </p>
1504 */

1505ImageData(
1506    int width, int height, int depth, PaletteData palette,
1507    int scanlinePad, byte[] data, int maskPad, byte[] maskData,
1508    byte[] alphaData, int alpha, int transparentPixel, int type,
1509    int x, int y, int disposalMethod, int delayTime)
1510{
1511
1512    if (palette == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1513    if (!(depth == 1 || depth == 2 || depth == 4 || depth == 8
1514        || depth == 16 || depth == 24 || depth == 32)) {
1515        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1516    }
1517    if (width <= 0 || height <= 0) {
1518        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1519    }
1520    if (scanlinePad == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO);
1521
1522    int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1))
1523        / scanlinePad * scanlinePad;
1524    setAllFields(
1525        width,
1526        height,
1527        depth,
1528        scanlinePad,
1529        bytesPerLine,
1530        data != null ? data : new byte[bytesPerLine * height],
1531        palette,
1532        transparentPixel,
1533        maskData,
1534        maskPad,
1535        alphaData,
1536        alpha,
1537        type,
1538        x,
1539        y,
1540        disposalMethod,
1541        delayTime);
1542}
1543
1544/**
1545 * Initializes all fields in the receiver. This method must be called
1546 * by all public constructors to ensure that all fields are initialized
1547 * for a new ImageData object. If a new field is added to the class,
1548 * then it must be added to this method.
1549 * <p>
1550 * This method is for internal use, and is not described further.
1551 * </p>
1552 */

1553void setAllFields(int width, int height, int depth, int scanlinePad,
1554    int bytesPerLine, byte[] data, PaletteData palette, int transparentPixel,
1555    byte[] maskData, int maskPad, byte[] alphaData, int alpha,
1556    int type, int x, int y, int disposalMethod, int delayTime) {
1557
1558    this.width = width;
1559    this.height = height;
1560    this.depth = depth;
1561    this.scanlinePad = scanlinePad;
1562    this.bytesPerLine = bytesPerLine;
1563    this.data = data;
1564    this.palette = palette;
1565    this.transparentPixel = transparentPixel;
1566    this.maskData = maskData;
1567    this.maskPad = maskPad;
1568    this.alphaData = alphaData;
1569    this.alpha = alpha;
1570    this.type = type;
1571    this.x = x;
1572    this.y = y;
1573    this.disposalMethod = disposalMethod;
1574    this.delayTime = delayTime;
1575}
1576
1577/**
1578 * Invokes internal SWT functionality to create a new instance of
1579 * this class.
1580 * <p>
1581 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
1582 * API for <code>ImageData</code>. It is marked public only so that it
1583 * can be shared within the packages provided by SWT. It is subject
1584 * to change without notice, and should never be called from
1585 * application code.
1586 * </p>
1587 * <p>
1588 * This method is for internal use, and is not described further.
1589 * </p>
1590 */

1591public static ImageData internal_new(
1592    int width, int height, int depth, PaletteData palette,
1593    int scanlinePad, byte[] data, int maskPad, byte[] maskData,
1594    byte[] alphaData, int alpha, int transparentPixel, int type,
1595    int x, int y, int disposalMethod, int delayTime)
1596{
1597    return new ImageData(
1598        width, height, depth, palette, scanlinePad, data, maskPad, maskData,
1599        alphaData, alpha, transparentPixel, type, x, y, disposalMethod, delayTime);
1600}
1601
1602ImageData colorMaskImage(int pixel) {
1603    ImageData mask = new ImageData(width, height, 1, bwPalette(),
1604        2, null, 0, null, null, -1, -1, SWT.IMAGE_UNDEFINED,
1605        0, 0, 0, 0);
1606    int[] row = new int[width];
1607    for (int y = 0; y < height; y++) {
1608        getPixels(0, y, width, row, 0);
1609        for (int i = 0; i < width; i++) {
1610            if (pixel != -1 && row[i] == pixel) {
1611                row[i] = 0;
1612            } else {
1613                row[i] = 1;
1614            }
1615        }
1616        mask.setPixels(0, y, width, row, 0);
1617    }
1618    return mask;
1619}
1620
1621static byte[] checkData(byte [] data) {
1622    if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1623    return data;
1624}
1625
1626/**
1627 * Returns <code>getWidth</code> pixel values starting at offset
1628 * <code>x</code> in scanline <code>y</code> in the receiver's
1629 * data starting at <code>startIndex</code>.
1630 *
1631 * @param x the x position of the first pixel to get
1632 * @param y the y position of the first pixel to get
1633 * @param getWidth the width of the data to get
1634 * @param pixels the buffer in which to put the pixels
1635 * @param startIndex the offset into the byte array to begin storing pixels
1636 *
1637 * @exception IndexOutOfBoundsException if getWidth is too large
1638 * @exception IllegalArgumentException <ul>
1639 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1640 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1641 * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
1642 * </ul>
1643 * @exception SWTException <ul>
1644 * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4 or 8
1645 * (For higher depths, use the int[] version of this method.)</li>
1646 * </ul>
1647 */

1648public void getPixels(int x, int y, int getWidth, byte[] pixels, int startIndex) {
1649    if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1650    if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error
1651
1652(SWT.ERROR_INVALID_ARGUMENT);
1653    if (getWidth == 0) return;
1654    int index;
1655    int theByte;
1656    int mask = 0;
1657    int n = getWidth;
1658    int i = startIndex;
1659    int srcX = x, srcY = y;
1660    if (depth == 1) {
1661        index = (y * bytesPerLine) + (x >> 3);
1662        theByte = data[index] & 0xFF;
1663        while (n > 0) {
1664            mask = 1 << (7 - (srcX & 0x7));
1665            if ((theByte & mask) == 0) {
1666                pixels[i] = 0;
1667            } else {
1668                pixels[i] = 1;
1669            }
1670            i++;
1671            n--;
1672            srcX++;
1673            if (srcX >= width) {
1674                srcY++;
1675                index = srcY * bytesPerLine;
1676                if (n > 0) theByte = data[index] & 0xFF;
1677                srcX = 0;
1678            } else {
1679                if (mask == 1) {
1680                    index++;
1681                    if (n > 0) theByte = data[index] & 0xFF;
1682                }
1683            }
1684        }
1685        return;
1686    }
1687    if (depth == 2) {
1688        index = (y * bytesPerLine) + (x >> 2);
1689        theByte = data[index] & 0xFF;
1690        int offset;
1691        while (n > 0) {
1692            offset = 3 - (srcX % 4);
1693            mask = 3 << (offset * 2);
1694            pixels[i] = (byte)((theByte & mask) >> (offset * 2));
1695            i++;
1696            n--;
1697            srcX++;
1698            if (srcX >= width) {
1699                srcY++;
1700                index = srcY * bytesPerLine;
1701                if (n > 0) theByte = data[index] & 0xFF;
1702                srcX = 0;
1703            } else {
1704                if (offset == 0) {
1705                    index++;
1706                    theByte = data[index] & 0xFF;
1707                }
1708            }
1709        }
1710        return;
1711    }
1712    if (depth == 4) {
1713        index = (y * bytesPerLine) + (x >> 1);
1714        if ((x & 0x1) == 1) {
1715            theByte = data[index] & 0xFF;
1716            pixels[i] = (byte)(theByte & 0x0F);
1717            i++;
1718            n--;
1719            srcX++;
1720            if (srcX >= width) {
1721                srcY++;
1722                index = srcY * bytesPerLine;
1723                srcX = 0;
1724            } else {
1725                index++;
1726            }
1727        }
1728        while (n > 1) {
1729            theByte = data[index] & 0xFF;
1730            pixels[i] = (byte)(theByte >> 4);
1731            i++;
1732            n--;
1733            srcX++;
1734            if (srcX >= width) {
1735                srcY++;
1736                index = srcY * bytesPerLine;
1737                srcX = 0;
1738            } else {
1739                pixels[i] = (byte)(theByte & 0x0F);
1740                i++;
1741                n--;
1742                srcX++;
1743                if (srcX >= width) {
1744                    srcY++;
1745                    index = srcY * bytesPerLine;
1746                    srcX = 0;
1747                } else {
1748                    index++;
1749                }
1750            }
1751        }
1752        if (n > 0) {
1753            theByte = data[index] & 0xFF;
1754            pixels[i] = (byte)(theByte >> 4);
1755        }
1756        return;
1757    }
1758    if (depth == 8) {
1759        index = (y * bytesPerLine) + x;
1760        for (int j = 0; j < getWidth; j++) {
1761            pixels[i] = data[index];
1762            i++;
1763            srcX++;
1764            if (srcX >= width) {
1765                srcY++;
1766                index = srcY * bytesPerLine;
1767                srcX = 0;
1768            } else {
1769                index++;
1770            }
1771        }
1772        return;
1773    }
1774    SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
1775}
1776
1777/**
1778 * Returns <code>getWidth</code> pixel values starting at offset
1779 * <code>x</code> in scanline <code>y</code> in the receiver's
1780 * data starting at <code>startIndex</code>.
1781 *
1782 * @param x the x position of the first pixel to get
1783 * @param y the y position of the first pixel to get
1784 * @param getWidth the width of the data to get
1785 * @param pixels the buffer in which to put the pixels
1786 * @param startIndex the offset into the buffer to begin storing pixels
1787 *
1788 * @exception IndexOutOfBoundsException if getWidth is too large
1789 * @exception IllegalArgumentException <ul>
1790 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1791 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1792 * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
1793 * </ul>
1794 * @exception SWTException <ul>
1795 * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
1796 * </ul>
1797 */

1798public void getPixels(int x, int y, int getWidth, int[] pixels, int startIndex) {
1799    if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1800    if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error
1801
1802(SWT.ERROR_INVALID_ARGUMENT);
1803    if (getWidth == 0) return;
1804    int index;
1805    int theByte;
1806    int mask;
1807    int n = getWidth;
1808    int i = startIndex;
1809    int srcX = x, srcY = y;
1810    if (depth == 1) {
1811        index = (y * bytesPerLine) + (x >> 3);
1812        theByte = data[index] & 0xFF;
1813        while (n > 0) {
1814            mask = 1 << (7 - (srcX & 0x7));
1815            if ((theByte & mask) == 0) {
1816                pixels[i] = 0;
1817            } else {
1818                pixels[i] = 1;
1819            }
1820            i++;
1821            n--;
1822            srcX++;
1823            if (srcX >= width) {
1824                srcY++;
1825                index = srcY * bytesPerLine;
1826                if (n > 0) theByte = data[index] & 0xFF;
1827                srcX = 0;
1828            } else {
1829                if (mask == 1) {
1830                    index++;
1831                    if (n > 0) theByte = data[index] & 0xFF;
1832                }
1833            }
1834        }
1835        return;
1836    }
1837    if (depth == 2) {
1838        index = (y * bytesPerLine) + (x >> 2);
1839        theByte = data[index] & 0xFF;
1840        int offset;
1841        while (n > 0) {
1842            offset = 3 - (srcX % 4);
1843            mask = 3 << (offset * 2);
1844            pixels[i] = (byte)((theByte & mask) >> (offset * 2));
1845            i++;
1846            n--;
1847            srcX++;
1848            if (srcX >= width) {
1849                srcY++;
1850                index = srcY * bytesPerLine;
1851                if (n > 0) theByte = data[index] & 0xFF;
1852                srcX = 0;
1853            } else {
1854                if (offset == 0) {
1855                    index++;
1856                    theByte = data[index] & 0xFF;
1857                }
1858            }
1859        }
1860        return;
1861    }
1862    if (depth == 4) {
1863        index = (y * bytesPerLine) + (x >> 1);
1864        if ((x & 0x1) == 1) {
1865            theByte = data[index] & 0xFF;
1866            pixels[i] = theByte & 0x0F;
1867            i++;
1868            n--;
1869            srcX++;
1870            if (srcX >= width) {
1871                srcY++;
1872                index = srcY * bytesPerLine;
1873                srcX = 0;
1874            } else {
1875                index++;
1876            }
1877        }
1878        while (n > 1) {
1879            theByte = data[index] & 0xFF;
1880            pixels[i] = theByte >> 4;
1881            i++;
1882            n--;
1883            srcX++;
1884            if (srcX >= width) {
1885                srcY++;
1886                index = srcY * bytesPerLine;
1887                srcX = 0;
1888            } else {
1889                pixels[i] = theByte & 0x0F;
1890                i++;
1891                n--;
1892                srcX++;
1893                if (srcX >= width) {
1894                    srcY++;
1895                    index = srcY * bytesPerLine;
1896                    srcX = 0;
1897                } else {
1898                    index++;
1899                }
1900            }
1901        }
1902        if (n > 0) {
1903            theByte = data[index] & 0xFF;
1904            pixels[i] = theByte >> 4;
1905        }
1906        return;
1907    }
1908    if (depth == 8) {
1909        index = (y * bytesPerLine) + x;
1910        for (int j = 0; j < getWidth; j++) {
1911            pixels[i] = data[index] & 0xFF;
1912            i++;
1913            srcX++;
1914            if (srcX >= width) {
1915                srcY++;
1916                index = srcY * bytesPerLine;
1917                srcX = 0;
1918            } else {
1919                index++;
1920            }
1921        }
1922        return;
1923    }
1924    if (depth == 16) {
1925        index = (y * bytesPerLine) + (x * 2);
1926        for (int j = 0; j < getWidth; j++) {
1927            pixels[i] = ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
1928            i++;
1929            srcX++;
1930            if (srcX >= width) {
1931                srcY++;
1932                index = srcY * bytesPerLine;
1933                srcX = 0;
1934            } else {
1935                index += 2;
1936            }
1937        }
1938        return;
1939    }
1940    if (depth == 24) {
1941        index = (y * bytesPerLine) + (x * 3);
1942        for (int j = 0; j < getWidth; j++) {
1943            pixels[i] = ((data[index] & 0xFF) << 16) | ((data[index+1] & 0xFF) << 8)
1944                | (data[index+2] & 0xFF);
1945            i++;
1946            srcX++;
1947            if (srcX >= width) {
1948                srcY++;
1949                index = srcY * bytesPerLine;
1950                srcX = 0;
1951            } else {
1952                index += 3;
1953            }
1954        }
1955        return;
1956    }
1957    if (depth == 32) {
1958        index = (y * bytesPerLine) + (x * 4);
1959        i = startIndex;
1960        for (int j = 0; j < getWidth; j++) {
1961            pixels[i] = ((data[index] & 0xFF) << 24) | ((data[index+1] & 0xFF) << 16)
1962                | ((data[index+2] & 0xFF) << 8) | (data[index+3] & 0xFF);
1963            i++;
1964            srcX++;
1965            if (srcX >= width) {
1966                srcY++;
1967                index = srcY * bytesPerLine;
1968                srcX = 0;
1969            } else {
1970                index += 4;
1971            }
1972        }
1973        return;
1974    }
1975    SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
1976}
1977
1978/**
1979 * Returns an array of <code>RGB</code>s which comprise the
1980 * indexed color table of the receiver, or null if the receiver
1981 * has a direct color model.
1982 *
1983 * @return the RGB values for the image or null if direct color
1984 *
1985 * @see PaletteData#getRGBs()
1986 */

1987public RGB[] getRGBs() {
1988    return palette.getRGBs();
1989}
1990
1991/**
1992 * Returns an <code>ImageData</code> which specifies the
1993 * transparency mask information for the receiver, or null if the
1994 * receiver has no transparency and is not an icon.
1995 *
1996 * @return the transparency mask or null if none exists
1997 */

1998public ImageData getTransparencyMask() {
1999    if (getTransparencyType() == SWT.TRANSPARENCY_MASK) {
2000        return new ImageData(width, height, 1, bwPalette(), maskPad, maskData);
2001    }
2002    return colorMaskImage(transparentPixel);
2003}
2004
2005/**
2006 * Returns the image transparency type.
2007 *
2008 * @return the receiver's transparency type
2009 */

2010public int getTransparencyType() {
2011    if (maskData != null) return SWT.TRANSPARENCY_MASK;
2012    if (transparentPixel != -1) return SWT.TRANSPARENCY_PIXEL;
2013    if (alphaData != null) return SWT.TRANSPARENCY_ALPHA;
2014    return SWT.TRANSPARENCY_NONE;
2015}
2016
2017/**
2018 * Returns the byte order of the receiver.
2019 *
2020 * @return MSB_FIRST or LSB_FIRST
2021 */

2022int getByteOrder() {
2023    return depth != 16 ? MSB_FIRST : LSB_FIRST;
2024}
2025
2026/**
2027 * Sets the pixel values starting at offset <code>x</code> in
2028 * scanline <code>y</code> in the receiver's data to the
2029 * values from the array <code>pixels</code> starting at
2030 * <code>startIndex</code>.
2031 *
2032 * @param x the x position of the pixel to set
2033 * @param y the y position of the pixel to set
2034 * @param putWidth the width of the pixels to set
2035 * @param pixels the pixels to set
2036 * @param startIndex the index at which to begin setting
2037 *
2038 * @exception IndexOutOfBoundsException if putWidth is too large
2039 * @exception IllegalArgumentException <ul>
2040 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
2041 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
2042 * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
2043 * </ul>
2044 * @exception SWTException <ul>
2045 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8
2046 * (For higher depths, use the int[] version of this method.)</li>
2047 * </ul>
2048 */

2049public void setPixels(int x, int y, int putWidth, byte[] pixels, int startIndex) {
2050    if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
2051    if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2052    if (putWidth == 0) return;
2053    int index;
2054    int theByte;
2055    int mask;
2056    int n = putWidth;
2057    int i = startIndex;
2058    int srcX = x, srcY = y;
2059    if (depth == 1) {
2060        index = (y * bytesPerLine) + (x >> 3);
2061        while (n > 0) {
2062            mask = 1 << (7 - (srcX & 0x7));
2063            if ((pixels[i] & 0x1) == 1) {
2064                data[index] = (byte)((data[index] & 0xFF) | mask);
2065            } else {
2066                data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
2067            }
2068            i++;
2069            n--;
2070            srcX++;
2071            if (srcX >= width) {
2072                srcY++;
2073                index = srcY * bytesPerLine;
2074                srcX = 0;
2075            } else {
2076                if (mask == 1) {
2077                    index++;
2078                }
2079            }
2080        }
2081        return;
2082    }
2083    if (depth == 2) {
2084        byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
2085        index = (y * bytesPerLine) + (x >> 2);
2086        int offset = 3 - (x % 4);
2087        while (n > 0) {
2088            theByte = pixels[i] & 0x3;
2089            data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
2090            i++;
2091            n--;
2092            srcX++;
2093            if (srcX >= width) {
2094                srcY++;
2095                index = srcY * bytesPerLine;
2096                offset = 0;
2097                srcX = 0;
2098            } else {
2099                if (offset == 0) {
2100                    index++;
2101                    offset = 3;
2102                } else {
2103                    offset--;
2104                }
2105            }
2106        }
2107        return;
2108    }
2109    if (depth == 4) {
2110        index = (y * bytesPerLine) + (x >> 1);
2111        boolean high = (x & 0x1) == 0;
2112        while (n > 0) {
2113            theByte = pixels[i] & 0x0F;
2114            if (high) {
2115                data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
2116            } else {
2117                data[index] = (byte)((data[index] & 0xF0) | theByte);
2118            }
2119            i++;
2120            n--;
2121            srcX++;
2122            if (srcX >= width) {
2123                srcY++;
2124                index = srcY * bytesPerLine;
2125                high = true;
2126                srcX = 0;
2127            } else {
2128                if (!high) index++;
2129                high = !high;
2130            }
2131        }
2132        return;
2133    }
2134    if (depth == 8) {
2135        index = (y * bytesPerLine) + x;
2136        for (int j = 0; j < putWidth; j++) {
2137            data[index] = (byte)(pixels[i] & 0xFF);
2138            i++;
2139            srcX++;
2140            if (srcX >= width) {
2141                srcY++;
2142                index = srcY * bytesPerLine;
2143                srcX = 0;
2144            } else {
2145                index++;
2146            }
2147        }
2148        return;
2149    }
2150    SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
2151}
2152
2153/**
2154 * Sets the pixel values starting at offset <code>x</code> in
2155 * scanline <code>y</code> in the receiver's data to the
2156 * values from the array <code>pixels</code> starting at
2157 * <code>startIndex</code>.
2158 *
2159 * @param x the x position of the pixel to set
2160 * @param y the y position of the pixel to set
2161 * @param putWidth the width of the pixels to set
2162 * @param pixels the pixels to set
2163 * @param startIndex the index at which to begin setting
2164 *
2165 * @exception IndexOutOfBoundsException if putWidth is too large
2166 * @exception IllegalArgumentException <ul>
2167 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
2168 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
2169 * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
2170 * </ul>
2171 * @exception SWTException <ul>
2172 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
2173 * </ul>
2174 */

2175public void setPixels(int x, int y, int putWidth, int[] pixels, int startIndex) {
2176    if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
2177    if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2178    if (putWidth == 0) return;
2179    int index;
2180    int theByte;
2181    int mask;
2182    int n = putWidth;
2183    int i = startIndex;
2184    int pixel;
2185    int srcX = x, srcY = y;
2186    if (depth == 1) {
2187        index = (y * bytesPerLine) + (x >> 3);
2188        while (n > 0) {
2189            mask = 1 << (7 - (srcX & 0x7));
2190            if ((pixels[i] & 0x1) == 1) {
2191                data[index] = (byte)((data[index] & 0xFF) | mask);
2192            } else {
2193                data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
2194            }
2195            i++;
2196            n--;
2197            srcX++;
2198            if (srcX >= width) {
2199                srcY++;
2200                index = srcY * bytesPerLine;
2201                srcX = 0;
2202            } else {
2203                if (mask == 1) {
2204                    index++;
2205                }
2206            }
2207        }
2208        return;
2209    }
2210    if (depth == 2) {
2211        byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
2212        index = (y * bytesPerLine) + (x >> 2);
2213        int offset = 3 - (x % 4);
2214        while (n > 0) {
2215            theByte = pixels[i] & 0x3;
2216            data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
2217            i++;
2218            n--;
2219            srcX++;
2220            if (srcX >= width) {
2221                srcY++;
2222                index = srcY * bytesPerLine;
2223                offset = 3;
2224                srcX = 0;
2225            } else {
2226                if (offset == 0) {
2227                    index++;
2228                    offset = 3;
2229                } else {
2230                    offset--;
2231                }
2232            }
2233        }
2234        return;
2235    }
2236    if (depth == 4) {
2237        index = (y * bytesPerLine) + (x >> 1);
2238        boolean high = (x & 0x1) == 0;
2239        while (n > 0) {
2240            theByte = pixels[i] & 0x0F;
2241            if (high) {
2242                data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
2243            } else {
2244                data[index] = (byte)((data[index] & 0xF0) | theByte);
2245            }
2246            i++;
2247            n--;
2248            srcX++;
2249            if (srcX >= width) {
2250                srcY++;
2251                index = srcY * bytesPerLine;
2252                high = true;
2253                srcX = 0;
2254            } else {
2255                if (!high) index++;
2256                high = !high;
2257            }
2258        }
2259        return;
2260    }
2261    if (depth == 8) {
2262        index = (y * bytesPerLine) + x;
2263        for (int j = 0; j < putWidth; j++) {
2264            data[index] = (byte)(pixels[i] & 0xFF);
2265            i++;
2266            srcX++;
2267            if (srcX >= width) {
2268                srcY++;
2269                index = srcY * bytesPerLine;
2270                srcX = 0;
2271            } else {
2272                index++;
2273            }
2274        }
2275        return;
2276
2277    }
2278    if (depth == 16) {
2279        index = (y * bytesPerLine) + (x * 2);
2280        for (int j = 0; j < putWidth; j++) {
2281            pixel = pixels[i];
2282            data[index] = (byte)(pixel & 0xFF);
2283            data[index + 1] = (byte)((pixel >> 8) & 0xFF);
2284            i++;
2285            srcX++;
2286            if (srcX >= width) {
2287                srcY++;
2288                index = srcY * bytesPerLine;
2289                srcX = 0;
2290            } else {
2291                index += 2;
2292            }
2293        }
2294        return;
2295    }
2296    if (depth == 24) {
2297        index = (y * bytesPerLine) + (x * 3);
2298        for (int j = 0; j < putWidth; j++) {
2299            pixel = pixels[i];
2300            data[index] = (byte)((pixel >> 16) & 0xFF);
2301            data[index + 1] = (byte)((pixel >> 8) & 0xFF);
2302            data[index + 2] = (byte)(pixel & 0xFF);
2303            i++;
2304            srcX++;
2305            if (srcX >= width) {
2306                srcY++;
2307                index = srcY * bytesPerLine;
2308                srcX = 0;
2309            } else {
2310                index += 3;
2311            }
2312        }
2313        return;
2314    }
2315    if (depth == 32) {
2316        index = (y * bytesPerLine) + (x * 4);
2317        for (int j = 0; j < putWidth; j++) {
2318            pixel = pixels[i];
2319            data[index] = (byte)((pixel >> 24) & 0xFF);
2320            data[index + 1] = (byte)((pixel >> 16) & 0xFF);
2321            data[index + 2] = (byte)((pixel >> 8) & 0xFF);
2322            data[index + 3] = (byte)(pixel & 0xFF);
2323            i++;
2324            srcX++;
2325            if (srcX >= width) {
2326                srcY++;
2327                index = srcY * bytesPerLine;
2328                srcX = 0;
2329            } else {
2330                index += 4;
2331            }
2332        }
2333        return;
2334    }
2335    SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
2336}
2337
2338/**
2339 * Returns a palette with 2 colors: black & white.
2340 */

2341static PaletteData bwPalette() {
2342    return new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255, 255, 255)});
2343}
2344
2345/**
2346 * Gets the offset of the most significant bit for
2347 * the given mask.
2348 */

2349static int getMSBOffset(int mask) {
2350    for (int i = 31; i >= 0; i--) {
2351        if (((mask >> i) & 0x1) != 0) return i + 1;
2352    }
2353    return 0;
2354}
2355
2356/**
2357 * Finds the closest match.
2358 */

2359static int closestMatch(int depth, byte red, byte green, byte blue, int redMask, int greenMask, int blueMask, byte[] reds, byte[] greens, byte[] blues) {
2360    if (depth > 8) {
2361        int rshift = 32 - getMSBOffset(redMask);
2362        int gshift = 32 - getMSBOffset(greenMask);
2363        int bshift = 32 - getMSBOffset(blueMask);
2364        return (((red << 24) >>> rshift) & redMask) |
2365            (((green << 24) >>> gshift) & greenMask) |
2366            (((blue << 24) >>> bshift) & blueMask);
2367    }
2368    int r, g, b;
2369    int minDistance = 0x7fffffff;
2370    int nearestPixel = 0;
2371    int n = reds.length;
2372    for (int j = 0; j < n; j++) {
2373        r = (reds[j] & 0xFF) - (red & 0xFF);
2374        g = (greens[j] & 0xFF) - (green & 0xFF);
2375        b = (blues[j] & 0xFF) - (blue & 0xFF);
2376        int distance = r*r + g*g + b*b;
2377        if (distance < minDistance) {
2378            nearestPixel = j;
2379            if (distance == 0) break;
2380            minDistance = distance;
2381        }
2382    }
2383    return nearestPixel;
2384}
2385
2386static final ImageData convertMask(ImageData mask) {
2387    if (mask.depth == 1) return mask;
2388    PaletteData palette = new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255,255,255)});
2389    ImageData newMask = new ImageData(mask.width, mask.height, 1, palette);
2390    /* Find index of black in mask palette */
2391    int blackIndex = 0;
2392    RGB[] rgbs = mask.getRGBs();
2393    if (rgbs != null) {
2394        while (blackIndex < rgbs.length) {
2395            if (rgbs[blackIndex].equals(palette.colors[0])) break;
2396            blackIndex++;
2397        }
2398    }
2399    int[] pixels = new int[mask.width];
2400    for (int y = 0; y < mask.height; y++) {
2401        mask.getPixels(0, y, mask.width, pixels, 0);
2402        for (int i = 0; i < pixels.length; i++) {
2403            if (pixels[i] == blackIndex) {
2404                pixels[i] = 0;
2405            } else {
2406                pixels[i] = 1;
2407            }
2408        }
2409        newMask.setPixels(0, y, mask.width, pixels, 0);
2410    }
2411    return newMask;
2412}
2413
2414static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
2415    if (pad == newPad) return data;
2416    int stride = (width * depth + 7) / 8;
2417    int bpl = (stride + (pad - 1)) / pad * pad;
2418    int newBpl = (stride + (newPad - 1)) / newPad * newPad;
2419    byte[] newData = new byte[height * newBpl];
2420    int srcIndex = 0, destIndex = 0;
2421    for (int y = 0; y < height; y++) {
2422        System.arraycopy(data, srcIndex, newData, destIndex, stride);
2423        srcIndex += bpl;
2424        destIndex += newBpl;
2425    }
2426    return newData;
2427}
2428
2429/**
2430 * Blit operation bits to be OR'ed together to specify the desired operation.
2431 */

2432static final int
2433    BLIT_SRC = 1, // copy source directly, else applies logic operations
2434
BLIT_ALPHA = 2, // enable alpha blending
2435
BLIT_DITHER = 4; // enable dithering in low color modes
2436

2437/**
2438 * Alpha mode, values 0 - 255 specify global alpha level
2439 */

2440static final int
2441    ALPHA_OPAQUE = 255, // Fully opaque (ignores any alpha data)
2442
ALPHA_TRANSPARENT = 0, // Fully transparent (ignores any alpha data)
2443
ALPHA_CHANNEL_SEPARATE = -1, // Use alpha channel from separate alphaData
2444
ALPHA_CHANNEL_SOURCE = -2, // Use alpha channel embedded in sourceData
2445
ALPHA_MASK_UNPACKED = -3, // Use transparency mask formed by bytes in alphaData (non-zero is opaque)
2446
ALPHA_MASK_PACKED = -4, // Use transparency mask formed by packed bits in alphaData
2447
ALPHA_MASK_INDEX = -5, // Consider source palette indices transparent if in alphaData array
2448
ALPHA_MASK_RGB = -6; // Consider source RGBs transparent if in RGB888 format alphaData array
2449

2450/**
2451 * Byte and bit order constants.
2452 */

2453static final int LSB_FIRST = 0;
2454static final int MSB_FIRST = 1;
2455
2456/**
2457 * Data types (internal)
2458 */

2459/*
2460private static final int
2461    // direct / true color formats with arbitrary masks & shifts
2462    TYPE_GENERIC_8 = 0,
2463    TYPE_GENERIC_16_MSB = 1,
2464    TYPE_GENERIC_16_LSB = 2,
2465    TYPE_GENERIC_24 = 3,
2466    TYPE_GENERIC_32_MSB = 4,
2467    TYPE_GENERIC_32_LSB = 5,
2468    // palette indexed color formats
2469    TYPE_INDEX_8 = 6,
2470    TYPE_INDEX_4 = 7,
2471    TYPE_INDEX_2 = 8,
2472    TYPE_INDEX_1_MSB = 9,
2473    TYPE_INDEX_1_LSB = 10;
2474*/

2475/**
2476 * Computes the required channel shift from a mask.
2477 */

2478static int getChannelShift(int mask) {
2479    if (mask == 0) return 0;
2480    int i;
2481    for (i = 0; ((mask & 1) == 0) && (i < 32); ++i) {
2482        mask >>>= 1;
2483    }
2484    return i;
2485}
2486
2487/**
2488 * Computes the required channel width (depth) from a mask.
2489 */

2490static int getChannelWidth(int mask, int shift) {
2491    if (mask == 0) return 0;
2492    int i;
2493    mask >>>= shift;
2494    for (i = shift; ((mask & 1) != 0) && (i < 32); ++i) {
2495        mask >>>= 1;
2496    }
2497    return i - shift;
2498}
2499
2500/**
2501 * Extracts a field from packed RGB data given a mask for that field.
2502 */

2503static byte getChannelField(int data, int mask) {
2504    final int shift = getChannelShift(mask);
2505    return ANY_TO_EIGHT[getChannelWidth(mask, shift)][(data & mask) >>> shift];
2506}
2507
2508/*
2509 * Fill in dithered gradated values for a color channel
2510 */

2511static final void buildDitheredGradientChannel(int from, int to, int steps,
2512    int bandWidth, int bandHeight, boolean vertical,
2513    byte[] bitmapData, int dp, int bytesPerLine, int bits) {
2514    final int mask = 0xff00 >>> bits;
2515    int val = from << 16;
2516    final int inc = ((to << 16) - val) / steps + 1;
2517    if (vertical) {
2518        for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
2519            for (int dx = 0, dptr = dp; dx < bandWidth; ++dx, dptr += 4) {
2520                final int thresh = DITHER_MATRIX[dy & 7][dx] >>> bits;
2521                int temp = val + thresh;
2522                if (temp > 0xffffff) bitmapData[dptr] = -1;
2523                else bitmapData[dptr] = (byte)((temp >>> 16) & mask);
2524            }
2525            val += inc;
2526        }
2527    } else {
2528        for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
2529            for (int dy = 0, dptr = dp; dy < bandHeight; ++dy, dptr += bytesPerLine) {
2530                final int thresh = DITHER_MATRIX[dy][dx & 7] >>> bits;
2531                int temp = val + thresh;
2532                if (temp > 0xffffff) bitmapData[dptr] = -1;
2533                else bitmapData[dptr] = (byte)((temp >>> 16) & mask);
2534            }
2535            val += inc;
2536        }
2537    }
2538}
2539}
2540
2541static class LEDataInputStream extends InputStream {
2542    int position;
2543    InputStream in;
2544
2545    /**
2546     * The byte array containing the bytes to read.
2547     */

2548    protected byte[] buf;
2549    
2550    /**
2551     * The current position within the byte array <code>buf</code>. A value
2552     * equal to buf.length indicates no bytes available. A value of
2553     * 0 indicates the buffer is full.
2554     */

2555    protected int pos;
2556    
2557
2558    public LEDataInputStream(InputStream input) {
2559        this(input, 512);
2560    }
2561    
2562    public LEDataInputStream(InputStream input, int bufferSize) {
2563        this.in = input;
2564        if (bufferSize > 0) {
2565            buf = new byte[bufferSize];
2566            pos = bufferSize;
2567        }
2568        else throw new IllegalArgumentException JavaDoc();
2569    }
2570    
2571    public void close() throws IOException {
2572        buf = null;
2573        if (in != null) {
2574            in.close();
2575            in = null;
2576        }
2577    }
2578    
2579    /**
2580     * Answer how many bytes were read.
2581     */

2582    public int getPosition() {
2583        return position;
2584    }
2585    
2586    /**
2587     * Answers how many bytes are available for reading without blocking
2588     */

2589    public int available() throws IOException {
2590        if (buf == null) throw new IOException();
2591        return (buf.length - pos) + in.available();
2592    }
2593    
2594    /**
2595     * Answer the next byte of the input stream.
2596     */

2597    public int read() throws IOException {
2598        if (buf == null) throw new IOException();
2599        position++;
2600        if (pos < buf.length) return (buf[pos++] & 0xFF);
2601        return in.read();
2602    }
2603    
2604    /**
2605     * Don't imitate the JDK behaviour of reading a random number
2606     * of bytes when you can actually read them all.
2607     */

2608    public int read(byte b[], int off, int len) throws IOException {
2609        int result;
2610        int left = len;
2611        result = readData(b, off, len);
2612        while (true) {
2613            if (result == -1) return -1;
2614            position += result;
2615            if (result == left) return len;
2616            left -= result;
2617            off += result;
2618            result = readData(b, off, left);
2619        }
2620    }
2621    
2622    /**
2623     * Reads at most <code>length</code> bytes from this LEDataInputStream and
2624     * stores them in byte array <code>buffer</code> starting at <code>offset</code>.
2625     * <p>
2626     * Answer the number of bytes actually read or -1 if no bytes were read and
2627     * end of stream was encountered. This implementation reads bytes from
2628     * the pushback buffer first, then the target stream if more bytes are required
2629     * to satisfy <code>count</code>.
2630     * </p>
2631     * @param buffer the byte array in which to store the read bytes.
2632     * @param offset the offset in <code>buffer</code> to store the read bytes.
2633     * @param length the maximum number of bytes to store in <code>buffer</code>.
2634     *
2635     * @return int the number of bytes actually read or -1 if end of stream.
2636     *
2637     * @exception java.io.IOException if an IOException occurs.
2638     */

2639    private int readData(byte[] buffer, int offset, int length) throws IOException {
2640        if (buf == null) throw new IOException();
2641        if (offset < 0 || offset > buffer.length ||
2642            length < 0 || (length > buffer.length - offset)) {
2643            throw new ArrayIndexOutOfBoundsException JavaDoc();
2644            }
2645                
2646        int cacheCopied = 0;
2647        int newOffset = offset;
2648    
2649        // Are there pushback bytes available?
2650
int available = buf.length - pos;
2651        if (available > 0) {
2652            cacheCopied = (available >= length) ? length : available;
2653            System.arraycopy(buf, pos, buffer, newOffset, cacheCopied);
2654            newOffset += cacheCopied;
2655            pos += cacheCopied;
2656        }
2657    
2658        // Have we copied enough?
2659
if (cacheCopied == length) return length;
2660
2661        int inCopied = in.read(buffer, newOffset, length - cacheCopied);
2662
2663        if (inCopied > 0) return inCopied + cacheCopied;
2664        if (cacheCopied == 0) return inCopied;
2665        return cacheCopied;
2666    }
2667    
2668    /**
2669     * Answer an integer comprised of the next
2670     * four bytes of the input stream.
2671     */

2672    public int readInt() throws IOException {
2673        byte[] buf = new byte[4];
2674        read(buf);
2675        return ((((((buf[3] & 0xFF) << 8) |
2676            (buf[2] & 0xFF)) << 8) |
2677            (buf[1] & 0xFF)) << 8) |
2678            (buf[0] & 0xFF);
2679    }
2680    
2681    /**
2682     * Answer a short comprised of the next
2683     * two bytes of the input stream.
2684     */

2685    public short readShort() throws IOException {
2686        byte[] buf = new byte[2];
2687        read(buf);
2688        return (short)(((buf[1] & 0xFF) << 8) | (buf[0] & 0xFF));
2689    }
2690    
2691    /**
2692     * Push back the entire content of the given buffer <code>b</code>.
2693     * <p>
2694     * The bytes are pushed so that they would be read back b[0], b[1], etc.
2695     * If the push back buffer cannot handle the bytes copied from <code>b</code>,
2696     * an IOException will be thrown and no byte will be pushed back.
2697     * </p>
2698     *
2699     * @param b the byte array containing bytes to push back into the stream
2700     *
2701     * @exception java.io.IOException if the pushback buffer is too small
2702     */

2703    public void unread(byte[] b) throws IOException {
2704        int length = b.length;
2705        if (length > pos) throw new IOException();
2706        position -= length;
2707        pos -= length;
2708        System.arraycopy(b, 0, buf, pos, length);
2709    }
2710}
2711public static abstract class FileFormat {
2712    LEDataInputStream inputStream;
2713    ImageLoader loader;
2714    int compression;
2715
2716byte[] bitInvertData(byte[] data, int startIndex, int endIndex) {
2717    // Destructively bit invert data in the given byte array.
2718
for (int i = startIndex; i < endIndex; i++) {
2719        data[i] = (byte)(255 - data[i - startIndex]);
2720    }
2721    return data;
2722}
2723
2724/**
2725 * Return whether or not the specified input stream
2726 * represents a supported file format.
2727 */

2728abstract boolean isFileFormat(LEDataInputStream stream);
2729
2730abstract ImageData[] loadFromByteStream();
2731
2732public ImageData[] loadFromStream(LEDataInputStream stream) {
2733    try {
2734        inputStream = stream;
2735        return loadFromByteStream();
2736    } catch (Exception JavaDoc e) {
2737        SWT.error(SWT.ERROR_IO, e);
2738        return null;
2739    }
2740}
2741
2742public static ImageData[] load(InputStream is, ImageLoader loader) {
2743    LEDataInputStream stream = new LEDataInputStream(is);
2744    boolean isSupported = false;
2745    FileFormat fileFormat = new WinICOFileFormat();
2746    if (fileFormat.isFileFormat(stream)) isSupported = true;
2747    else {
2748        fileFormat = new WinBMPFileFormat();
2749        if (fileFormat.isFileFormat(stream)) isSupported = true;
2750    }
2751    if (!isSupported) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
2752    fileFormat.loader = loader;
2753    return fileFormat.loadFromStream(stream);
2754}
2755}
2756static class WinBMPFileFormat extends FileFormat {
2757    static final int BMPFileHeaderSize = 14;
2758    static final int BMPHeaderFixedSize = 40;
2759    int importantColors;
2760
2761void decompressData(byte[] src, byte[] dest, int stride, int cmp) {
2762    if (cmp == 1) { // BMP_RLE8_COMPRESSION
2763
if (decompressRLE8Data(src, src.length, stride, dest, dest.length) <= 0)
2764            SWT.error(SWT.ERROR_INVALID_IMAGE);
2765        return;
2766    }
2767    if (cmp == 2) { // BMP_RLE4_COMPRESSION
2768
if (decompressRLE4Data(src, src.length, stride, dest, dest.length) <= 0)
2769            SWT.error(SWT.ERROR_INVALID_IMAGE);
2770        return;
2771    }
2772    SWT.error(SWT.ERROR_INVALID_IMAGE);
2773}
2774int decompressRLE4Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) {
2775    int sp = 0;
2776    int se = numBytes;
2777    int dp = 0;
2778    int de = destSize;
2779    int x = 0, y = 0;
2780    while (sp < se) {
2781        int len = src[sp] & 0xFF;
2782        sp++;
2783        if (len == 0) {
2784            len = src[sp] & 0xFF;
2785            sp++;
2786            switch (len) {
2787                case 0: /* end of line */
2788                    y++;
2789                    x = 0;
2790                    dp = y * stride;
2791                    if (dp >= de)
2792                        return -1;
2793                    break;
2794                case 1: /* end of bitmap */
2795                    return 1;
2796                case 2: /* delta */
2797                    x += src[sp] & 0xFF;
2798                    sp++;
2799                    y += src[sp] & 0xFF;
2800                    sp++;
2801                    dp = y * stride + x / 2;
2802                    if (dp >= de)
2803                        return -1;
2804                    break;
2805                default: /* absolute mode run */
2806                    if ((len & 1) != 0) /* odd run lengths not currently supported */
2807                        return -1;
2808                    x += len;
2809                    len = len / 2;
2810                    if (len > (se - sp))
2811                        return -1;
2812                    if (len > (de - dp))
2813                        return -1;
2814                    for (int i = 0; i < len; i++) {
2815                        dest[dp] = src[sp];
2816                        dp++;
2817                        sp++;
2818                    }
2819                    if ((sp & 1) != 0)
2820                        sp++; /* word align sp? */
2821                    break;
2822            }
2823        } else {
2824            if ((len & 1) != 0)
2825                return -1;
2826            x += len;
2827            len = len / 2;
2828            byte theByte = src[sp];
2829            sp++;
2830            if (len > (de - dp))
2831                return -1;
2832            for (int i = 0; i < len; i++) {
2833                dest[dp] = theByte;
2834                dp++;
2835            }
2836        }
2837    }
2838    return 1;
2839}
2840int decompressRLE8Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) {
2841    int sp = 0;
2842    int se = numBytes;
2843    int dp = 0;
2844    int de = destSize;
2845    int x = 0, y = 0;
2846    while (sp < se) {
2847        int len = src[sp] & 0xFF;
2848        sp++;
2849        if (len == 0) {
2850            len = src[sp] & 0xFF;
2851            sp++;
2852            switch (len) {
2853                case 0: /* end of line */
2854                    y++;
2855                    x = 0;
2856                    dp = y * stride;
2857                    if (dp >= de)
2858                        return -1;
2859                    break;
2860                case 1: /* end of bitmap */
2861                    return 1;
2862                case 2: /* delta */
2863                    x += src[sp] & 0xFF;
2864                    sp++;
2865                    y += src[sp] & 0xFF;
2866                    sp++;
2867                    dp = y * stride + x;
2868                    if (dp >= de)
2869                        return -1;
2870                    break;
2871                default: /* absolute mode run */
2872                    if (len > (se - sp))
2873                        return -1;
2874                    if (len > (de - dp))
2875                        return -1;
2876                    for (int i = 0; i < len; i++) {
2877                        dest[dp] = src[sp];
2878                        dp++;
2879                        sp++;
2880                    }
2881                    if ((sp & 1) != 0)
2882                        sp++; /* word align sp? */
2883                    x += len;
2884                    break;
2885            }
2886        } else {
2887            byte theByte = src[sp];
2888            sp++;
2889            if (len > (de - dp))
2890                return -1;
2891            for (int i = 0; i < len; i++) {
2892                dest[dp] = theByte;
2893                dp++;
2894            }
2895            x += len;
2896        }
2897    }
2898    return 1;
2899}
2900boolean isFileFormat(LEDataInputStream stream) {
2901    try {
2902        byte[] header = new byte[18];
2903        stream.read(header);
2904        stream.unread(header);
2905        int infoHeaderSize = (header[14] & 0xFF) | ((header[15] & 0xFF) << 8) | ((header[16] & 0xFF) << 16) | ((header[17] & 0xFF) << 24);
2906        return header[0] == 0x42 && header[1] == 0x4D && infoHeaderSize >= BMPHeaderFixedSize;
2907    } catch (Exception JavaDoc e) {
2908        return false;
2909    }
2910}
2911byte[] loadData(byte[] infoHeader) {
2912    int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24);
2913    int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
2914    int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
2915    int stride = (width * bitCount + 7) / 8;
2916    stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
2917
byte[] data = loadData(infoHeader, stride);
2918    flipScanLines(data, stride, height);
2919    return data;
2920}
2921byte[] loadData(byte[] infoHeader, int stride) {
2922    int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
2923    int dataSize = height * stride;
2924    byte[] data = new byte[dataSize];
2925    int cmp = (infoHeader[16] & 0xFF) | ((infoHeader[17] & 0xFF) << 8) | ((infoHeader[18] & 0xFF) << 16) | ((infoHeader[19] & 0xFF) << 24);
2926    if (cmp == 0) { // BMP_NO_COMPRESSION
2927
try {
2928            if (inputStream.read(data) != dataSize)
2929                SWT.error(SWT.ERROR_INVALID_IMAGE);
2930        } catch (IOException e) {
2931            SWT.error(SWT.ERROR_IO, e);
2932        }
2933    } else {
2934        int compressedSize = (infoHeader[20] & 0xFF) | ((infoHeader[21] & 0xFF) << 8) | ((infoHeader[22] & 0xFF) << 16) | ((infoHeader[23] & 0xFF) << 24);
2935        byte[] compressed = new byte[compressedSize];
2936        try {
2937            if (inputStream.read(compressed) != compressedSize)
2938                SWT.error(SWT.ERROR_INVALID_IMAGE);
2939        } catch (IOException e) {
2940            SWT.error(SWT.ERROR_IO, e);
2941        }
2942        decompressData(compressed, data, stride, cmp);
2943    }
2944    return data;
2945}
2946int[] loadFileHeader() {
2947    int[] header = new int[5];
2948    try {
2949        header[0] = inputStream.readShort();
2950        header[1] = inputStream.readInt();
2951        header[2] = inputStream.readShort();
2952        header[3] = inputStream.readShort();
2953        header[4] = inputStream.readInt();
2954    } catch (IOException e) {
2955        SWT.error(SWT.ERROR_IO, e);
2956    }
2957    if (header[0] != 0x4D42)
2958        SWT.error(SWT.ERROR_INVALID_IMAGE);
2959    return header;
2960}
2961ImageData[] loadFromByteStream() {
2962    int[] fileHeader = loadFileHeader();
2963    byte[] infoHeader = new byte[BMPHeaderFixedSize];
2964    try {
2965        inputStream.read(infoHeader);
2966    } catch (Exception JavaDoc e) {
2967        SWT.error(SWT.ERROR_IO, e);
2968    }
2969    int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24);
2970    int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
2971    int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
2972    PaletteData palette = loadPalette(infoHeader);
2973    if (inputStream.getPosition() < fileHeader[4]) {
2974        // Seek to the specified offset
2975
try {
2976            inputStream.skip(fileHeader[4] - inputStream.getPosition());
2977        } catch (IOException e) {
2978            SWT.error(SWT.ERROR_IO, e);
2979        }
2980    }
2981    byte[] data = loadData(infoHeader);
2982    this.compression = (infoHeader[16] & 0xFF) | ((infoHeader[17] & 0xFF) << 8) | ((infoHeader[18] & 0xFF) << 16) | ((infoHeader[19] & 0xFF) << 24);
2983    this.importantColors = (infoHeader[36] & 0xFF) | ((infoHeader[37] & 0xFF) << 8) | ((infoHeader[38] & 0xFF) << 16) | ((infoHeader[39] & 0xFF) << 24);
2984// int xPelsPerMeter = (infoHeader[24] & 0xFF) | ((infoHeader[25] & 0xFF) << 8) | ((infoHeader[26] & 0xFF) << 16) | ((infoHeader[27] & 0xFF) << 24);
2985
// int yPelsPerMeter = (infoHeader[28] & 0xFF) | ((infoHeader[29] & 0xFF) << 8) | ((infoHeader[30] & 0xFF) << 16) | ((infoHeader[31] & 0xFF) << 24);
2986
int type = (this.compression == 1 /*BMP_RLE8_COMPRESSION*/) || (this.compression == 2 /*BMP_RLE4_COMPRESSION*/) ? SWT.IMAGE_BMP_RLE : SWT.IMAGE_BMP;
2987    return new ImageData[] {
2988        ImageData.internal_new(
2989            width,
2990            height,
2991            bitCount,
2992            palette,
2993            4,
2994            data,
2995            0,
2996            null,
2997            null,
2998            -1,
2999            -1,
3000            type,
3001            0,
3002            0,
3003            0,
3004            0)
3005    };
3006}
3007PaletteData loadPalette(byte[] infoHeader) {
3008    int depth = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
3009    if (depth <= 8) {
3010        int numColors = (infoHeader[32] & 0xFF) | ((infoHeader[33] & 0xFF) << 8) | ((infoHeader[34] & 0xFF) << 16) | ((infoHeader[35] & 0xFF) << 24);
3011        if (numColors == 0) {
3012            numColors = 1 << depth;
3013        } else {
3014            if (numColors > 256)
3015                numColors = 256;
3016        }
3017        byte[] buf = new byte[numColors * 4];
3018        try {
3019            if (inputStream.read(buf) != buf.length)
3020                SWT.error(SWT.ERROR_INVALID_IMAGE);
3021        } catch (IOException e) {
3022            SWT.error(SWT.ERROR_IO, e);
3023        }
3024        return paletteFromBytes(buf, numColors);
3025    }
3026    if (depth == 16) return new PaletteData(0x7C00, 0x3E0, 0x1F);
3027    if (depth == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000);
3028    return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
3029}
3030PaletteData paletteFromBytes(byte[] bytes, int numColors) {
3031    int bytesOffset = 0;
3032    RGB[] colors = new RGB[numColors];
3033    for (int i = 0; i < numColors; i++) {
3034        colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
3035            bytes[bytesOffset + 1] & 0xFF,
3036            bytes[bytesOffset] & 0xFF);
3037        bytesOffset += 4;
3038    }
3039    return new PaletteData(colors);
3040}
3041/**
3042 * Answer a byte array containing the BMP representation of
3043 * the given device independent palette.
3044 */

3045static byte[] paletteToBytes(PaletteData pal) {
3046    int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256);
3047    byte[] bytes = new byte[n * 4];
3048    int offset = 0;
3049    for (int i = 0; i < n; i++) {
3050        RGB col = pal.colors[i];
3051        bytes[offset] = (byte)col.blue;
3052        bytes[offset + 1] = (byte)col.green;
3053        bytes[offset + 2] = (byte)col.red;
3054        offset += 4;
3055    }
3056    return bytes;
3057}
3058
3059void flipScanLines(byte[] data, int stride, int height) {
3060    int i1 = 0;
3061    int i2 = (height - 1) * stride;
3062    for (int i = 0; i < height / 2; i++) {
3063        for (int index = 0; index < stride; index++) {
3064            byte b = data[index + i1];
3065            data[index + i1] = data[index + i2];
3066            data[index + i2] = b;
3067        }
3068        i1 += stride;
3069        i2 -= stride;
3070    }
3071}
3072
3073}
3074
3075static class WinICOFileFormat extends FileFormat {
3076    
3077static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
3078    if (pad == newPad) return data;
3079    int stride = (width * depth + 7) / 8;
3080    int bpl = (stride + (pad - 1)) / pad * pad;
3081    int newBpl = (stride + (newPad - 1)) / newPad * newPad;
3082    byte[] newData = new byte[height * newBpl];
3083    int srcIndex = 0, destIndex = 0;
3084    for (int y = 0; y < height; y++) {
3085        System.arraycopy(data, srcIndex, newData, destIndex, newBpl);
3086        srcIndex += bpl;
3087        destIndex += newBpl;
3088    }
3089    return newData;
3090}
3091/**
3092 * Answer the size in bytes of the file representation of the given
3093 * icon
3094 */

3095int iconSize(ImageData i) {
3096    int shapeDataStride = (i.width * i.depth + 31) / 32 * 4;
3097    int maskDataStride = (i.width + 31) / 32 * 4;
3098    int dataSize = (shapeDataStride + maskDataStride) * i.height;
3099    int paletteSize = i.palette.colors != null ? i.palette.colors.length * 4 : 0;
3100    return WinBMPFileFormat.BMPHeaderFixedSize + paletteSize + dataSize;
3101}
3102boolean isFileFormat(LEDataInputStream stream) {
3103    try {
3104        byte[] header = new byte[4];
3105        stream.read(header);
3106        stream.unread(header);
3107        return header[0] == 0 && header[1] == 0 && header[2] == 1 && header[3] == 0;
3108    } catch (Exception JavaDoc e) {
3109        return false;
3110    }
3111}
3112boolean isValidIcon(ImageData i) {
3113    switch (i.depth) {
3114        case 1:
3115        case 4:
3116        case 8:
3117            if (i.palette.isDirect) return false;
3118            int size = i.palette.colors.length;
3119            return size == 2 || size == 16 || size == 32 || size == 256;
3120        case 24:
3121        case 32:
3122            return i.palette.isDirect;
3123    }
3124    return false;
3125}
3126int loadFileHeader(LEDataInputStream byteStream) {
3127    int[] fileHeader = new int[3];
3128    try {
3129        fileHeader[0] = byteStream.readShort();
3130        fileHeader[1] = byteStream.readShort();
3131        fileHeader[2] = byteStream.readShort();
3132    } catch (IOException e) {
3133        SWT.error(SWT.ERROR_IO, e);
3134    }
3135    if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
3136        SWT.error(SWT.ERROR_INVALID_IMAGE);
3137    int numIcons = fileHeader[2];
3138    if (numIcons <= 0)
3139        SWT.error(SWT.ERROR_INVALID_IMAGE);
3140    return numIcons;
3141}
3142int loadFileHeader(LEDataInputStream byteStream, boolean hasHeader) {
3143    int[] fileHeader = new int[3];
3144    try {
3145        if (hasHeader) {
3146            fileHeader[0] = byteStream.readShort();
3147            fileHeader[1] = byteStream.readShort();
3148        } else {
3149            fileHeader[0] = 0;
3150            fileHeader[1] = 1;
3151        }
3152        fileHeader[2] = byteStream.readShort();
3153    } catch (IOException e) {
3154        SWT.error(SWT.ERROR_IO, e);
3155    }
3156    if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
3157        SWT.error(SWT.ERROR_INVALID_IMAGE);
3158    int numIcons = fileHeader[2];
3159    if (numIcons <= 0)
3160        SWT.error(SWT.ERROR_INVALID_IMAGE);
3161    return numIcons;
3162}
3163ImageData[] loadFromByteStream() {
3164    int numIcons = loadFileHeader(inputStream);
3165    int[][] headers = loadIconHeaders(numIcons);
3166    ImageData[] icons = new ImageData[headers.length];
3167    for (int i = 0; i < icons.length; i++) {
3168        icons[i] = loadIcon(headers[i]);
3169    }
3170    return icons;
3171}
3172/**
3173 * Load one icon from the byte stream.
3174 */

3175ImageData loadIcon(int[] iconHeader) {
3176    byte[] infoHeader = loadInfoHeader(iconHeader);
3177    WinBMPFileFormat bmpFormat = new WinBMPFileFormat();
3178    bmpFormat.inputStream = inputStream;
3179    PaletteData palette = bmpFormat.loadPalette(infoHeader);
3180    byte[] shapeData = bmpFormat.loadData(infoHeader);
3181    int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24);
3182    int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
3183    int depth = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
3184    infoHeader[14] = 1;
3185    infoHeader[15] = 0;
3186    byte[] maskData = bmpFormat.loadData(infoHeader);
3187    maskData = convertPad(maskData, width, height, 1, 4, 2);
3188    bitInvertData(maskData, 0, maskData.length);
3189    return ImageData.internal_new(
3190        width,
3191        height,
3192        depth,
3193        palette,
3194        4,
3195        shapeData,
3196        2,
3197        maskData,
3198        null,
3199        -1,
3200        -1,
3201        SWT.IMAGE_ICO,
3202        0,
3203        0,
3204        0,
3205        0);
3206}
3207int[][] loadIconHeaders(int numIcons) {
3208    int[][] headers = new int[numIcons][7];
3209    try {
3210        for (int i = 0; i < numIcons; i++) {
3211            headers[i][0] = inputStream.read();
3212            headers[i][1] = inputStream.read();
3213            headers[i][2] = inputStream.readShort();
3214            headers[i][3] = inputStream.readShort();
3215            headers[i][4] = inputStream.readShort();
3216            headers[i][5] = inputStream.readInt();
3217            headers[i][6] = inputStream.readInt();
3218        }
3219    } catch (IOException e) {
3220        SWT.error(SWT.ERROR_IO, e);
3221    }
3222    return headers;
3223}
3224byte[] loadInfoHeader(int[] iconHeader) {
3225    int width = iconHeader[0];
3226    int height = iconHeader[1];
3227    int numColors = iconHeader[2]; // the number of colors is in the low byte, but the high byte must be 0
3228
if (numColors == 0) numColors = 256; // this is specified: '00' represents '256' (0x100) colors
3229
if ((numColors != 2) && (numColors != 8) && (numColors != 16) &&
3230        (numColors != 32) && (numColors != 256))
3231        SWT.error(SWT.ERROR_INVALID_IMAGE);
3232    if (inputStream.getPosition() < iconHeader[6]) {
3233        // Seek to the specified offset
3234
try {
3235            inputStream.skip(iconHeader[6] - inputStream.getPosition());
3236        } catch (IOException e) {
3237            SWT.error(SWT.ERROR_IO, e);
3238            return null;
3239        }
3240    }
3241    byte[] infoHeader = new byte[WinBMPFileFormat.BMPHeaderFixedSize];
3242    try {
3243        inputStream.read(infoHeader);
3244    } catch (IOException e) {
3245        SWT.error(SWT.ERROR_IO, e);
3246    }
3247    if (((infoHeader[12] & 0xFF) | ((infoHeader[13] & 0xFF) << 8)) != 1)
3248        SWT.error(SWT.ERROR_INVALID_IMAGE);
3249    int infoWidth = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24);
3250    int infoHeight = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
3251    int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
3252    if (height == infoHeight && bitCount == 1) height /= 2;
3253    if (!((width == infoWidth) && (height * 2 == infoHeight) &&
3254        (bitCount == 1 || bitCount == 4 || bitCount == 8 || bitCount == 24 || bitCount == 32)))
3255            SWT.error(SWT.ERROR_INVALID_IMAGE);
3256    infoHeader[8] = (byte)(height & 0xFF);
3257    infoHeader[9] = (byte)((height >> 8) & 0xFF);
3258    infoHeader[10] = (byte)((height >> 16) & 0xFF);
3259    infoHeader[11] = (byte)((height >> 24) & 0xFF);
3260    return infoHeader;
3261}
3262}
3263static class SWT {
3264    public static final int IMAGE_ICO = 3;
3265    public static final int ERROR_IO = 39;
3266    public static final int ERROR_INVALID_IMAGE = 40;
3267    public static final int ERROR_NULL_ARGUMENT = 4;
3268    public static final int ERROR_INVALID_ARGUMENT = 5;
3269    public static final int ERROR_CANNOT_BE_ZERO = 7;
3270    public static final int IMAGE_UNDEFINED = -1;
3271    public static final int ERROR_UNSUPPORTED_DEPTH = 38;
3272    public static final int TRANSPARENCY_MASK = 1 << 1;
3273    public static final int ERROR_UNSUPPORTED_FORMAT = 42;
3274    public static final int TRANSPARENCY_ALPHA = 1 << 0;
3275    public static final int TRANSPARENCY_NONE = 0x0;
3276    public static final int TRANSPARENCY_PIXEL = 1 << 2;
3277    public static final int IMAGE_BMP = 0;
3278    public static final int IMAGE_BMP_RLE = 1;
3279    
3280    public static void error(int code) {
3281        throw new RuntimeException JavaDoc("Error "+code); //$NON-NLS-1$
3282
}
3283    public static void error(int code, Throwable JavaDoc t) {
3284        throw new RuntimeException JavaDoc(t);
3285    }
3286}
3287}
3288
Popular Tags