| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.swt.tools.internal; |
| |
| import java.io.*; |
| import java.util.Vector; |
| |
| /** |
| * Customize the icon of a Windows exe |
| * |
| * WARNING! This class is not part of SWT API. It is NOT API. It is an internal |
| * tool that may be changed or removed at anytime. |
| * |
| * Based on MSDN "An In-Depth Look into the Win32 Portable Executable File Format" |
| */ |
| public class IconExe { |
| |
| /** |
| * Replace the Desktop icons provided in the Windows executable program |
| * with matching icons provided by the user. |
| * |
| * Takes 2 arguments |
| * argument 0: the Windows executable e.g c:/eclipse/eclipse.exe |
| * argument 1: The .ico file to write to the given executable e.g. c:/myApp.ico |
| * |
| * Note 1. Write access to the executable program is required. As a result, that |
| * program must not be currently running or edited elsewhere. |
| * |
| * Note 2. The Eclipse 3.1 launcher requires a .ico file with the following 6 images |
| * 1. 32x32, 4 bit (Windows 16 colors palette) |
| * 2. 16x16, 4 bit (Windows 16 colors palette) |
| * 3. 16x16, 8 bit (256 colors) |
| * 4. 32x32, 8 bit (256 colors) |
| * 5. 48x48, 4 bit (Windows 16 colors palette) |
| * 6. 48x48, 8 bit (256 colors) |
| * A user icon matching exactly the width/height/depth of an executable icon will be written |
| * to the executable and will replace that executable icon. If an executable icon |
| * does not match a user icon, it is silently left as is. |
| * |
| * Note 3. This function modifies the content of the executable program and may cause |
| * its corruption. |
| */ |
| public static void main(String[] args) { |
| if (args.length < 2) { |
| System.err.println("Usage: IconExe <windows executable> <ico file>"); |
| return; |
| } |
| ImageLoader loader = new ImageLoader(); |
| try { |
| ImageData[] data = null; |
| |
| if (args.length == 2) { |
| /* ICO case */ |
| data = loader.load(args[1]); |
| } else { |
| /* BMP case - each following argument is a single BMP file |
| * BMP is handled for testing purpose only. The ICO file is the |
| * official Microsoft format for image resources. |
| */ |
| data = new ImageData[args.length - 1]; |
| for (int i = 1; i < args.length; i++) { |
| ImageData[] current = loader.load(args[i]); |
| data[i - 1] = current[0]; |
| } |
| } |
| int nMissing = unloadIcons(args[0], data); |
| if (nMissing != 0) System.err.println("Error - "+nMissing+" icon(s) not replaced in "+args[0]+" using "+args[1]); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| /* Implementation */ |
| |
| /** |
| * Retrieve the Desktop icons provided in the Windows executable program. |
| * These icons are typically shown in various places of the Windows desktop. |
| * |
| * Note. The Eclipse 3.1 launcher returns the following 6 images |
| * 1. 32x32, 4 bit (Windows 16 colors palette) |
| * 2. 16x16, 4 bit (Windows 16 colors palette) |
| * 3. 16x16, 8 bit (256 colors) |
| * 4. 32x32, 8 bit (256 colors) |
| * 5. 48x48, 4 bit (Windows 16 colors palette) |
| * 6. 48x48, 8 bit (256 colors) |
| * |
| * @param program the Windows executable e.g c:/eclipse/eclipse.exe |
| */ |
| static ImageData[] loadIcons(String program) throws FileNotFoundException, IOException { |
| RandomAccessFile raf = new RandomAccessFile(program, "r"); |
| IconExe iconExe = new IconExe(); |
| IconResInfo[] iconInfo = iconExe.getIcons(raf); |
| ImageData[] data = new ImageData[iconInfo.length]; |
| for (int i = 0; i < data.length; i++) data[i] = iconInfo[i].data; |
| raf.close(); |
| return data; |
| } |
| |
| /** |
| * Replace the Desktop icons provided in the Windows executable program |
| * with icons provided by the user. |
| * |
| * Note 1. Write access to the executable program is required. As a result, that |
| * program must not be currently running or edited elsewhere. |
| * |
| * Note 2. Use loadIcons to determine which set of icons (width, height, depth) |
| * is required to replace the icons in the executable program. A user icon |
| * matching exactly the width/height/depth of an executable icon will be written |
| * to the executable and will replace that executable icon. If an executable icon |
| * does not match a user icon, it is left as is. Verify the return value matches |
| * the number of icons to write. Finally, use loadIcons after this operation |
| * to verify the icons have changed as expected. |
| * |
| * Note 3. The Eclipse 3.1 launcher requires the following 6 images (in any order). |
| * 1. 32x32, 4 bit (Windows 16 colors palette) |
| * 2. 16x16, 4 bit (Windows 16 colors palette) |
| * 3. 16x16, 8 bit (256 colors) |
| * 4. 32x32, 8 bit (256 colors) |
| * 5. 48x48, 4 bit (Windows 16 colors palette) |
| * 6. 48x48, 8 bit (256 colors) |
| * |
| * Note 4. This function modifies the content of the executable program and may cause |
| * its corruption. |
| * |
| * @param program the Windows executable e.g c:/eclipse/eclipse.exe |
| * @param icons to write to the given executable |
| * @return the number of icons from the original program that were not successfully replaced (0 if success) |
| */ |
| static int unloadIcons(String program, ImageData[] icons) throws FileNotFoundException, IOException { |
| RandomAccessFile raf = new RandomAccessFile(program, "rw"); |
| IconExe iconExe = new IconExe(); |
| IconResInfo[] iconInfo = iconExe.getIcons(raf); |
| int cnt = 0; |
| for (int i = 0; i < iconInfo.length; i++) { |
| for (int j = 0; j < icons.length; j++) |
| if (iconInfo[i].data.width == icons[j].width && |
| iconInfo[i].data.height == icons[j].height && |
| iconInfo[i].data.depth == icons[j].depth) { |
| raf.seek(iconInfo[i].offset); |
| unloadIcon(raf, icons[j]); |
| cnt++; |
| } |
| } |
| raf.close(); |
| return iconInfo.length - cnt; |
| } |
| |
| public static final String VERSION = "20050124"; |
| |
| static final boolean DEBUG = false; |
| public static class IconResInfo { |
| ImageData data; |
| int offset; |
| int size; |
| } |
| |
| IconResInfo[] iconInfo = null; |
| int iconCnt; |
| |
| IconResInfo[] getIcons(RandomAccessFile raf) throws IOException { |
| iconInfo = new IconResInfo[4]; |
| iconCnt = 0; |
| IMAGE_DOS_HEADER imageDosHeader = new IMAGE_DOS_HEADER(); |
| read(raf, imageDosHeader); |
| if (imageDosHeader.e_magic != IMAGE_DOS_SIGNATURE) return null; |
| int imageNtHeadersOffset = imageDosHeader.e_lfanew; |
| raf.seek(imageNtHeadersOffset); |
| IMAGE_NT_HEADERS imageNtHeaders = new IMAGE_NT_HEADERS(); |
| read(raf, imageNtHeaders); |
| if (imageNtHeaders.Signature != IMAGE_NT_SIGNATURE) return null; |
| |
| // DumpResources |
| int resourcesRVA = imageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; |
| if (resourcesRVA == 0) return null; |
| if (DEBUG) System.out.println("* Resources (RVA= "+resourcesRVA+")"); |
| IMAGE_SECTION_HEADER imageSectionHeader = new IMAGE_SECTION_HEADER(); |
| int firstSectionOffset = imageNtHeadersOffset + IMAGE_NT_HEADERS.FIELD_OFFSET_OptionalHeader + imageNtHeaders.FileHeader.SizeOfOptionalHeader; |
| raf.seek(firstSectionOffset); |
| boolean found = false; |
| for (int i = 0; i < imageNtHeaders.FileHeader.NumberOfSections; i++) { |
| read(raf, imageSectionHeader); |
| if (resourcesRVA >= imageSectionHeader.VirtualAddress && resourcesRVA < imageSectionHeader.VirtualAddress + imageSectionHeader.Misc_VirtualSize) { |
| // could check the imageSectionHeader name is .rsrc |
| found = true; |
| break; |
| } |
| } |
| if (!found) return null; |
| int delta = imageSectionHeader.VirtualAddress - imageSectionHeader.PointerToRawData; |
| int imageResourceDirectoryOffset = resourcesRVA - delta; |
| dumpResourceDirectory(raf, imageResourceDirectoryOffset, imageResourceDirectoryOffset, delta, 0, 0, false); |
| if (iconCnt < iconInfo.length) { |
| IconResInfo[] newArray = new IconResInfo[iconCnt]; |
| System.arraycopy(iconInfo, 0, newArray, 0, iconCnt); |
| iconInfo = newArray; |
| } |
| return iconInfo; |
| } |
| |
| void dumpResourceDirectory(RandomAccessFile raf, int imageResourceDirectoryOffset, int resourceBase, int delta, int type, int level, boolean rt_icon_root) throws IOException { |
| if (DEBUG) System.out.println("** LEVEL "+level); |
| |
| IMAGE_RESOURCE_DIRECTORY imageResourceDirectory = new IMAGE_RESOURCE_DIRECTORY(); |
| raf.seek(imageResourceDirectoryOffset); |
| read(raf, imageResourceDirectory); |
| |
| if (DEBUG) { |
| String sType = ""+type; |
| // level 1 resources are resource types |
| if (level == 1) { |
| System.out.println("___________________________"); |
| if (type == RT_ICON) sType = "RT_ICON"; |
| if (type == RT_GROUP_ICON) sType = "RT_GROUP_ICON"; |
| } |
| System.out.println("Resource Directory ["+sType+"]"+" (Named "+imageResourceDirectory.NumberOfNamedEntries+", ID "+imageResourceDirectory.NumberOfIdEntries+")"); |
| } |
| int IRDE_StartOffset = imageResourceDirectoryOffset + IMAGE_RESOURCE_DIRECTORY.SIZEOF; |
| IMAGE_RESOURCE_DIRECTORY_ENTRY[] imageResourceDirectoryEntries = new IMAGE_RESOURCE_DIRECTORY_ENTRY[imageResourceDirectory.NumberOfIdEntries]; |
| for (int i = 0; i < imageResourceDirectoryEntries.length; i++) { |
| imageResourceDirectoryEntries[i] = new IMAGE_RESOURCE_DIRECTORY_ENTRY(); |
| read(raf, imageResourceDirectoryEntries[i]); |
| } |
| for (int i = 0; i < imageResourceDirectoryEntries.length; i++) { |
| if (imageResourceDirectoryEntries[i].DataIsDirectory) { |
| dumpResourceDirectory(raf, imageResourceDirectoryEntries[i].OffsetToDirectory + resourceBase, resourceBase, delta, imageResourceDirectoryEntries[i].Id, level + 1, rt_icon_root ? true : type == RT_ICON); |
| } else { |
| // Resource found |
| /// pResDirEntry->Name |
| IMAGE_RESOURCE_DIRECTORY_ENTRY irde = imageResourceDirectoryEntries[i]; |
| IMAGE_RESOURCE_DATA_ENTRY data = new IMAGE_RESOURCE_DATA_ENTRY(); |
| raf.seek(imageResourceDirectoryEntries[i].OffsetToData + resourceBase); |
| read(raf, data); |
| if (DEBUG) System.out.println("Resource Id "+irde.Id+" Data Offset RVA "+data.OffsetToData+", Size "+data.Size); |
| if (rt_icon_root) { |
| if (DEBUG) System.out.println("iconcnt "+iconCnt+" |"+iconInfo.length); |
| iconInfo[iconCnt] = new IconResInfo(); |
| iconInfo[iconCnt].data = parseIcon(raf, data.OffsetToData - delta, data.Size); |
| iconInfo[iconCnt].offset = data.OffsetToData - delta; |
| iconInfo[iconCnt].size = data.Size; |
| iconCnt++; |
| if (iconCnt == iconInfo.length) { |
| IconResInfo[] newArray = new IconResInfo[iconInfo.length + 4]; |
| System.arraycopy(iconInfo, 0, newArray, 0, iconInfo.length); |
| iconInfo = newArray; |
| } |
| } |
| } |
| } |
| } |
| |
| static ImageData parseIcon(RandomAccessFile raf, int offset, int size) throws IOException { |
| raf.seek(offset); |
| BITMAPINFO bitmapInfo = new BITMAPINFO(); |
| read(raf, bitmapInfo); |
| bitmapInfo.bmiHeader.biHeight /= 2; |
| int width = bitmapInfo.bmiHeader.biWidth; |
| int height = bitmapInfo.bmiHeader.biHeight; |
| int depth = bitmapInfo.bmiHeader.biBitCount; |
| |
| PaletteData palette = loadPalette(bitmapInfo.bmiHeader, raf); |
| byte[] shapeData = loadData(bitmapInfo.bmiHeader, raf); |
| bitmapInfo.bmiHeader.biBitCount = 1; |
| byte[] maskData = loadData(bitmapInfo.bmiHeader, raf); |
| maskData = convertPad(maskData, width, height, 1, 4, 2); |
| bitInvertData(maskData, 0, maskData.length); |
| return ImageData.internal_new( |
| width, |
| height, |
| depth, |
| palette, |
| 4, |
| shapeData, |
| 2, |
| maskData, |
| null, |
| -1, |
| -1, |
| SWT.IMAGE_ICO, |
| 0, |
| 0, |
| 0, |
| 0); |
| } |
| |
| static byte[] bitInvertData(byte[] data, int startIndex, int endIndex) { |
| // Destructively bit invert data in the given byte array. |
| for (int i = startIndex; i < endIndex; i++) { |
| data[i] = (byte)(255 - data[i - startIndex]); |
| } |
| return data; |
| } |
| |
| static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) { |
| if (pad == newPad) return data; |
| int stride = (width * depth + 7) / 8; |
| int bpl = (stride + (pad - 1)) / pad * pad; |
| int newBpl = (stride + (newPad - 1)) / newPad * newPad; |
| byte[] newData = new byte[height * newBpl]; |
| int srcIndex = 0, destIndex = 0; |
| for (int y = 0; y < height; y++) { |
| System.arraycopy(data, srcIndex, newData, destIndex, newBpl); |
| srcIndex += bpl; |
| destIndex += newBpl; |
| } |
| return newData; |
| } |
| static PaletteData loadPalette(BITMAPINFOHEADER bih, RandomAccessFile raf) throws IOException { |
| int depth = bih.biBitCount; |
| if (depth <= 8) { |
| int numColors = bih.biClrUsed; |
| if (numColors == 0) { |
| numColors = 1 << depth; |
| } else { |
| if (numColors > 256) |
| numColors = 256; |
| } |
| byte[] buf = new byte[numColors * 4]; |
| raf.read(buf); |
| return paletteFromBytes(buf, numColors); |
| } |
| if (depth == 16) return new PaletteData(0x7C00, 0x3E0, 0x1F); |
| if (depth == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000); |
| return new PaletteData(0xFF00, 0xFF0000, 0xFF000000); |
| } |
| static PaletteData paletteFromBytes(byte[] bytes, int numColors) { |
| int bytesOffset = 0; |
| RGB[] colors = new RGB[numColors]; |
| for (int i = 0; i < numColors; i++) { |
| colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF, |
| bytes[bytesOffset + 1] & 0xFF, |
| bytes[bytesOffset] & 0xFF); |
| bytesOffset += 4; |
| } |
| return new PaletteData(colors); |
| } |
| static byte[] loadData(BITMAPINFOHEADER bih, RandomAccessFile raf) throws IOException { |
| int stride = (bih.biWidth * bih.biBitCount + 7) / 8; |
| stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple |
| byte[] data = loadData(bih, raf, stride); |
| flipScanLines(data, stride, bih.biHeight); |
| return data; |
| } |
| static void flipScanLines(byte[] data, int stride, int height) { |
| int i1 = 0; |
| int i2 = (height - 1) * stride; |
| for (int i = 0; i < height / 2; i++) { |
| for (int index = 0; index < stride; index++) { |
| byte b = data[index + i1]; |
| data[index + i1] = data[index + i2]; |
| data[index + i2] = b; |
| } |
| i1 += stride; |
| i2 -= stride; |
| } |
| } |
| static byte[] loadData(BITMAPINFOHEADER bih, RandomAccessFile raf, int stride) throws IOException { |
| int dataSize = bih.biHeight * stride; |
| byte[] data = new byte[dataSize]; |
| int cmp = bih.biCompression; |
| if (cmp == 0) { // BMP_NO_COMPRESSION |
| raf.read(data); |
| } else { |
| if (DEBUG) System.out.println("ICO cannot be compressed?"); |
| } |
| return data; |
| } |
| |
| static void unloadIcon(RandomAccessFile raf, ImageData icon) throws IOException { |
| int sizeImage = (((icon.width * icon.depth + 31) / 32 * 4) + |
| ((icon.width + 31) / 32 * 4)) * icon.height; |
| write4(raf, BMPHeaderFixedSize); |
| write4(raf, icon.width); |
| write4(raf, icon.height * 2); |
| writeU2(raf, 1); |
| writeU2(raf, icon.depth); |
| write4(raf, 0); |
| write4(raf, sizeImage); |
| write4(raf, 0); |
| write4(raf, 0); |
| write4(raf, icon.palette.colors != null ? icon.palette.colors.length : 0); |
| write4(raf, 0); |
| |
| byte[] rgbs = paletteToBytes(icon.palette); |
| raf.write(rgbs); |
| unloadShapeData(raf, icon); |
| unloadMaskData(raf, icon); |
| } |
| static byte[] paletteToBytes(PaletteData pal) { |
| int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256); |
| byte[] bytes = new byte[n * 4]; |
| int offset = 0; |
| for (int i = 0; i < n; i++) { |
| RGB col = pal.colors[i]; |
| bytes[offset] = (byte)col.blue; |
| bytes[offset + 1] = (byte)col.green; |
| bytes[offset + 2] = (byte)col.red; |
| offset += 4; |
| } |
| return bytes; |
| } |
| static void unloadMaskData(RandomAccessFile raf, ImageData icon) { |
| ImageData mask = icon.getTransparencyMask(); |
| int bpl = (icon.width + 7) / 8; |
| int pad = mask.scanlinePad; |
| int srcBpl = (bpl + pad - 1) / pad * pad; |
| int destBpl = (bpl + 3) / 4 * 4; |
| byte[] buf = new byte[destBpl]; |
| int offset = (icon.height - 1) * srcBpl; |
| byte[] data = mask.data; |
| try { |
| for (int i = 0; i < icon.height; i++) { |
| System.arraycopy(data, offset, buf, 0, bpl); |
| bitInvertData(buf, 0, bpl); |
| raf.write(buf, 0, destBpl); |
| offset -= srcBpl; |
| } |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| } |
| static void unloadShapeData(RandomAccessFile raf, ImageData icon) { |
| int bpl = (icon.width * icon.depth + 7) / 8; |
| int pad = icon.scanlinePad; |
| int srcBpl = (bpl + pad - 1) / pad * pad; |
| int destBpl = (bpl + 3) / 4 * 4; |
| byte[] buf = new byte[destBpl]; |
| int offset = (icon.height - 1) * srcBpl; |
| byte[] data = icon.data; |
| try { |
| for (int i = 0; i < icon.height; i++) { |
| System.arraycopy(data, offset, buf, 0, bpl); |
| raf.write(buf, 0, destBpl); |
| offset -= srcBpl; |
| } |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| } |
| static boolean readIconGroup(RandomAccessFile raf, int offset, int size) throws IOException { |
| raf.seek(offset); |
| NEWHEADER newHeader = new NEWHEADER(); |
| read(raf, newHeader); |
| if (newHeader.ResType != RES_ICON) return false; |
| RESDIR[] resDir = new RESDIR[newHeader.ResCount]; |
| for (int i = 0; i < newHeader.ResCount; i++) { |
| resDir[i] = new RESDIR(); |
| read(raf, resDir[i]); |
| } |
| return true; |
| } |
| |
| static void copyFile(String src, String dst) throws FileNotFoundException, IOException { |
| File srcFile = new File(src); |
| File dstFile = new File(dst); |
| FileInputStream in = new FileInputStream(srcFile); |
| FileOutputStream out = new FileOutputStream(dstFile); |
| int c; |
| while ((c = in.read()) != -1) out.write(c); |
| in.close(); |
| out.close(); |
| } |
| |
| /* IO utilities to parse Windows executable */ |
| static final int IMAGE_DOS_SIGNATURE = 0x5a4d; |
| static final int IMAGE_NT_SIGNATURE = 0x00004550; |
| static final int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; |
| static final int RES_ICON = 1; |
| static final int RT_ICON = 3; |
| static final int RT_GROUP_ICON = 14; |
| static final int BMPHeaderFixedSize = 40; |
| |
| public static class IMAGE_DOS_HEADER { |
| int e_magic; // WORD |
| int e_cblp; // WORD |
| int e_cp; // WORD |
| int e_crlc; // WORD |
| int e_cparhdr; // WORD |
| int e_minalloc; // WORD |
| int e_maxalloc; // WORD |
| int e_ss; // WORD |
| int e_sp; // WORD |
| int e_csum; // WORD |
| int e_ip; // WORD |
| int e_cs; // WORD |
| int e_lfarlc; // WORD |
| int e_ovno; // WORD |
| int[] e_res = new int[4]; // WORD[4] |
| int e_oemid; // WORD |
| int e_oeminfo; // WORD |
| int[] e_res2 = new int[10]; // WORD[10] |
| int e_lfanew; // LONG |
| } |
| |
| public static class IMAGE_FILE_HEADER { |
| int Machine; // WORD |
| int NumberOfSections; // WORD |
| int TimeDateStamp; // DWORD |
| int PointerToSymbolTable; // DWORD |
| int NumberOfSymbols; // DWORD |
| int SizeOfOptionalHeader; // WORD |
| int Characteristics; // WORD |
| }; |
| |
| public static class IMAGE_DATA_DIRECTORY { |
| int VirtualAddress; // DWORD |
| int Size; // DWORD |
| } |
| |
| public static class IMAGE_OPTIONAL_HEADER { |
| int Magic; // WORD |
| int MajorLinkerVersion; // BYTE |
| int MinorLinkerVersion; // BYTE |
| int SizeOfCode; // DWORD |
| int SizeOfInitializedData; // DWORD |
| int SizeOfUninitializedData; // DWORD |
| int AddressOfEntryPoint; // DWORD |
| int BaseOfCode; // DWORD |
| int BaseOfData; // DWORD |
| int ImageBase; // DWORD |
| int SectionAlignment; // DWORD |
| int FileAlignment; // DWORD |
| int MajorOperatingSystemVersion; // WORD |
| int MinorOperatingSystemVersion; // WORD |
| int MajorImageVersion; // WORD |
| int MinorImageVersion; // WORD |
| int MajorSubsystemVersion; // WORD |
| int MinorSubsystemVersion; // WORD |
| int Win32VersionValue; // DWORD |
| int SizeOfImage; // DWORD |
| int SizeOfHeaders; // DWORD |
| int CheckSum; // DWORD |
| int Subsystem; // WORD |
| int DllCharacteristics; // WORD |
| int SizeOfStackReserve; // DWORD |
| int SizeOfStackCommit; // DWORD |
| int SizeOfHeapReserve; // DWORD |
| int SizeOfHeapCommit; // DWORD |
| int LoaderFlags; // DWORD |
| int NumberOfRvaAndSizes; // DWORD |
| IMAGE_DATA_DIRECTORY[] DataDirectory = new IMAGE_DATA_DIRECTORY[16]; |
| } |
| public static class IMAGE_NT_HEADERS { |
| int Signature; // DWORD |
| IMAGE_FILE_HEADER FileHeader = new IMAGE_FILE_HEADER(); |
| IMAGE_OPTIONAL_HEADER OptionalHeader = new IMAGE_OPTIONAL_HEADER(); |
| final static int FIELD_OFFSET_OptionalHeader = 24; |
| } |
| |
| public static class IMAGE_SECTION_HEADER { |
| int[] Name = new int[8]; // BYTE[8] |
| int Misc_VirtualSize; // DWORD (union Misc { DWORD PhysicalAddress; DWORD VirtualSize } |
| int VirtualAddress; // DWORD |
| int SizeOfRawData; // DWORD |
| int PointerToRawData; // DWORD |
| int PointerToRelocations; // DWORD |
| int PointerToLinenumbers; // DWORD |
| int NumberOfRelocations; // WORD |
| int NumberOfLinenumbers; // WORD |
| int Characteristics; // DWORD |
| }; |
| |
| public static class IMAGE_RESOURCE_DIRECTORY { |
| int Characteristics; // DWORD |
| int TimeDateStamp; // DWORD |
| int MajorVersion; // WORD |
| int MinorVersion; // WORD |
| int NumberOfNamedEntries; // WORD - used |
| int NumberOfIdEntries; // WORD - used |
| final static int SIZEOF = 16; |
| } |
| |
| public static class IMAGE_RESOURCE_DIRECTORY_ENTRY { |
| // union |
| int NameOffset; // DWORD 31 bits |
| boolean NameIsString; // DWORD 1 bit |
| int Name; // DWORD |
| int Id; // WORD |
| // union |
| int OffsetToData; // DWORD |
| int OffsetToDirectory; // DWORD 31 bits |
| boolean DataIsDirectory; // DWORD 1 bit |
| } |
| |
| public static class IMAGE_RESOURCE_DATA_ENTRY { |
| int OffsetToData; // DWORD |
| int Size; // DWORD |
| int CodePage; // DWORD |
| int Reserved; // DWORD |
| } |
| |
| public static class NEWHEADER { |
| int Reserved; // WORD |
| int ResType; // WORD |
| int ResCount; // WORD |
| } |
| |
| public static class ICONRESDIR { |
| int Width; // BYTE |
| int Height; // BYTE |
| int ColorCount; // BYTE |
| int reserved; // BYTE |
| } |
| |
| public static class CURSORDIR { |
| int Width; // WORD |
| int Height; // WORD |
| } |
| |
| public static class RESDIR { |
| // union |
| ICONRESDIR Icon = new ICONRESDIR(); |
| CURSORDIR Cursor = new CURSORDIR(); |
| int Planes; // WORD |
| int BitCount; // WORD |
| int BytesInRes; // DWORD |
| int IconCursorId; // WORD |
| } |
| |
| public static class BITMAPINFOHEADER { |
| int biSize; // DWORD |
| int biWidth; // LONG |
| int biHeight; // LONG |
| int biPlanes; // WORD |
| int biBitCount; // WORD |
| int biCompression; // DWORD |
| int biSizeImage; // DWORD |
| int biXPelsPerMeter; // LONG |
| int biYPelsPerMeter; // LONG |
| int biClrUsed; // DWORD |
| int biClrImportant; // DWORD |
| } |
| |
| static class RGBQUAD { |
| int rgBlue; // BYTE |
| int rgbGreen; // BYTE |
| int rgbRed; // BYTE |
| int rgbReserved; // BYTE |
| } |
| static class BITMAPINFO { |
| BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); |
| RGBQUAD[] bmiColors = null; |
| } |
| static void read(RandomAccessFile raf, BITMAPINFOHEADER bih) throws IOException { |
| bih.biSize = read4(raf); |
| bih.biWidth = read4(raf); |
| bih.biHeight = read4(raf); |
| bih.biPlanes = readU2(raf); |
| bih.biBitCount = readU2(raf); |
| bih.biCompression = read4(raf); |
| bih.biSizeImage = read4(raf); |
| bih.biXPelsPerMeter = read4(raf); |
| bih.biYPelsPerMeter = read4(raf); |
| bih.biClrUsed = read4(raf); |
| bih.biClrImportant = read4(raf); |
| } |
| static void read(RandomAccessFile raf, BITMAPINFO bi) throws IOException { |
| read(raf, bi.bmiHeader); |
| } |
| /* Little Endian helpers */ |
| static int readU2(RandomAccessFile raf) throws IOException { |
| int b0 = raf.readByte() & 0xFF; |
| int b1 = raf.readByte() & 0xFF; |
| return (b1 << 8 | b0); |
| } |
| static int read4(RandomAccessFile raf) throws IOException { |
| int b0 = raf.readByte() & 0xFF;; |
| int b1 = raf.readByte() & 0xFF;; |
| int b2 = raf.readByte() & 0xFF;; |
| int b3 = raf.readByte() & 0xFF;; |
| return b3 << 24 | b2 << 16 | b1 << 8 | b0; |
| } |
| static void write4(RandomAccessFile raf, int value) throws IOException { |
| raf.write(value & 0xFF); |
| raf.write((value >> 8) & 0xFF); |
| raf.write((value >> 16) & 0xFF); |
| raf.write((value >> 24) & 0xFF); |
| } |
| static void writeU2(RandomAccessFile raf, int value) throws IOException { |
| raf.write(value & 0xFF); |
| raf.write((value >> 8) & 0xFF); |
| } |
| static void read(RandomAccessFile raf, IMAGE_DOS_HEADER idh) throws IOException { |
| idh.e_magic = readU2(raf); |
| idh.e_cblp = readU2(raf); |
| idh.e_cp = readU2(raf); |
| idh.e_crlc = readU2(raf); |
| idh.e_cparhdr = readU2(raf); |
| idh.e_minalloc = readU2(raf); |
| idh.e_maxalloc = readU2(raf); |
| idh.e_ss = readU2(raf); |
| idh.e_sp = readU2(raf); |
| idh.e_csum = readU2(raf); |
| idh.e_ip = readU2(raf); |
| idh.e_cs = readU2(raf); |
| idh.e_lfarlc = readU2(raf); |
| idh.e_ovno = readU2(raf); |
| for (int i = 0; i < idh.e_res.length; i++) idh.e_res[i] = readU2(raf); |
| idh.e_oemid = readU2(raf); |
| idh.e_oeminfo = readU2(raf); |
| for (int i = 0; i < idh.e_res2.length; i++) idh.e_res2[i] = readU2(raf); |
| idh.e_lfanew = read4(raf); |
| } |
| static void read(RandomAccessFile raf, IMAGE_FILE_HEADER ifh) throws IOException { |
| ifh.Machine = readU2(raf); |
| ifh.NumberOfSections = readU2(raf); |
| ifh.TimeDateStamp = read4(raf); |
| ifh.PointerToSymbolTable = read4(raf); |
| ifh.NumberOfSymbols = read4(raf); |
| ifh.SizeOfOptionalHeader = readU2(raf); |
| ifh.Characteristics = readU2(raf); |
| } |
| static void read(RandomAccessFile raf, IMAGE_DATA_DIRECTORY idd) throws IOException { |
| idd.VirtualAddress = read4(raf); |
| idd.Size = read4(raf); |
| } |
| static void read(RandomAccessFile raf, IMAGE_OPTIONAL_HEADER ioh) throws IOException { |
| ioh.Magic = readU2(raf); |
| ioh.MajorLinkerVersion = raf.read(); |
| ioh.MinorLinkerVersion = raf.read(); |
| ioh.SizeOfCode = read4(raf); |
| ioh.SizeOfInitializedData = read4(raf); |
| ioh.SizeOfUninitializedData = read4(raf); |
| ioh.AddressOfEntryPoint = read4(raf); |
| ioh.BaseOfCode = read4(raf); |
| ioh.BaseOfData = read4(raf); |
| ioh.ImageBase = read4(raf); |
| ioh.SectionAlignment = read4(raf); |
| ioh.FileAlignment = read4(raf); |
| ioh.MajorOperatingSystemVersion = readU2(raf); |
| ioh.MinorOperatingSystemVersion = readU2(raf); |
| ioh.MajorImageVersion = readU2(raf); |
| ioh.MinorImageVersion = readU2(raf); |
| ioh.MajorSubsystemVersion = readU2(raf); |
| ioh.MinorSubsystemVersion = readU2(raf); |
| ioh.Win32VersionValue = read4(raf); |
| ioh.SizeOfImage = read4(raf); |
| ioh.SizeOfHeaders = read4(raf); |
| ioh.CheckSum = read4(raf); |
| ioh.Subsystem = readU2(raf); |
| ioh.DllCharacteristics = readU2(raf); |
| ioh.SizeOfStackReserve = read4(raf); |
| ioh.SizeOfStackCommit = read4(raf); |
| ioh.SizeOfHeapReserve = read4(raf); |
| ioh.SizeOfHeapCommit = read4(raf); |
| ioh.LoaderFlags = read4(raf); |
| ioh.NumberOfRvaAndSizes = read4(raf); |
| for (int i = 0 ; i < ioh.DataDirectory.length; i++) { |
| ioh.DataDirectory[i] = new IMAGE_DATA_DIRECTORY(); |
| read(raf, ioh.DataDirectory[i]); |
| } |
| } |
| static void read(RandomAccessFile raf, IMAGE_NT_HEADERS inh) throws IOException { |
| inh.Signature = read4(raf); |
| read(raf, inh.FileHeader); |
| read(raf, inh.OptionalHeader); |
| } |
| static void read(RandomAccessFile raf, IMAGE_SECTION_HEADER ish) throws IOException { |
| for (int i = 0 ; i < ish.Name.length; i++) ish.Name[i] = raf.read(); |
| ish.Misc_VirtualSize = read4(raf); |
| ish.VirtualAddress = read4(raf); |
| ish.SizeOfRawData = read4(raf); |
| ish.PointerToRawData = read4(raf); |
| ish.PointerToRelocations = read4(raf); |
| ish.PointerToLinenumbers = read4(raf); |
| ish.NumberOfRelocations = readU2(raf); |
| ish.NumberOfLinenumbers = readU2(raf); |
| ish.Characteristics = read4(raf); |
| } |
| static void read(RandomAccessFile raf, IMAGE_RESOURCE_DIRECTORY ird) throws IOException { |
| ird.Characteristics = read4(raf); |
| ird.TimeDateStamp = read4(raf); |
| ird.MajorVersion = readU2(raf); |
| ird.MinorVersion = readU2(raf); |
| ird.NumberOfNamedEntries = readU2(raf); |
| ird.NumberOfIdEntries = readU2(raf); |
| }; |
| static void read(RandomAccessFile raf, IMAGE_RESOURCE_DIRECTORY_ENTRY irde) throws IOException { |
| irde.Name = read4(raf); |
| irde.OffsetToData = read4(raf); |
| // construct other union members |
| irde.NameOffset = irde.Name & ~ (1 << 31); |
| irde.NameIsString = (irde.Name & (1 << 31)) != 0; |
| irde.Id = irde.Name & 0xFFFF; |
| irde.OffsetToDirectory = irde.OffsetToData & ~ (1 << 31); |
| irde.DataIsDirectory = (irde.OffsetToData & (1 << 31)) != 0; |
| } |
| static void read(RandomAccessFile raf, IMAGE_RESOURCE_DATA_ENTRY irde) throws IOException { |
| irde.OffsetToData = read4(raf); |
| irde.Size = read4(raf); |
| irde.CodePage = read4(raf); |
| irde.Reserved = read4(raf); |
| } |
| static void read(RandomAccessFile raf, NEWHEADER nh) throws IOException { |
| nh.Reserved = readU2(raf); |
| nh.ResType = readU2(raf); |
| nh.ResCount = readU2(raf); |
| } |
| static void read(RandomAccessFile raf, ICONRESDIR i) throws IOException { |
| i.Width = raf.read(); |
| i.Height = raf.read(); |
| i.ColorCount = raf.read(); |
| i.reserved = raf.read(); |
| } |
| static void read(RandomAccessFile raf, CURSORDIR c) throws IOException { |
| c.Width = readU2(raf); |
| c.Height = readU2(raf); |
| } |
| static void read(RandomAccessFile raf, RESDIR rs) throws IOException { |
| long start = raf.getFilePointer(); |
| read(raf, rs.Icon); |
| raf.seek(start); |
| read(raf, rs.Cursor); |
| rs.Planes = readU2(raf); |
| rs.BitCount = readU2(raf); |
| rs.BytesInRes = read4(raf); |
| rs.IconCursorId = readU2(raf); |
| } |
| |
| /* ImageData and Image Decoder inlining to avoid dependency on SWT |
| * The following section can be entirely removed if SWT can be used. |
| */ |
| |
| static class RGB { |
| |
| /** |
| * the red component of the RGB |
| */ |
| public int red; |
| |
| /** |
| * the green component of the RGB |
| */ |
| public int green; |
| |
| /** |
| * the blue component of the RGB |
| */ |
| public int blue; |
| |
| static final long serialVersionUID = 3258415023461249074L; |
| |
| /** |
| * Constructs an instance of this class with the given |
| * red, green and blue values. |
| * |
| * @param red the red component of the new instance |
| * @param green the green component of the new instance |
| * @param blue the blue component of the new instance |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li> |
| * </ul> |
| */ |
| public RGB(int red, int green, int blue) { |
| if ((red > 255) || (red < 0) || |
| (green > 255) || (green < 0) || |
| (blue > 255) || (blue < 0)) |
| SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| this.red = red; |
| this.green = green; |
| this.blue = blue; |
| } |
| |
| /** |
| * Compares the argument to the receiver, and returns true |
| * if they represent the <em>same</em> object using a class |
| * specific comparison. |
| * |
| * @param object the object to compare with this object |
| * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise |
| * |
| * @see #hashCode() |
| */ |
| public boolean equals (Object object) { |
| if (object == this) return true; |
| if (!(object instanceof RGB)) return false; |
| RGB rgb = (RGB)object; |
| return (rgb.red == this.red) && (rgb.green == this.green) && (rgb.blue == this.blue); |
| } |
| |
| /** |
| * Returns an integer hash code for the receiver. Any two |
| * objects which return <code>true</code> when passed to |
| * <code>equals</code> must return the same value for this |
| * method. |
| * |
| * @return the receiver's hash |
| * |
| * @see #equals(Object) |
| */ |
| public int hashCode () { |
| return (blue << 16) | (green << 8) | red; |
| } |
| |
| /** |
| * Returns a string containing a concise, human-readable |
| * description of the receiver. |
| * |
| * @return a string representation of the <code>RGB</code> |
| */ |
| public String toString () { |
| return "RGB {" + red + ", " + green + ", " + blue + "}"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ |
| |
| //$NON-NLS-4$ |
| } |
| |
| } |
| static class PaletteData { |
| |
| /** |
| * true if the receiver is a direct palette, |
| * and false otherwise |
| */ |
| public boolean isDirect; |
| |
| /** |
| * the RGB values for an indexed palette, where the |
| * indices of the array correspond to pixel values |
| */ |
| public RGB[] colors; |
| |
| /** |
| * the red mask for a direct palette |
| */ |
| public int redMask; |
| |
| /** |
| * the green mask for a direct palette |
| */ |
| public int greenMask; |
| |
| /** |
| * the blue mask for a direct palette |
| */ |
| public int blueMask; |
| |
| /** |
| * the red shift for a direct palette |
| */ |
| public int redShift; |
| |
| /** |
| * the green shift for a direct palette |
| */ |
| public int greenShift; |
| |
| /** |
| * the blue shift for a direct palette |
| */ |
| public int blueShift; |
| |
| /** |
| * Constructs a new indexed palette given an array of RGB values. |
| * |
| * @param colors the array of <code>RGB</code>s for the palette |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> |
| * </ul> |
| */ |
| public PaletteData(RGB[] colors) { |
| if (colors == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| this.colors = colors; |
| this.isDirect = false; |
| } |
| |
| /** |
| * Constructs a new direct palette given the red, green and blue masks. |
| * |
| * @param redMask the red mask |
| * @param greenMask the green mask |
| * @param blueMask the blue mask |
| */ |
| public PaletteData(int redMask, int greenMask, int blueMask) { |
| this.redMask = redMask; |
| this.greenMask = greenMask; |
| this.blueMask = blueMask; |
| this.isDirect = true; |
| this.redShift = shiftForMask(redMask); |
| this.greenShift = shiftForMask(greenMask); |
| this.blueShift = shiftForMask(blueMask); |
| } |
| |
| /** |
| * Returns the pixel value corresponding to the given <code>RBG</code>. |
| * |
| * @param rgb the RGB to get the pixel value for |
| * @return the pixel value for the given RGB |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if the RGB is not found in the palette</li> |
| * </ul> |
| */ |
| public int getPixel(RGB rgb) { |
| if (rgb == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| if (isDirect) { |
| int pixel = 0; |
| pixel |= (redShift < 0 ? rgb.red << -redShift : rgb.red >>> redShift) & redMask; |
| pixel |= (greenShift < 0 ? rgb.green << -greenShift : rgb.green >>> greenShift) & greenMask; |
| pixel |= (blueShift < 0 ? rgb.blue << -blueShift : rgb.blue >>> blueShift) & blueMask; |
| return pixel; |
| } else { |
| for (int i = 0; i < colors.length; i++) { |
| if (colors[i].equals(rgb)) return i; |
| } |
| /* The RGB did not exist in the palette */ |
| SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| return 0; |
| } |
| } |
| |
| /** |
| * Returns an <code>RGB</code> corresponding to the given pixel value. |
| * |
| * @param pixel the pixel to get the RGB value for |
| * @return the RGB value for the given pixel |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if the pixel does not exist in the palette</li> |
| * </ul> |
| */ |
| public RGB getRGB(int pixel) { |
| if (isDirect) { |
| int r = pixel & redMask; |
| r = (redShift < 0) ? r >>> -redShift : r << redShift; |
| int g = pixel & greenMask; |
| g = (greenShift < 0) ? g >>> -greenShift : g << greenShift; |
| int b = pixel & blueMask; |
| b = (blueShift < 0) ? b >>> -blueShift : b << blueShift; |
| return new RGB(r, g, b); |
| } else { |
| if (pixel < 0 || pixel >= colors.length) { |
| SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| } |
| return colors[pixel]; |
| } |
| } |
| |
| /** |
| * Returns all the RGB values in the receiver if it is an |
| * indexed palette, or null if it is a direct palette. |
| * |
| * @return the <code>RGB</code>s for the receiver or null |
| */ |
| public RGB[] getRGBs() { |
| return colors; |
| } |
| |
| /** |
| * Computes the shift value for a given mask. |
| * |
| * @param mask the mask to compute the shift for |
| * @return the shift amount |
| * |
| * @see PaletteData |
| */ |
| int shiftForMask(int mask) { |
| for (int i = 31; i >= 0; i--) { |
| if (((mask >> i) & 0x1) != 0) return 7 - i; |
| } |
| return 32; |
| } |
| |
| } |
| static class ImageLoader { |
| |
| /** |
| * the array of ImageData objects in this ImageLoader. |
| * This array is read in when the load method is called, |
| * and it is written out when the save method is called |
| */ |
| public ImageData[] data; |
| |
| /** |
| * the width of the logical screen on which the images |
| * reside, in pixels (this corresponds to the GIF89a |
| * Logical Screen Width value) |
| */ |
| public int logicalScreenWidth; |
| |
| /** |
| * the height of the logical screen on which the images |
| * reside, in pixels (this corresponds to the GIF89a |
| * Logical Screen Height value) |
| */ |
| public int logicalScreenHeight; |
| |
| /** |
| * the background pixel for the logical screen (this |
| * corresponds to the GIF89a Background Color Index value). |
| * The default is -1 which means 'unspecified background' |
| * |
| */ |
| public int backgroundPixel; |
| |
| /** |
| * the number of times to repeat the display of a sequence |
| * of animated images (this corresponds to the commonly-used |
| * GIF application extension for "NETSCAPE 2.0 01") |
| */ |
| public int repeatCount; |
| |
| /* |
| * the set of ImageLoader event listeners, created on demand |
| */ |
| Vector imageLoaderListeners; |
| |
| /** |
| * Construct a new empty ImageLoader. |
| */ |
| public ImageLoader() { |
| reset(); |
| } |
| |
| /** |
| * Resets the fields of the ImageLoader, except for the |
| * <code>imageLoaderListeners</code> field. |
| */ |
| void reset() { |
| data = null; |
| logicalScreenWidth = 0; |
| logicalScreenHeight = 0; |
| backgroundPixel = -1; |
| repeatCount = 1; |
| } |
| |
| /** |
| * Loads an array of <code>ImageData</code> objects from the |
| * specified input stream. Throws an error if either an error |
| * occurs while loading the images, or if the images are not |
| * of a supported type. Returns the loaded image data array. |
| * |
| * @param stream the input stream to load the images from |
| * @return an array of <code>ImageData</code> objects loaded from the specified input stream |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the stream is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li> |
| * <li>ERROR_IO - if an input/output error occurs while reading data</li> |
| * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li> |
| * </ul> |
| */ |
| public ImageData[] load(InputStream stream) { |
| if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| reset(); |
| data = FileFormat.load(stream, this); |
| return data; |
| } |
| |
| /** |
| * Loads an array of <code>ImageData</code> objects from the |
| * file with the specified name. Throws an error if either |
| * an error occurs while loading the images, or if the images are |
| * not of a supported type. Returns the loaded image data array. |
| * |
| * @param filename the name of the file to load the images from |
| * @return an array of <code>ImageData</code> objects loaded from the specified file |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the file name is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li> |
| * <li>ERROR_IO - if an IO error occurs while reading data</li> |
| * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li> |
| * </ul> |
| */ |
| public ImageData[] load(String filename) { |
| if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| InputStream stream = null; |
| try { |
| stream = new FileInputStream(filename); |
| return load(stream); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } finally { |
| try { |
| if (stream != null) stream.close(); |
| } catch (IOException e) { |
| // Ignore error |
| } |
| } |
| return null; |
| } |
| } |
| static class ImageData { |
| |
| /** |
| * The width of the image, in pixels. |
| */ |
| public int width; |
| |
| /** |
| * The height of the image, in pixels. |
| */ |
| public int height; |
| |
| /** |
| * The color depth of the image, in bits per pixel. |
| * <p> |
| * Note that a depth of 8 or less does not necessarily |
| * mean that the image is palette indexed, or |
| * conversely that a depth greater than 8 means that |
| * the image is direct color. Check the associated |
| * PaletteData's isDirect field for such determinations. |
| */ |
| public int depth; |
| |
| /** |
| * The scanline padding. |
| * <p> |
| * If one scanline of the image is not a multiple of |
| * this number, it will be padded with zeros until it is. |
| * </p> |
| */ |
| public int scanlinePad; |
| |
| /** |
| * The number of bytes per scanline. |
| * <p> |
| * This is a multiple of the scanline padding. |
| * </p> |
| */ |
| public int bytesPerLine; |
| |
| /** |
| * The pixel data of the image. |
| * <p> |
| * Note that for 16 bit depth images the pixel data is stored |
| * in least significant byte order; however, for 24bit and |
| * 32bit depth images the pixel data is stored in most |
| * significant byte order. |
| * </p> |
| */ |
| public byte[] data; |
| |
| /** |
| * The color table for the image. |
| */ |
| public PaletteData palette; |
| |
| /** |
| * The transparent pixel. |
| * <p> |
| * Pixels with this value are transparent. |
| * </p><p> |
| * The default is -1 which means 'no transparent pixel'. |
| * </p> |
| */ |
| public int transparentPixel; |
| |
| /** |
| * An icon-specific field containing the data from the icon mask. |
| * <p> |
| * This is a 1 bit bitmap stored with the most significant |
| * bit first. The number of bytes per scanline is |
| * '((width + 7) / 8 + (maskPad - 1)) / maskPad * maskPad'. |
| * </p><p> |
| * The default is null which means 'no transparency mask'. |
| * </p> |
| */ |
| public byte[] maskData; |
| |
| /** |
| * An icon-specific field containing the scanline pad of the mask. |
| * <p> |
| * If one scanline of the transparency mask is not a |
| * multiple of this number, it will be padded with zeros until |
| * it is. |
| * </p> |
| */ |
| public int maskPad; |
| |
| /** |
| * The alpha data of the image. |
| * <p> |
| * Every pixel can have an <em>alpha blending</em> value that |
| * varies from 0, meaning fully transparent, to 255 meaning |
| * fully opaque. The number of bytes per scanline is |
| * 'width'. |
| * </p> |
| */ |
| public byte[] alphaData; |
| |
| /** |
| * The global alpha value to be used for every pixel. |
| * <p> |
| * If this value is set, the <code>alphaData</code> field |
| * is ignored and when the image is rendered each pixel |
| * will be blended with the background an amount |
| * proportional to this value. |
| * </p><p> |
| * The default is -1 which means 'no global alpha value' |
| * </p> |
| */ |
| public int alpha; |
| |
| /** |
| * The type of file from which the image was read. |
| * |
| * It is expressed as one of the following values: |
| * <dl> |
| * <dt><code>IMAGE_BMP</code></dt> |
| * <dd>Windows BMP file format, no compression</dd> |
| * <dt><code>IMAGE_BMP_RLE</code></dt> |
| * <dd>Windows BMP file format, RLE compression if appropriate</dd> |
| * <dt><code>IMAGE_GIF</code></dt> |
| * <dd>GIF file format</dd> |
| * <dt><code>IMAGE_ICO</code></dt> |
| * <dd>Windows ICO file format</dd> |
| * <dt><code>IMAGE_JPEG</code></dt> |
| * <dd>JPEG file format</dd> |
| * <dt><code>IMAGE_PNG</code></dt> |
| * <dd>PNG file format</dd> |
| * </dl> |
| */ |
| public int type; |
| |
| /** |
| * The x coordinate of the top left corner of the image |
| * within the logical screen (this field corresponds to |
| * the GIF89a Image Left Position value). |
| */ |
| public int x; |
| |
| /** |
| * The y coordinate of the top left corner of the image |
| * within the logical screen (this field corresponds to |
| * the GIF89a Image Top Position value). |
| */ |
| public int y; |
| |
| /** |
| * A description of how to dispose of the current image |
| * before displaying the next. |
| * |
| * It is expressed as one of the following values: |
| * <dl> |
| * <dt><code>DM_UNSPECIFIED</code></dt> |
| * <dd>disposal method not specified</dd> |
| * <dt><code>DM_FILL_NONE</code></dt> |
| * <dd>do nothing - leave the image in place</dd> |
| * <dt><code>DM_FILL_BACKGROUND</code></dt> |
| * <dd>fill with the background color</dd> |
| * <dt><code>DM_FILL_PREVIOUS</code></dt> |
| * <dd>restore the previous picture</dd> |
| * </dl> |
| * (this field corresponds to the GIF89a Disposal Method value) |
| */ |
| public int disposalMethod; |
| |
| /** |
| * The time to delay before displaying the next image |
| * in an animation (this field corresponds to the GIF89a |
| * Delay Time value). |
| */ |
| public int delayTime; |
| |
| /** |
| * Arbitrary channel width data to 8-bit conversion table. |
| */ |
| static final byte[][] ANY_TO_EIGHT = new byte[9][]; |
| static { |
| for (int b = 0; b < 9; ++b) { |
| byte[] data = ANY_TO_EIGHT[b] = new byte[1 << b]; |
| if (b == 0) continue; |
| int inc = 0; |
| for (int bit = 0x10000; (bit >>= b) != 0;) inc |= bit; |
| for (int v = 0, p = 0; v < 0x10000; v+= inc) data[p++] = (byte)(v >> 8); |
| } |
| } |
| static final byte[] ONE_TO_ONE_MAPPING = ANY_TO_EIGHT[8]; |
| |
| /** |
| * Scaled 8x8 Bayer dither matrix. |
| */ |
| static final int[][] DITHER_MATRIX = { |
| { 0xfc0000, 0x7c0000, 0xdc0000, 0x5c0000, 0xf40000, 0x740000, 0xd40000, 0x540000 }, |
| { 0x3c0000, 0xbc0000, 0x1c0000, 0x9c0000, 0x340000, 0xb40000, 0x140000, 0x940000 }, |
| { 0xcc0000, 0x4c0000, 0xec0000, 0x6c0000, 0xc40000, 0x440000, 0xe40000, 0x640000 }, |
| { 0x0c0000, 0x8c0000, 0x2c0000, 0xac0000, 0x040000, 0x840000, 0x240000, 0xa40000 }, |
| { 0xf00000, 0x700000, 0xd00000, 0x500000, 0xf80000, 0x780000, 0xd80000, 0x580000 }, |
| { 0x300000, 0xb00000, 0x100000, 0x900000, 0x380000, 0xb80000, 0x180000, 0x980000 }, |
| { 0xc00000, 0x400000, 0xe00000, 0x600000, 0xc80000, 0x480000, 0xe80000, 0x680000 }, |
| { 0x000000, 0x800000, 0x200000, 0xa00000, 0x080000, 0x880000, 0x280000, 0xa80000 } |
| }; |
| |
| /** |
| * Constructs a new, empty ImageData with the given width, height, |
| * depth and palette. The data will be initialized to an (all zero) |
| * array of the appropriate size. |
| * |
| * @param width the width of the image |
| * @param height the height of the image |
| * @param depth the depth of the image |
| * @param palette the palette of the image (must not be null) |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not |
| * one of 1, 2, 4, 8, 16, 24 or 32</li> |
| * <li>ERROR_NULL_ARGUMENT - if the palette is null</li> |
| * </ul> |
| */ |
| public ImageData(int width, int height, int depth, PaletteData palette) { |
| this(width, height, depth, palette, |
| 4, null, 0, null, |
| null, -1, -1, SWT.IMAGE_UNDEFINED, |
| 0, 0, 0, 0); |
| } |
| |
| /** |
| * Constructs a new, empty ImageData with the given width, height, |
| * depth, palette, scanlinePad and data. |
| * |
| * @param width the width of the image |
| * @param height the height of the image |
| * @param depth the depth of the image |
| * @param palette the palette of the image |
| * @param scanlinePad the padding of each line, in bytes |
| * @param data the data of the image |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not |
| * one of 1, 2, 4, 8, 16, 24 or 32</li> |
| * <li>ERROR_NULL_ARGUMENT - if the palette or data is null</li> |
| * <li>ERROR_CANNOT_BE_ZERO - if the scanlinePad is zero</li> |
| * </ul> |
| */ |
| public ImageData(int width, int height, int depth, PaletteData palette, int scanlinePad, byte[] data) { |
| this(width, height, depth, palette, |
| scanlinePad, checkData(data), 0, null, |
| null, -1, -1, SWT.IMAGE_UNDEFINED, |
| 0, 0, 0, 0); |
| } |
| |
| /** |
| * Constructs an <code>ImageData</code> loaded from a file with the |
| * specified name. Throws an error if an error occurs loading the |
| * image, or if the image has an unsupported type. |
| * <p> |
| * This constructor is provided for convenience when loading a single |
| * image only. If the file contains multiple images, only the first |
| * one will be loaded. To load multiple images, use |
| * <code>ImageLoader.load()</code>. |
| * </p> |
| * |
| * @param filename the name of the file to load the image from (must not be null) |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the file name is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li> |
| * <li>ERROR_IO if an IO error occurs while reading data</li> |
| * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li> |
| * </ul> |
| */ |
| public ImageData(String filename) { |
| ImageData[] data = new ImageLoader().load(filename); |
| if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE); |
| ImageData i = data[0]; |
| setAllFields( |
| i.width, |
| i.height, |
| i.depth, |
| i.scanlinePad, |
| i.bytesPerLine, |
| i.data, |
| i.palette, |
| i.transparentPixel, |
| i.maskData, |
| i.maskPad, |
| i.alphaData, |
| i.alpha, |
| i.type, |
| i.x, |
| i.y, |
| i.disposalMethod, |
| i.delayTime); |
| } |
| |
| /** |
| * Prevents uninitialized instances from being created outside the package. |
| */ |
| ImageData() { |
| } |
| |
| /** |
| * Constructs an image data by giving values for all non-computable fields. |
| * <p> |
| * This method is for internal use, and is not described further. |
| * </p> |
| */ |
| ImageData( |
| int width, int height, int depth, PaletteData palette, |
| int scanlinePad, byte[] data, int maskPad, byte[] maskData, |
| byte[] alphaData, int alpha, int transparentPixel, int type, |
| int x, int y, int disposalMethod, int delayTime) |
| { |
| |
| if (palette == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| if (!(depth == 1 || depth == 2 || depth == 4 || depth == 8 |
| || depth == 16 || depth == 24 || depth == 32)) { |
| SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| } |
| if (width <= 0 || height <= 0) { |
| SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| } |
| if (scanlinePad == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO); |
| |
| int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1)) |
| / scanlinePad * scanlinePad; |
| setAllFields( |
| width, |
| height, |
| depth, |
| scanlinePad, |
| bytesPerLine, |
| data != null ? data : new byte[bytesPerLine * height], |
| palette, |
| transparentPixel, |
| maskData, |
| maskPad, |
| alphaData, |
| alpha, |
| type, |
| x, |
| y, |
| disposalMethod, |
| delayTime); |
| } |
| |
| /** |
| * Initializes all fields in the receiver. This method must be called |
| * by all public constructors to ensure that all fields are initialized |
| * for a new ImageData object. If a new field is added to the class, |
| * then it must be added to this method. |
| * <p> |
| * This method is for internal use, and is not described further. |
| * </p> |
| */ |
| void setAllFields(int width, int height, int depth, int scanlinePad, |
| int bytesPerLine, byte[] data, PaletteData palette, int transparentPixel, |
| byte[] maskData, int maskPad, byte[] alphaData, int alpha, |
| int type, int x, int y, int disposalMethod, int delayTime) { |
| |
| this.width = width; |
| this.height = height; |
| this.depth = depth; |
| this.scanlinePad = scanlinePad; |
| this.bytesPerLine = bytesPerLine; |
| this.data = data; |
| this.palette = palette; |
| this.transparentPixel = transparentPixel; |
| this.maskData = maskData; |
| this.maskPad = maskPad; |
| this.alphaData = alphaData; |
| this.alpha = alpha; |
| this.type = type; |
| this.x = x; |
| this.y = y; |
| this.disposalMethod = disposalMethod; |
| this.delayTime = delayTime; |
| } |
| |
| /** |
| * Invokes internal SWT functionality to create a new instance of |
| * this class. |
| * <p> |
| * <b>IMPORTANT:</b> This method is <em>not</em> part of the public |
| * API for <code>ImageData</code>. It is marked public only so that it |
| * can be shared within the packages provided by SWT. It is subject |
| * to change without notice, and should never be called from |
| * application code. |
| * </p> |
| * <p> |
| * This method is for internal use, and is not described further. |
| * </p> |
| */ |
| public static ImageData internal_new( |
| int width, int height, int depth, PaletteData palette, |
| int scanlinePad, byte[] data, int maskPad, byte[] maskData, |
| byte[] alphaData, int alpha, int transparentPixel, int type, |
| int x, int y, int disposalMethod, int delayTime) |
| { |
| return new ImageData( |
| width, height, depth, palette, scanlinePad, data, maskPad, maskData, |
| alphaData, alpha, transparentPixel, type, x, y, disposalMethod, delayTime); |
| } |
| |
| ImageData colorMaskImage(int pixel) { |
| ImageData mask = new ImageData(width, height, 1, bwPalette(), |
| 2, null, 0, null, null, -1, -1, SWT.IMAGE_UNDEFINED, |
| 0, 0, 0, 0); |
| int[] row = new int[width]; |
| for (int y = 0; y < height; y++) { |
| getPixels(0, y, width, row, 0); |
| for (int i = 0; i < width; i++) { |
| if (pixel != -1 && row[i] == pixel) { |
| row[i] = 0; |
| } else { |
| row[i] = 1; |
| } |
| } |
| mask.setPixels(0, y, width, row, 0); |
| } |
| return mask; |
| } |
| |
| static byte[] checkData(byte [] data) { |
| if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| return data; |
| } |
| |
| /** |
| * Returns <code>getWidth</code> pixel values starting at offset |
| * <code>x</code> in scanline <code>y</code> in the receiver's |
| * data starting at <code>startIndex</code>. |
| * |
| * @param x the x position of the first pixel to get |
| * @param y the y position of the first pixel to get |
| * @param getWidth the width of the data to get |
| * @param pixels the buffer in which to put the pixels |
| * @param startIndex the offset into the byte array to begin storing pixels |
| * |
| * @exception IndexOutOfBoundsException if getWidth is too large |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if pixels is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li> |
| * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4 or 8 |
| * (For higher depths, use the int[] version of this method.)</li> |
| * </ul> |
| */ |
| public void getPixels(int x, int y, int getWidth, byte[] pixels, int startIndex) { |
| if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error |
| |
| (SWT.ERROR_INVALID_ARGUMENT); |
| if (getWidth == 0) return; |
| int index; |
| int theByte; |
| int mask = 0; |
| int n = getWidth; |
| int i = startIndex; |
| int srcX = x, srcY = y; |
| if (depth == 1) { |
| index = (y * bytesPerLine) + (x >> 3); |
| theByte = data[index] & 0xFF; |
| while (n > 0) { |
| mask = 1 << (7 - (srcX & 0x7)); |
| if ((theByte & mask) == 0) { |
| pixels[i] = 0; |
| } else { |
| pixels[i] = 1; |
| } |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| if (n > 0) theByte = data[index] & 0xFF; |
| srcX = 0; |
| } else { |
| if (mask == 1) { |
| index++; |
| if (n > 0) theByte = data[index] & 0xFF; |
| } |
| } |
| } |
| return; |
| } |
| if (depth == 2) { |
| index = (y * bytesPerLine) + (x >> 2); |
| theByte = data[index] & 0xFF; |
| int offset; |
| while (n > 0) { |
| offset = 3 - (srcX % 4); |
| mask = 3 << (offset * 2); |
| pixels[i] = (byte)((theByte & mask) >> (offset * 2)); |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| if (n > 0) theByte = data[index] & 0xFF; |
| srcX = 0; |
| } else { |
| if (offset == 0) { |
| index++; |
| theByte = data[index] & 0xFF; |
| } |
| } |
| } |
| return; |
| } |
| if (depth == 4) { |
| index = (y * bytesPerLine) + (x >> 1); |
| if ((x & 0x1) == 1) { |
| theByte = data[index] & 0xFF; |
| pixels[i] = (byte)(theByte & 0x0F); |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index++; |
| } |
| } |
| while (n > 1) { |
| theByte = data[index] & 0xFF; |
| pixels[i] = (byte)(theByte >> 4); |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| pixels[i] = (byte)(theByte & 0x0F); |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index++; |
| } |
| } |
| } |
| if (n > 0) { |
| theByte = data[index] & 0xFF; |
| pixels[i] = (byte)(theByte >> 4); |
| } |
| return; |
| } |
| if (depth == 8) { |
| index = (y * bytesPerLine) + x; |
| for (int j = 0; j < getWidth; j++) { |
| pixels[i] = data[index]; |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index++; |
| } |
| } |
| return; |
| } |
| SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); |
| } |
| |
| /** |
| * Returns <code>getWidth</code> pixel values starting at offset |
| * <code>x</code> in scanline <code>y</code> in the receiver's |
| * data starting at <code>startIndex</code>. |
| * |
| * @param x the x position of the first pixel to get |
| * @param y the y position of the first pixel to get |
| * @param getWidth the width of the data to get |
| * @param pixels the buffer in which to put the pixels |
| * @param startIndex the offset into the buffer to begin storing pixels |
| * |
| * @exception IndexOutOfBoundsException if getWidth is too large |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if pixels is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li> |
| * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li> |
| * </ul> |
| */ |
| public void getPixels(int x, int y, int getWidth, int[] pixels, int startIndex) { |
| if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error |
| |
| (SWT.ERROR_INVALID_ARGUMENT); |
| if (getWidth == 0) return; |
| int index; |
| int theByte; |
| int mask; |
| int n = getWidth; |
| int i = startIndex; |
| int srcX = x, srcY = y; |
| if (depth == 1) { |
| index = (y * bytesPerLine) + (x >> 3); |
| theByte = data[index] & 0xFF; |
| while (n > 0) { |
| mask = 1 << (7 - (srcX & 0x7)); |
| if ((theByte & mask) == 0) { |
| pixels[i] = 0; |
| } else { |
| pixels[i] = 1; |
| } |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| if (n > 0) theByte = data[index] & 0xFF; |
| srcX = 0; |
| } else { |
| if (mask == 1) { |
| index++; |
| if (n > 0) theByte = data[index] & 0xFF; |
| } |
| } |
| } |
| return; |
| } |
| if (depth == 2) { |
| index = (y * bytesPerLine) + (x >> 2); |
| theByte = data[index] & 0xFF; |
| int offset; |
| while (n > 0) { |
| offset = 3 - (srcX % 4); |
| mask = 3 << (offset * 2); |
| pixels[i] = (byte)((theByte & mask) >> (offset * 2)); |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| if (n > 0) theByte = data[index] & 0xFF; |
| srcX = 0; |
| } else { |
| if (offset == 0) { |
| index++; |
| theByte = data[index] & 0xFF; |
| } |
| } |
| } |
| return; |
| } |
| if (depth == 4) { |
| index = (y * bytesPerLine) + (x >> 1); |
| if ((x & 0x1) == 1) { |
| theByte = data[index] & 0xFF; |
| pixels[i] = theByte & 0x0F; |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index++; |
| } |
| } |
| while (n > 1) { |
| theByte = data[index] & 0xFF; |
| pixels[i] = theByte >> 4; |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| pixels[i] = theByte & 0x0F; |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index++; |
| } |
| } |
| } |
| if (n > 0) { |
| theByte = data[index] & 0xFF; |
| pixels[i] = theByte >> 4; |
| } |
| return; |
| } |
| if (depth == 8) { |
| index = (y * bytesPerLine) + x; |
| for (int j = 0; j < getWidth; j++) { |
| pixels[i] = data[index] & 0xFF; |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index++; |
| } |
| } |
| return; |
| } |
| if (depth == 16) { |
| index = (y * bytesPerLine) + (x * 2); |
| for (int j = 0; j < getWidth; j++) { |
| pixels[i] = ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF); |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index += 2; |
| } |
| } |
| return; |
| } |
| if (depth == 24) { |
| index = (y * bytesPerLine) + (x * 3); |
| for (int j = 0; j < getWidth; j++) { |
| pixels[i] = ((data[index] & 0xFF) << 16) | ((data[index+1] & 0xFF) << 8) |
| | (data[index+2] & 0xFF); |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index += 3; |
| } |
| } |
| return; |
| } |
| if (depth == 32) { |
| index = (y * bytesPerLine) + (x * 4); |
| i = startIndex; |
| for (int j = 0; j < getWidth; j++) { |
| pixels[i] = ((data[index] & 0xFF) << 24) | ((data[index+1] & 0xFF) << 16) |
| | ((data[index+2] & 0xFF) << 8) | (data[index+3] & 0xFF); |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index += 4; |
| } |
| } |
| return; |
| } |
| SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); |
| } |
| |
| /** |
| * Returns an array of <code>RGB</code>s which comprise the |
| * indexed color table of the receiver, or null if the receiver |
| * has a direct color model. |
| * |
| * @return the RGB values for the image or null if direct color |
| * |
| * @see PaletteData#getRGBs() |
| */ |
| public RGB[] getRGBs() { |
| return palette.getRGBs(); |
| } |
| |
| /** |
| * Returns an <code>ImageData</code> which specifies the |
| * transparency mask information for the receiver, or null if the |
| * receiver has no transparency and is not an icon. |
| * |
| * @return the transparency mask or null if none exists |
| */ |
| public ImageData getTransparencyMask() { |
| if (getTransparencyType() == SWT.TRANSPARENCY_MASK) { |
| return new ImageData(width, height, 1, bwPalette(), maskPad, maskData); |
| } else { |
| return colorMaskImage(transparentPixel); |
| } |
| } |
| |
| /** |
| * Returns the image transparency type. |
| * |
| * @return the receiver's transparency type |
| */ |
| public int getTransparencyType() { |
| if (maskData != null) return SWT.TRANSPARENCY_MASK; |
| if (transparentPixel != -1) return SWT.TRANSPARENCY_PIXEL; |
| if (alphaData != null) return SWT.TRANSPARENCY_ALPHA; |
| return SWT.TRANSPARENCY_NONE; |
| } |
| |
| /** |
| * Returns the byte order of the receiver. |
| * |
| * @return MSB_FIRST or LSB_FIRST |
| */ |
| int getByteOrder() { |
| return depth != 16 ? MSB_FIRST : LSB_FIRST; |
| } |
| |
| /** |
| * Sets the pixel values starting at offset <code>x</code> in |
| * scanline <code>y</code> in the receiver's data to the |
| * values from the array <code>pixels</code> starting at |
| * <code>startIndex</code>. |
| * |
| * @param x the x position of the pixel to set |
| * @param y the y position of the pixel to set |
| * @param putWidth the width of the pixels to set |
| * @param pixels the pixels to set |
| * @param startIndex the index at which to begin setting |
| * |
| * @exception IndexOutOfBoundsException if putWidth is too large |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if pixels is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li> |
| * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8 |
| * (For higher depths, use the int[] version of this method.)</li> |
| * </ul> |
| */ |
| public void setPixels(int x, int y, int putWidth, byte[] pixels, int startIndex) { |
| if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| if (putWidth == 0) return; |
| int index; |
| int theByte; |
| int mask; |
| int n = putWidth; |
| int i = startIndex; |
| int srcX = x, srcY = y; |
| if (depth == 1) { |
| index = (y * bytesPerLine) + (x >> 3); |
| while (n > 0) { |
| mask = 1 << (7 - (srcX & 0x7)); |
| if ((pixels[i] & 0x1) == 1) { |
| data[index] = (byte)((data[index] & 0xFF) | mask); |
| } else { |
| data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1)); |
| } |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| if (mask == 1) { |
| index++; |
| } |
| } |
| } |
| return; |
| } |
| if (depth == 2) { |
| byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F }; |
| index = (y * bytesPerLine) + (x >> 2); |
| int offset = 3 - (x % 4); |
| while (n > 0) { |
| theByte = pixels[i] & 0x3; |
| data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2))); |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| offset = 0; |
| srcX = 0; |
| } else { |
| if (offset == 0) { |
| index++; |
| offset = 3; |
| } else { |
| offset--; |
| } |
| } |
| } |
| return; |
| } |
| if (depth == 4) { |
| index = (y * bytesPerLine) + (x >> 1); |
| boolean high = (x & 0x1) == 0; |
| while (n > 0) { |
| theByte = pixels[i] & 0x0F; |
| if (high) { |
| data[index] = (byte)((data[index] & 0x0F) | (theByte << 4)); |
| } else { |
| data[index] = (byte)((data[index] & 0xF0) | theByte); |
| } |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| high = true; |
| srcX = 0; |
| } else { |
| if (!high) index++; |
| high = !high; |
| } |
| } |
| return; |
| } |
| if (depth == 8) { |
| index = (y * bytesPerLine) + x; |
| for (int j = 0; j < putWidth; j++) { |
| data[index] = (byte)(pixels[i] & 0xFF); |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index++; |
| } |
| } |
| return; |
| } |
| SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); |
| } |
| |
| /** |
| * Sets the pixel values starting at offset <code>x</code> in |
| * scanline <code>y</code> in the receiver's data to the |
| * values from the array <code>pixels</code> starting at |
| * <code>startIndex</code>. |
| * |
| * @param x the x position of the pixel to set |
| * @param y the y position of the pixel to set |
| * @param putWidth the width of the pixels to set |
| * @param pixels the pixels to set |
| * @param startIndex the index at which to begin setting |
| * |
| * @exception IndexOutOfBoundsException if putWidth is too large |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if pixels is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li> |
| * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li> |
| * </ul> |
| */ |
| public void setPixels(int x, int y, int putWidth, int[] pixels, int startIndex) { |
| if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| if (putWidth == 0) return; |
| int index; |
| int theByte; |
| int mask; |
| int n = putWidth; |
| int i = startIndex; |
| int pixel; |
| int srcX = x, srcY = y; |
| if (depth == 1) { |
| index = (y * bytesPerLine) + (x >> 3); |
| while (n > 0) { |
| mask = 1 << (7 - (srcX & 0x7)); |
| if ((pixels[i] & 0x1) == 1) { |
| data[index] = (byte)((data[index] & 0xFF) | mask); |
| } else { |
| data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1)); |
| } |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| if (mask == 1) { |
| index++; |
| } |
| } |
| } |
| return; |
| } |
| if (depth == 2) { |
| byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F }; |
| index = (y * bytesPerLine) + (x >> 2); |
| int offset = 3 - (x % 4); |
| while (n > 0) { |
| theByte = pixels[i] & 0x3; |
| data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2))); |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| offset = 3; |
| srcX = 0; |
| } else { |
| if (offset == 0) { |
| index++; |
| offset = 3; |
| } else { |
| offset--; |
| } |
| } |
| } |
| return; |
| } |
| if (depth == 4) { |
| index = (y * bytesPerLine) + (x >> 1); |
| boolean high = (x & 0x1) == 0; |
| while (n > 0) { |
| theByte = pixels[i] & 0x0F; |
| if (high) { |
| data[index] = (byte)((data[index] & 0x0F) | (theByte << 4)); |
| } else { |
| data[index] = (byte)((data[index] & 0xF0) | theByte); |
| } |
| i++; |
| n--; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| high = true; |
| srcX = 0; |
| } else { |
| if (!high) index++; |
| high = !high; |
| } |
| } |
| return; |
| } |
| if (depth == 8) { |
| index = (y * bytesPerLine) + x; |
| for (int j = 0; j < putWidth; j++) { |
| data[index] = (byte)(pixels[i] & 0xFF); |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index++; |
| } |
| } |
| return; |
| |
| } |
| if (depth == 16) { |
| index = (y * bytesPerLine) + (x * 2); |
| for (int j = 0; j < putWidth; j++) { |
| pixel = pixels[i]; |
| data[index] = (byte)(pixel & 0xFF); |
| data[index + 1] = (byte)((pixel >> 8) & 0xFF); |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index += 2; |
| } |
| } |
| return; |
| } |
| if (depth == 24) { |
| index = (y * bytesPerLine) + (x * 3); |
| for (int j = 0; j < putWidth; j++) { |
| pixel = pixels[i]; |
| data[index] = (byte)((pixel >> 16) & 0xFF); |
| data[index + 1] = (byte)((pixel >> 8) & 0xFF); |
| data[index + 2] = (byte)(pixel & 0xFF); |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index += 3; |
| } |
| } |
| return; |
| } |
| if (depth == 32) { |
| index = (y * bytesPerLine) + (x * 4); |
| for (int j = 0; j < putWidth; j++) { |
| pixel = pixels[i]; |
| data[index] = (byte)((pixel >> 24) & 0xFF); |
| data[index + 1] = (byte)((pixel >> 16) & 0xFF); |
| data[index + 2] = (byte)((pixel >> 8) & 0xFF); |
| data[index + 3] = (byte)(pixel & 0xFF); |
| i++; |
| srcX++; |
| if (srcX >= width) { |
| srcY++; |
| index = srcY * bytesPerLine; |
| srcX = 0; |
| } else { |
| index += 4; |
| } |
| } |
| return; |
| } |
| SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); |
| } |
| |
| /** |
| * Returns a palette with 2 colors: black & white. |
| */ |
| static PaletteData bwPalette() { |
| return new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255, 255, 255)}); |
| } |
| |
| /** |
| * Gets the offset of the most significant bit for |
| * the given mask. |
| */ |
| static int getMSBOffset(int mask) { |
| for (int i = 31; i >= 0; i--) { |
| if (((mask >> i) & 0x1) != 0) return i + 1; |
| } |
| return 0; |
| } |
| |
| /** |
| * Finds the closest match. |
| */ |
| static int closestMatch(int depth, byte red, byte green, byte blue, int redMask, int greenMask, int blueMask, byte[] reds, byte[] greens, byte[] blues) { |
| if (depth > 8) { |
| int rshift = 32 - getMSBOffset(redMask); |
| int gshift = 32 - getMSBOffset(greenMask); |
| int bshift = 32 - getMSBOffset(blueMask); |
| return (((red << 24) >>> rshift) & redMask) | |
| (((green << 24) >>> gshift) & greenMask) | |
| (((blue << 24) >>> bshift) & blueMask); |
| } |
| int r, g, b; |
| int minDistance = 0x7fffffff; |
| int nearestPixel = 0; |
| int n = reds.length; |
| for (int j = 0; j < n; j++) { |
| r = (reds[j] & 0xFF) - (red & 0xFF); |
| g = (greens[j] & 0xFF) - (green & 0xFF); |
| b = (blues[j] & 0xFF) - (blue & 0xFF); |
| int distance = r*r + g*g + b*b; |
| if (distance < minDistance) { |
| nearestPixel = j; |
| if (distance == 0) break; |
| minDistance = distance; |
| } |
| } |
| return nearestPixel; |
| } |
| |
| static final ImageData convertMask(ImageData mask) { |
| if (mask.depth == 1) return mask; |
| PaletteData palette = new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255,255,255)}); |
| ImageData newMask = new ImageData(mask.width, mask.height, 1, palette); |
| /* Find index of black in mask palette */ |
| int blackIndex = 0; |
| RGB[] rgbs = mask.getRGBs(); |
| if (rgbs != null) { |
| while (blackIndex < rgbs.length) { |
| if (rgbs[blackIndex].equals(palette.colors[0])) break; |
| blackIndex++; |
| } |
| } |
| int[] pixels = new int[mask.width]; |
| for (int y = 0; y < mask.height; y++) { |
| mask.getPixels(0, y, mask.width, pixels, 0); |
| for (int i = 0; i < pixels.length; i++) { |
| if (pixels[i] == blackIndex) { |
| pixels[i] = 0; |
| } else { |
| pixels[i] = 1; |
| } |
| } |
| newMask.setPixels(0, y, mask.width, pixels, 0); |
| } |
| return newMask; |
| } |
| |
| static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) { |
| if (pad == newPad) return data; |
| int stride = (width * depth + 7) / 8; |
| int bpl = (stride + (pad - 1)) / pad * pad; |
| int newBpl = (stride + (newPad - 1)) / newPad * newPad; |
| byte[] newData = new byte[height * newBpl]; |
| int srcIndex = 0, destIndex = 0; |
| for (int y = 0; y < height; y++) { |
| System.arraycopy(data, srcIndex, newData, destIndex, stride); |
| srcIndex += bpl; |
| destIndex += newBpl; |
| } |
| return newData; |
| } |
| |
| /** |
| * Blit operation bits to be OR'ed together to specify the desired operation. |
| */ |
| static final int |
| BLIT_SRC = 1, // copy source directly, else applies logic operations |
| BLIT_ALPHA = 2, // enable alpha blending |
| BLIT_DITHER = 4; // enable dithering in low color modes |
| |
| /** |
| * Alpha mode, values 0 - 255 specify global alpha level |
| */ |
| static final int |
| ALPHA_OPAQUE = 255, // Fully opaque (ignores any alpha data) |
| ALPHA_TRANSPARENT = 0, // Fully transparent (ignores any alpha data) |
| ALPHA_CHANNEL_SEPARATE = -1, // Use alpha channel from separate alphaData |
| ALPHA_CHANNEL_SOURCE = -2, // Use alpha channel embedded in sourceData |
| ALPHA_MASK_UNPACKED = -3, // Use transparency mask formed by bytes in alphaData (non-zero is opaque) |
| ALPHA_MASK_PACKED = -4, // Use transparency mask formed by packed bits in alphaData |
| ALPHA_MASK_INDEX = -5, // Consider source palette indices transparent if in alphaData array |
| ALPHA_MASK_RGB = -6; // Consider source RGBs transparent if in RGB888 format alphaData array |
| |
| /** |
| * Byte and bit order constants. |
| */ |
| static final int LSB_FIRST = 0; |
| static final int MSB_FIRST = 1; |
| |
| /** |
| * Data types (internal) |
| */ |
| private static final int |
| // direct / true color formats with arbitrary masks & shifts |
| TYPE_GENERIC_8 = 0, |
| TYPE_GENERIC_16_MSB = 1, |
| TYPE_GENERIC_16_LSB = 2, |
| TYPE_GENERIC_24 = 3, |
| TYPE_GENERIC_32_MSB = 4, |
| TYPE_GENERIC_32_LSB = 5, |
| // palette indexed color formats |
| TYPE_INDEX_8 = 6, |
| TYPE_INDEX_4 = 7, |
| TYPE_INDEX_2 = 8, |
| TYPE_INDEX_1_MSB = 9, |
| TYPE_INDEX_1_LSB = 10; |
| |
| /** |
| * Computes the required channel shift from a mask. |
| */ |
| static int getChannelShift(int mask) { |
| if (mask == 0) return 0; |
| int i; |
| for (i = 0; ((mask & 1) == 0) && (i < 32); ++i) { |
| mask >>>= 1; |
| } |
| return i; |
| } |
| |
| /** |
| * Computes the required channel width (depth) from a mask. |
| */ |
| static int getChannelWidth(int mask, int shift) { |
| if (mask == 0) return 0; |
| int i; |
| mask >>>= shift; |
| for (i = shift; ((mask & 1) != 0) && (i < 32); ++i) { |
| mask >>>= 1; |
| } |
| return i - shift; |
| } |
| |
| /** |
| * Extracts a field from packed RGB data given a mask for that field. |
| */ |
| static byte getChannelField(int data, int mask) { |
| final int shift = getChannelShift(mask); |
| return ANY_TO_EIGHT[getChannelWidth(mask, shift)][(data & mask) >>> shift]; |
| } |
| |
| /* |
| * Fill in dithered gradated values for a color channel |
| */ |
| static final void buildDitheredGradientChannel(int from, int to, int steps, |
| int bandWidth, int bandHeight, boolean vertical, |
| byte[] bitmapData, int dp, int bytesPerLine, int bits) { |
| final int mask = 0xff00 >>> bits; |
| int val = from << 16; |
| final int inc = ((to << 16) - val) / steps + 1; |
| if (vertical) { |
| for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) { |
| for (int dx = 0, dptr = dp; dx < bandWidth; ++dx, dptr += 4) { |
| final int thresh = DITHER_MATRIX[dy & 7][dx] >>> bits; |
| int temp = val + thresh; |
| if (temp > 0xffffff) bitmapData[dptr] = -1; |
| else bitmapData[dptr] = (byte)((temp >>> 16) & mask); |
| } |
| val += inc; |
| } |
| } else { |
| for (int dx = 0; dx < bandWidth; ++dx, dp += 4) { |
| for (int dy = 0, dptr = dp; dy < bandHeight; ++dy, dptr += bytesPerLine) { |
| final int thresh = DITHER_MATRIX[dy][dx & 7] >>> bits; |
| int temp = val + thresh; |
| if (temp > 0xffffff) bitmapData[dptr] = -1; |
| else bitmapData[dptr] = (byte)((temp >>> 16) & mask); |
| } |
| val += inc; |
| } |
| } |
| } |
| } |
| |
| static class LEDataInputStream extends InputStream { |
| int position; |
| InputStream in; |
| |
| /** |
| * The byte array containing the bytes to read. |
| */ |
| protected byte[] buf; |
| |
| /** |
| * The current position within the byte array <code>buf</code>. A value |
| * equal to buf.length indicates no bytes available. A value of |
| * 0 indicates the buffer is full. |
| */ |
| protected int pos; |
| |
| |
| public LEDataInputStream(InputStream input) { |
| this(input, 512); |
| } |
| |
| public LEDataInputStream(InputStream input, int bufferSize) { |
| this.in = input; |
| if (bufferSize > 0) { |
| buf = new byte[bufferSize]; |
| pos = bufferSize; |
| } |
| else throw new IllegalArgumentException(); |
| } |
| |
| public void close() throws IOException { |
| buf = null; |
| if (in != null) { |
| in.close(); |
| in = null; |
| } |
| } |
| |
| /** |
| * Answer how many bytes were read. |
| */ |
| public int getPosition() { |
| return position; |
| } |
| |
| /** |
| * Answers how many bytes are available for reading without blocking |
| */ |
| public int available() throws IOException { |
| if (buf == null) throw new IOException(); |
| return (buf.length - pos) + in.available(); |
| } |
| |
| /** |
| * Answer the next byte of the input stream. |
| */ |
| public int read() throws IOException { |
| if (buf == null) throw new IOException(); |
| position++; |
| if (pos < buf.length) return (buf[pos++] & 0xFF); |
| return in.read(); |
| } |
| |
| /** |
| * Don't imitate the JDK behaviour of reading a random number |
| * of bytes when you can actually read them all. |
| */ |
| public int read(byte b[], int off, int len) throws IOException { |
| int result; |
| int left = len; |
| result = readData(b, off, len); |
| while (true) { |
| if (result == -1) return -1; |
| position += result; |
| if (result == left) return len; |
| left -= result; |
| off += result; |
| result = readData(b, off, left); |
| } |
| } |
| |
| /** |
| * Reads at most <code>length</code> bytes from this LEDataInputStream and |
| * stores them in byte array <code>buffer</code> starting at <code>offset</code>. |
| * <p> |
| * Answer the number of bytes actually read or -1 if no bytes were read and |
| * end of stream was encountered. This implementation reads bytes from |
| * the pushback buffer first, then the target stream if more bytes are required |
| * to satisfy <code>count</code>. |
| * </p> |
| * @param buffer the byte array in which to store the read bytes. |
| * @param offset the offset in <code>buffer</code> to store the read bytes. |
| * @param length the maximum number of bytes to store in <code>buffer</code>. |
| * |
| * @return int the number of bytes actually read or -1 if end of stream. |
| * |
| * @exception java.io.IOException if an IOException occurs. |
| */ |
| private int readData(byte[] buffer, int offset, int length) throws IOException { |
| if (buf == null) throw new IOException(); |
| if (offset < 0 || offset > buffer.length || |
| length < 0 || (length > buffer.length - offset)) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| |
| int cacheCopied = 0; |
| int newOffset = offset; |
| |
| // Are there pushback bytes available? |
| int available = buf.length - pos; |
| if (available > 0) { |
| cacheCopied = (available >= length) ? length : available; |
| System.arraycopy(buf, pos, buffer, newOffset, cacheCopied); |
| newOffset += cacheCopied; |
| pos += cacheCopied; |
| } |
| |
| // Have we copied enough? |
| if (cacheCopied == length) return length; |
| |
| int inCopied = in.read(buffer, newOffset, length - cacheCopied); |
| |
| if (inCopied > 0) return inCopied + cacheCopied; |
| if (cacheCopied == 0) return inCopied; |
| return cacheCopied; |
| } |
| |
| /** |
| * Answer an integer comprised of the next |
| * four bytes of the input stream. |
| */ |
| public int readInt() throws IOException { |
| byte[] buf = new byte[4]; |
| read(buf); |
| return ((((((buf[3] & 0xFF) << 8) | |
| (buf[2] & 0xFF)) << 8) | |
| (buf[1] & 0xFF)) << 8) | |
| (buf[0] & 0xFF); |
| } |
| |
| /** |
| * Answer a short comprised of the next |
| * two bytes of the input stream. |
| */ |
| public short readShort() throws IOException { |
| byte[] buf = new byte[2]; |
| read(buf); |
| return (short)(((buf[1] & 0xFF) << 8) | (buf[0] & 0xFF)); |
| } |
| |
| /** |
| * Push back the entire content of the given buffer <code>b</code>. |
| * <p> |
| * The bytes are pushed so that they would be read back b[0], b[1], etc. |
| * If the push back buffer cannot handle the bytes copied from <code>b</code>, |
| * an IOException will be thrown and no byte will be pushed back. |
| * </p> |
| * |
| * @param b the byte array containing bytes to push back into the stream |
| * |
| * @exception java.io.IOException if the pushback buffer is too small |
| */ |
| public void unread(byte[] b) throws IOException { |
| int length = b.length; |
| if (length > pos) throw new IOException(); |
| position -= length; |
| pos -= length; |
| System.arraycopy(b, 0, buf, pos, length); |
| } |
| } |
| public static abstract class FileFormat { |
| LEDataInputStream inputStream; |
| ImageLoader loader; |
| int compression; |
| |
| byte[] bitInvertData(byte[] data, int startIndex, int endIndex) { |
| // Destructively bit invert data in the given byte array. |
| for (int i = startIndex; i < endIndex; i++) { |
| data[i] = (byte)(255 - data[i - startIndex]); |
| } |
| return data; |
| } |
| |
| /** |
| * Return whether or not the specified input stream |
| * represents a supported file format. |
| */ |
| abstract boolean isFileFormat(LEDataInputStream stream); |
| |
| abstract ImageData[] loadFromByteStream(); |
| |
| public ImageData[] loadFromStream(LEDataInputStream stream) { |
| try { |
| inputStream = stream; |
| return loadFromByteStream(); |
| } catch (Exception e) { |
| SWT.error(SWT.ERROR_IO, e); |
| return null; |
| } |
| } |
| |
| public static ImageData[] load(InputStream is, ImageLoader loader) { |
| LEDataInputStream stream = new LEDataInputStream(is); |
| boolean isSupported = false; |
| FileFormat fileFormat = new WinICOFileFormat(); |
| if (fileFormat.isFileFormat(stream)) isSupported = true; |
| else { |
| fileFormat = new WinBMPFileFormat(); |
| if (fileFormat.isFileFormat(stream)) isSupported = true; |
| } |
| if (!isSupported) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); |
| fileFormat.loader = loader; |
| return fileFormat.loadFromStream(stream); |
| } |
| } |
| static class WinBMPFileFormat extends FileFormat { |
| static final int BMPFileHeaderSize = 14; |
| static final int BMPHeaderFixedSize = 40; |
| int importantColors; |
| |
| void decompressData(byte[] src, byte[] dest, int stride, int cmp) { |
| if (cmp == 1) { // BMP_RLE8_COMPRESSION |
| if (decompressRLE8Data(src, src.length, stride, dest, dest.length) <= 0) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| return; |
| } |
| if (cmp == 2) { // BMP_RLE4_COMPRESSION |
| if (decompressRLE4Data(src, src.length, stride, dest, dest.length) <= 0) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| return; |
| } |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| } |
| int decompressRLE4Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) { |
| int sp = 0; |
| int se = numBytes; |
| int dp = 0; |
| int de = destSize; |
| int x = 0, y = 0; |
| while (sp < se) { |
| int len = src[sp] & 0xFF; |
| sp++; |
| if (len == 0) { |
| len = src[sp] & 0xFF; |
| sp++; |
| switch (len) { |
| case 0: /* end of line */ |
| y++; |
| x = 0; |
| dp = y * stride; |
| if (dp >= de) |
| return -1; |
| break; |
| case 1: /* end of bitmap */ |
| return 1; |
| case 2: /* delta */ |
| x += src[sp] & 0xFF; |
| sp++; |
| y += src[sp] & 0xFF; |
| sp++; |
| dp = y * stride + x / 2; |
| if (dp >= de) |
| return -1; |
| break; |
| default: /* absolute mode run */ |
| if ((len & 1) != 0) /* odd run lengths not currently supported */ |
| return -1; |
| x += len; |
| len = len / 2; |
| if (len > (se - sp)) |
| return -1; |
| if (len > (de - dp)) |
| return -1; |
| for (int i = 0; i < len; i++) { |
| dest[dp] = src[sp]; |
| dp++; |
| sp++; |
| } |
| if ((sp & 1) != 0) |
| sp++; /* word align sp? */ |
| break; |
| } |
| } else { |
| if ((len & 1) != 0) |
| return -1; |
| x += len; |
| len = len / 2; |
| byte theByte = src[sp]; |
| sp++; |
| if (len > (de - dp)) |
| return -1; |
| for (int i = 0; i < len; i++) { |
| dest[dp] = theByte; |
| dp++; |
| } |
| } |
| } |
| return 1; |
| } |
| int decompressRLE8Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) { |
| int sp = 0; |
| int se = numBytes; |
| int dp = 0; |
| int de = destSize; |
| int x = 0, y = 0; |
| while (sp < se) { |
| int len = src[sp] & 0xFF; |
| sp++; |
| if (len == 0) { |
| len = src[sp] & 0xFF; |
| sp++; |
| switch (len) { |
| case 0: /* end of line */ |
| y++; |
| x = 0; |
| dp = y * stride; |
| if (dp >= de) |
| return -1; |
| break; |
| case 1: /* end of bitmap */ |
| return 1; |
| case 2: /* delta */ |
| x += src[sp] & 0xFF; |
| sp++; |
| y += src[sp] & 0xFF; |
| sp++; |
| dp = y * stride + x; |
| if (dp >= de) |
| return -1; |
| break; |
| default: /* absolute mode run */ |
| if (len > (se - sp)) |
| return -1; |
| if (len > (de - dp)) |
| return -1; |
| for (int i = 0; i < len; i++) { |
| dest[dp] = src[sp]; |
| dp++; |
| sp++; |
| } |
| if ((sp & 1) != 0) |
| sp++; /* word align sp? */ |
| x += len; |
| break; |
| } |
| } else { |
| byte theByte = src[sp]; |
| sp++; |
| if (len > (de - dp)) |
| return -1; |
| for (int i = 0; i < len; i++) { |
| dest[dp] = theByte; |
| dp++; |
| } |
| x += len; |
| } |
| } |
| return 1; |
| } |
| boolean isFileFormat(LEDataInputStream stream) { |
| try { |
| byte[] header = new byte[18]; |
| stream.read(header); |
| stream.unread(header); |
| int infoHeaderSize = (header[14] & 0xFF) | ((header[15] & 0xFF) << 8) | ((header[16] & 0xFF) << 16) | ((header[17] & 0xFF) << 24); |
| return header[0] == 0x42 && header[1] == 0x4D && infoHeaderSize >= BMPHeaderFixedSize; |
| } catch (Exception e) { |
| return false; |
| } |
| } |
| byte[] loadData(byte[] infoHeader) { |
| int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24); |
| int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24); |
| int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8); |
| int stride = (width * bitCount + 7) / 8; |
| stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple |
| byte[] data = loadData(infoHeader, stride); |
| flipScanLines(data, stride, height); |
| return data; |
| } |
| byte[] loadData(byte[] infoHeader, int stride) { |
| int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24); |
| int dataSize = height * stride; |
| byte[] data = new byte[dataSize]; |
| int cmp = (infoHeader[16] & 0xFF) | ((infoHeader[17] & 0xFF) << 8) | ((infoHeader[18] & 0xFF) << 16) | ((infoHeader[19] & 0xFF) << 24); |
| if (cmp == 0) { // BMP_NO_COMPRESSION |
| try { |
| if (inputStream.read(data) != dataSize) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| } else { |
| int compressedSize = (infoHeader[20] & 0xFF) | ((infoHeader[21] & 0xFF) << 8) | ((infoHeader[22] & 0xFF) << 16) | ((infoHeader[23] & 0xFF) << 24); |
| byte[] compressed = new byte[compressedSize]; |
| try { |
| if (inputStream.read(compressed) != compressedSize) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| decompressData(compressed, data, stride, cmp); |
| } |
| return data; |
| } |
| int[] loadFileHeader() { |
| int[] header = new int[5]; |
| try { |
| header[0] = inputStream.readShort(); |
| header[1] = inputStream.readInt(); |
| header[2] = inputStream.readShort(); |
| header[3] = inputStream.readShort(); |
| header[4] = inputStream.readInt(); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| if (header[0] != 0x4D42) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| return header; |
| } |
| ImageData[] loadFromByteStream() { |
| int[] fileHeader = loadFileHeader(); |
| byte[] infoHeader = new byte[BMPHeaderFixedSize]; |
| try { |
| inputStream.read(infoHeader); |
| } catch (Exception e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24); |
| int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24); |
| int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8); |
| PaletteData palette = loadPalette(infoHeader); |
| if (inputStream.getPosition() < fileHeader[4]) { |
| // Seek to the specified offset |
| try { |
| inputStream.skip(fileHeader[4] - inputStream.getPosition()); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| } |
| byte[] data = loadData(infoHeader); |
| this.compression = (infoHeader[16] & 0xFF) | ((infoHeader[17] & 0xFF) << 8) | ((infoHeader[18] & 0xFF) << 16) | ((infoHeader[19] & 0xFF) << 24); |
| this.importantColors = (infoHeader[36] & 0xFF) | ((infoHeader[37] & 0xFF) << 8) | ((infoHeader[38] & 0xFF) << 16) | ((infoHeader[39] & 0xFF) << 24); |
| int xPelsPerMeter = (infoHeader[24] & 0xFF) | ((infoHeader[25] & 0xFF) << 8) | ((infoHeader[26] & 0xFF) << 16) | ((infoHeader[27] & 0xFF) << 24); |
| int yPelsPerMeter = (infoHeader[28] & 0xFF) | ((infoHeader[29] & 0xFF) << 8) | ((infoHeader[30] & 0xFF) << 16) | ((infoHeader[31] & 0xFF) << 24); |
| int type = (this.compression == 1 /*BMP_RLE8_COMPRESSION*/) || (this.compression == 2 |
| |
| /*BMP_RLE4_COMPRESSION*/) ? SWT.IMAGE_BMP_RLE : SWT.IMAGE_BMP; |
| return new ImageData[] { |
| ImageData.internal_new( |
| width, |
| height, |
| bitCount, |
| palette, |
| 4, |
| data, |
| 0, |
| null, |
| null, |
| -1, |
| -1, |
| type, |
| 0, |
| 0, |
| 0, |
| 0) |
| }; |
| } |
| PaletteData loadPalette(byte[] infoHeader) { |
| int depth = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8); |
| if (depth <= 8) { |
| int numColors = (infoHeader[32] & 0xFF) | ((infoHeader[33] & 0xFF) << 8) | ((infoHeader[34] & 0xFF) << 16) | ((infoHeader[35] & 0xFF) << 24); |
| if (numColors == 0) { |
| numColors = 1 << depth; |
| } else { |
| if (numColors > 256) |
| numColors = 256; |
| } |
| byte[] buf = new byte[numColors * 4]; |
| try { |
| if (inputStream.read(buf) != buf.length) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| return paletteFromBytes(buf, numColors); |
| } |
| if (depth == 16) return new PaletteData(0x7C00, 0x3E0, 0x1F); |
| if (depth == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000); |
| return new PaletteData(0xFF00, 0xFF0000, 0xFF000000); |
| } |
| PaletteData paletteFromBytes(byte[] bytes, int numColors) { |
| int bytesOffset = 0; |
| RGB[] colors = new RGB[numColors]; |
| for (int i = 0; i < numColors; i++) { |
| colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF, |
| bytes[bytesOffset + 1] & 0xFF, |
| bytes[bytesOffset] & 0xFF); |
| bytesOffset += 4; |
| } |
| return new PaletteData(colors); |
| } |
| /** |
| * Answer a byte array containing the BMP representation of |
| * the given device independent palette. |
| */ |
| static byte[] paletteToBytes(PaletteData pal) { |
| int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256); |
| byte[] bytes = new byte[n * 4]; |
| int offset = 0; |
| for (int i = 0; i < n; i++) { |
| RGB col = pal.colors[i]; |
| bytes[offset] = (byte)col.blue; |
| bytes[offset + 1] = (byte)col.green; |
| bytes[offset + 2] = (byte)col.red; |
| offset += 4; |
| } |
| return bytes; |
| } |
| |
| void flipScanLines(byte[] data, int stride, int height) { |
| int i1 = 0; |
| int i2 = (height - 1) * stride; |
| for (int i = 0; i < height / 2; i++) { |
| for (int index = 0; index < stride; index++) { |
| byte b = data[index + i1]; |
| data[index + i1] = data[index + i2]; |
| data[index + i2] = b; |
| } |
| i1 += stride; |
| i2 -= stride; |
| } |
| } |
| |
| } |
| |
| static class WinICOFileFormat extends FileFormat { |
| |
| static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) { |
| if (pad == newPad) return data; |
| int stride = (width * depth + 7) / 8; |
| int bpl = (stride + (pad - 1)) / pad * pad; |
| int newBpl = (stride + (newPad - 1)) / newPad * newPad; |
| byte[] newData = new byte[height * newBpl]; |
| int srcIndex = 0, destIndex = 0; |
| for (int y = 0; y < height; y++) { |
| System.arraycopy(data, srcIndex, newData, destIndex, newBpl); |
| srcIndex += bpl; |
| destIndex += newBpl; |
| } |
| return newData; |
| } |
| /** |
| * Answer the size in bytes of the file representation of the given |
| * icon |
| */ |
| int iconSize(ImageData i) { |
| int shapeDataStride = (i.width * i.depth + 31) / 32 * 4; |
| int maskDataStride = (i.width + 31) / 32 * 4; |
| int dataSize = (shapeDataStride + maskDataStride) * i.height; |
| int paletteSize = i.palette.colors != null ? i.palette.colors.length * 4 : 0; |
| return WinBMPFileFormat.BMPHeaderFixedSize + paletteSize + dataSize; |
| } |
| boolean isFileFormat(LEDataInputStream stream) { |
| try { |
| byte[] header = new byte[4]; |
| stream.read(header); |
| stream.unread(header); |
| return header[0] == 0 && header[1] == 0 && header[2] == 1 && header[3] == 0; |
| } catch (Exception e) { |
| return false; |
| } |
| } |
| boolean isValidIcon(ImageData i) { |
| switch (i.depth) { |
| case 1: |
| case 4: |
| case 8: |
| if (i.palette.isDirect) return false; |
| int size = i.palette.colors.length; |
| return size == 2 || size == 16 || size == 32 || size == 256; |
| case 24: |
| case 32: |
| return i.palette.isDirect; |
| } |
| return false; |
| } |
| int loadFileHeader(LEDataInputStream byteStream) { |
| int[] fileHeader = new int[3]; |
| try { |
| fileHeader[0] = byteStream.readShort(); |
| fileHeader[1] = byteStream.readShort(); |
| fileHeader[2] = byteStream.readShort(); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| if ((fileHeader[0] != 0) || (fileHeader[1] != 1)) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| int numIcons = fileHeader[2]; |
| if (numIcons <= 0) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| return numIcons; |
| } |
| int loadFileHeader(LEDataInputStream byteStream, boolean hasHeader) { |
| int[] fileHeader = new int[3]; |
| try { |
| if (hasHeader) { |
| fileHeader[0] = byteStream.readShort(); |
| fileHeader[1] = byteStream.readShort(); |
| } else { |
| fileHeader[0] = 0; |
| fileHeader[1] = 1; |
| } |
| fileHeader[2] = byteStream.readShort(); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| if ((fileHeader[0] != 0) || (fileHeader[1] != 1)) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| int numIcons = fileHeader[2]; |
| if (numIcons <= 0) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| return numIcons; |
| } |
| ImageData[] loadFromByteStream() { |
| int numIcons = loadFileHeader(inputStream); |
| int[][] headers = loadIconHeaders(numIcons); |
| ImageData[] icons = new ImageData[headers.length]; |
| for (int i = 0; i < icons.length; i++) { |
| icons[i] = loadIcon(headers[i]); |
| } |
| return icons; |
| } |
| /** |
| * Load one icon from the byte stream. |
| */ |
| ImageData loadIcon(int[] iconHeader) { |
| byte[] infoHeader = loadInfoHeader(iconHeader); |
| WinBMPFileFormat bmpFormat = new WinBMPFileFormat(); |
| bmpFormat.inputStream = inputStream; |
| PaletteData palette = bmpFormat.loadPalette(infoHeader); |
| byte[] shapeData = bmpFormat.loadData(infoHeader); |
| int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24); |
| int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24); |
| int depth = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8); |
| infoHeader[14] = 1; |
| infoHeader[15] = 0; |
| byte[] maskData = bmpFormat.loadData(infoHeader); |
| maskData = convertPad(maskData, width, height, 1, 4, 2); |
| bitInvertData(maskData, 0, maskData.length); |
| return ImageData.internal_new( |
| width, |
| height, |
| depth, |
| palette, |
| 4, |
| shapeData, |
| 2, |
| maskData, |
| null, |
| -1, |
| -1, |
| SWT.IMAGE_ICO, |
| 0, |
| 0, |
| 0, |
| 0); |
| } |
| int[][] loadIconHeaders(int numIcons) { |
| int[][] headers = new int[numIcons][7]; |
| try { |
| for (int i = 0; i < numIcons; i++) { |
| headers[i][0] = inputStream.read(); |
| headers[i][1] = inputStream.read(); |
| headers[i][2] = inputStream.readShort(); |
| headers[i][3] = inputStream.readShort(); |
| headers[i][4] = inputStream.readShort(); |
| headers[i][5] = inputStream.readInt(); |
| headers[i][6] = inputStream.readInt(); |
| } |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| return headers; |
| } |
| byte[] loadInfoHeader(int[] iconHeader) { |
| int width = iconHeader[0]; |
| int height = iconHeader[1]; |
| int numColors = iconHeader[2]; // the number of colors is in the low byte, but the high byte must be 0 |
| if (numColors == 0) numColors = 256; // this is specified: '00' represents '256' (0x100) colors |
| if ((numColors != 2) && (numColors != 8) && (numColors != 16) && |
| (numColors != 32) && (numColors != 256)) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| if (inputStream.getPosition() < iconHeader[6]) { |
| // Seek to the specified offset |
| try { |
| inputStream.skip(iconHeader[6] - inputStream.getPosition()); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| return null; |
| } |
| } |
| byte[] infoHeader = new byte[WinBMPFileFormat.BMPHeaderFixedSize]; |
| try { |
| inputStream.read(infoHeader); |
| } catch (IOException e) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| if (((infoHeader[12] & 0xFF) | ((infoHeader[13] & 0xFF) << 8)) != 1) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| int infoWidth = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24); |
| int infoHeight = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24); |
| int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8); |
| if (height == infoHeight && bitCount == 1) height /= 2; |
| if (!((width == infoWidth) && (height * 2 == infoHeight) && |
| (bitCount == 1 || bitCount == 4 || bitCount == 8 || bitCount == 24 || bitCount == 32))) |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| infoHeader[8] = (byte)(height & 0xFF); |
| infoHeader[9] = (byte)((height >> 8) & 0xFF); |
| infoHeader[10] = (byte)((height >> 16) & 0xFF); |
| infoHeader[11] = (byte)((height >> 24) & 0xFF); |
| return infoHeader; |
| } |
| } |
| static class SWT { |
| public static final int IMAGE_ICO = 3; |
| public static final int ERROR_IO = 39; |
| public static final int ERROR_INVALID_IMAGE = 40; |
| public static final int ERROR_NULL_ARGUMENT = 4; |
| public static final int ERROR_INVALID_ARGUMENT = 5; |
| public static final int ERROR_CANNOT_BE_ZERO = 7; |
| public static final int IMAGE_UNDEFINED = -1; |
| public static final int ERROR_UNSUPPORTED_DEPTH = 38; |
| public static final int TRANSPARENCY_MASK = 1 << 1; |
| public static final int ERROR_UNSUPPORTED_FORMAT = 42; |
| public static final int TRANSPARENCY_ALPHA = 1 << 0; |
| public static final int TRANSPARENCY_NONE = 0x0; |
| public static final int TRANSPARENCY_PIXEL = 1 << 2; |
| public static final int IMAGE_BMP = 0; |
| public static final int IMAGE_BMP_RLE = 1; |
| |
| public static void error(int code) { |
| throw new RuntimeException("Error "+code); |
| } |
| public static void error(int code, Throwable t) { |
| throw new RuntimeException(t); |
| } |
| } |
| } |