0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2006 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.pde.internal.swt.tools;
0011:
0012: import java.io.*;
0013: import java.util.*;
0014:
0015: /**
0016: * Customize the icon of a Windows exe
0017: *
0018: * WARNING! This class is not part of SWT API. It is NOT API. It is an internal
0019: * tool that may be changed or removed at anytime.
0020: *
0021: * Based on MSDN "An In-Depth Look into the Win32 Portable Executable File Format"
0022: */
0023: public class IconExe {
0024:
0025: /**
0026: * Replace the Desktop icons provided in the Windows executable program
0027: * with matching icons provided by the user.
0028: *
0029: * Takes 2 arguments
0030: * argument 0: the Windows executable e.g c:/eclipse/eclipse.exe
0031: * argument 1: The .ico file to write to the given executable e.g. c:/myApp.ico
0032: *
0033: * Note 1. Write access to the executable program is required. As a result, that
0034: * program must not be currently running or edited elsewhere.
0035: *
0036: * Note 2. The Eclipse 3.1 launcher requires a .ico file with the following 6 images
0037: * 1. 32x32, 4 bit (Windows 16 colors palette)
0038: * 2. 16x16, 4 bit (Windows 16 colors palette)
0039: * 3. 16x16, 8 bit (256 colors)
0040: * 4. 32x32, 8 bit (256 colors)
0041: * 5. 48x48, 4 bit (Windows 16 colors palette)
0042: * 6. 48x48, 8 bit (256 colors)
0043: * A user icon matching exactly the width/height/depth of an executable icon will be written
0044: * to the executable and will replace that executable icon. If an executable icon
0045: * does not match a user icon, it is silently left as is.
0046: *
0047: * Note 3. This function modifies the content of the executable program and may cause
0048: * its corruption.
0049: */
0050: public static void main(String[] args) throws Exception {
0051: if (args.length < 2) {
0052: System.err
0053: .println("Usage: IconExe <windows executable> <ico file>"); //$NON-NLS-1$
0054: return;
0055: }
0056: ImageLoader loader = new ImageLoader();
0057:
0058: List images = new ArrayList();
0059: for (int i = 1; i < args.length; i++) {
0060: try {
0061: //An ICO should contain 7 images, a BMP will contain 1
0062: ImageData[] current = loader.load(args[i]);
0063: for (int j = 0; j < current.length; j++) {
0064: images.add(current[j]);
0065: }
0066: } catch (RuntimeException e) {
0067: //ignore so that we process the other images
0068: }
0069: }
0070: ImageData[] data = new ImageData[images.size()];
0071: data = (ImageData[]) images.toArray(data);
0072:
0073: int nMissing = unloadIcons(args[0], data);
0074: if (nMissing != 0)
0075: System.err
0076: .println("Error - " + nMissing + " icon(s) not replaced in " + args[0] + " using " + args[1]); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
0077: }
0078:
0079: /* Implementation */
0080:
0081: /**
0082: * Retrieve the Desktop icons provided in the Windows executable program.
0083: * These icons are typically shown in various places of the Windows desktop.
0084: *
0085: * Note. The Eclipse 3.1 launcher returns the following 6 images
0086: * 1. 32x32, 4 bit (Windows 16 colors palette)
0087: * 2. 16x16, 4 bit (Windows 16 colors palette)
0088: * 3. 16x16, 8 bit (256 colors)
0089: * 4. 32x32, 8 bit (256 colors)
0090: * 5. 48x48, 4 bit (Windows 16 colors palette)
0091: * 6. 48x48, 8 bit (256 colors)
0092: *
0093: * @param program the Windows executable e.g c:/eclipse/eclipse.exe
0094: */
0095: static ImageData[] loadIcons(String program)
0096: throws FileNotFoundException, IOException {
0097: RandomAccessFile raf = new RandomAccessFile(program, "r"); //$NON-NLS-1$
0098: IconExe iconExe = new IconExe();
0099: IconResInfo[] iconInfo = iconExe.getIcons(raf);
0100: ImageData[] data = new ImageData[iconInfo.length];
0101: for (int i = 0; i < data.length; i++)
0102: data[i] = iconInfo[i].data;
0103: raf.close();
0104: return data;
0105: }
0106:
0107: /**
0108: * Replace the Desktop icons provided in the Windows executable program
0109: * with icons provided by the user.
0110: *
0111: * Note 1. Write access to the executable program is required. As a result, that
0112: * program must not be currently running or edited elsewhere.
0113: *
0114: * Note 2. Use loadIcons to determine which set of icons (width, height, depth)
0115: * is required to replace the icons in the executable program. A user icon
0116: * matching exactly the width/height/depth of an executable icon will be written
0117: * to the executable and will replace that executable icon. If an executable icon
0118: * does not match a user icon, it is left as is. Verify the return value matches
0119: * the number of icons to write. Finally, use loadIcons after this operation
0120: * to verify the icons have changed as expected.
0121: *
0122: * Note 3. The Eclipse 3.1 launcher requires the following 6 images (in any order).
0123: * 1. 32x32, 4 bit (Windows 16 colors palette)
0124: * 2. 16x16, 4 bit (Windows 16 colors palette)
0125: * 3. 16x16, 8 bit (256 colors)
0126: * 4. 32x32, 8 bit (256 colors)
0127: * 5. 48x48, 4 bit (Windows 16 colors palette)
0128: * 6. 48x48, 8 bit (256 colors)
0129: *
0130: * Note 4. This function modifies the content of the executable program and may cause
0131: * its corruption.
0132: *
0133: * @param program the Windows executable e.g c:/eclipse/eclipse.exe
0134: * @param icons to write to the given executable
0135: * @return the number of icons from the original program that were not successfully replaced (0 if success)
0136: */
0137: static int unloadIcons(String program, ImageData[] icons)
0138: throws FileNotFoundException, IOException {
0139: RandomAccessFile raf = new RandomAccessFile(program, "rw"); //$NON-NLS-1$
0140: IconExe iconExe = new IconExe();
0141: IconResInfo[] iconInfo = iconExe.getIcons(raf);
0142: int cnt = 0;
0143: for (int i = 0; i < iconInfo.length; i++) {
0144: for (int j = 0; j < icons.length; j++) {
0145: if (icons[j] == null)
0146: continue;
0147: if (iconInfo[i].data.width == icons[j].width
0148: && iconInfo[i].data.height == icons[j].height
0149: && iconInfo[i].data.depth == icons[j].depth) {
0150: raf.seek(iconInfo[i].offset);
0151: unloadIcon(raf, icons[j]);
0152: cnt++;
0153: break;
0154: }
0155: }
0156: }
0157: raf.close();
0158: return iconInfo.length - cnt;
0159: }
0160:
0161: public static final String VERSION = "20050124"; //$NON-NLS-1$
0162:
0163: static final boolean DEBUG = false;
0164:
0165: public static class IconResInfo {
0166: ImageData data;
0167: int offset;
0168: int size;
0169: }
0170:
0171: IconResInfo[] iconInfo = null;
0172: int iconCnt;
0173:
0174: IconResInfo[] getIcons(RandomAccessFile raf) throws IOException {
0175: iconInfo = new IconResInfo[4];
0176: iconCnt = 0;
0177: IMAGE_DOS_HEADER imageDosHeader = new IMAGE_DOS_HEADER();
0178: read(raf, imageDosHeader);
0179: if (imageDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
0180: return null;
0181: int imageNtHeadersOffset = imageDosHeader.e_lfanew;
0182: raf.seek(imageNtHeadersOffset);
0183: IMAGE_NT_HEADERS imageNtHeaders = new IMAGE_NT_HEADERS();
0184: read(raf, imageNtHeaders);
0185: if (imageNtHeaders.Signature != IMAGE_NT_SIGNATURE)
0186: return null;
0187:
0188: // DumpResources
0189: int resourcesRVA = imageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
0190: if (resourcesRVA == 0)
0191: return null;
0192: if (DEBUG)
0193: System.out
0194: .println("* Resources (RVA= " + resourcesRVA + ")"); //$NON-NLS-1$ //$NON-NLS-2$
0195: IMAGE_SECTION_HEADER imageSectionHeader = new IMAGE_SECTION_HEADER();
0196: int firstSectionOffset = imageNtHeadersOffset
0197: + IMAGE_NT_HEADERS.FIELD_OFFSET_OptionalHeader
0198: + imageNtHeaders.FileHeader.SizeOfOptionalHeader;
0199: raf.seek(firstSectionOffset);
0200: boolean found = false;
0201: for (int i = 0; i < imageNtHeaders.FileHeader.NumberOfSections; i++) {
0202: read(raf, imageSectionHeader);
0203: if (resourcesRVA >= imageSectionHeader.VirtualAddress
0204: && resourcesRVA < imageSectionHeader.VirtualAddress
0205: + imageSectionHeader.Misc_VirtualSize) {
0206: // could check the imageSectionHeader name is .rsrc
0207: found = true;
0208: break;
0209: }
0210: }
0211: if (!found)
0212: return null;
0213: int delta = imageSectionHeader.VirtualAddress
0214: - imageSectionHeader.PointerToRawData;
0215: int imageResourceDirectoryOffset = resourcesRVA - delta;
0216: dumpResourceDirectory(raf, imageResourceDirectoryOffset,
0217: imageResourceDirectoryOffset, delta, 0, 0, false);
0218: if (iconCnt < iconInfo.length) {
0219: IconResInfo[] newArray = new IconResInfo[iconCnt];
0220: System.arraycopy(iconInfo, 0, newArray, 0, iconCnt);
0221: iconInfo = newArray;
0222: }
0223: return iconInfo;
0224: }
0225:
0226: void dumpResourceDirectory(RandomAccessFile raf,
0227: int imageResourceDirectoryOffset, int resourceBase,
0228: int delta, int type, int level, boolean rt_icon_root)
0229: throws IOException {
0230: if (DEBUG)
0231: System.out.println("** LEVEL " + level); //$NON-NLS-1$
0232:
0233: IMAGE_RESOURCE_DIRECTORY imageResourceDirectory = new IMAGE_RESOURCE_DIRECTORY();
0234: raf.seek(imageResourceDirectoryOffset);
0235: read(raf, imageResourceDirectory);
0236:
0237: if (DEBUG) {
0238: String sType = "" + type; //$NON-NLS-1$
0239: // level 1 resources are resource types
0240: if (level == 1) {
0241: System.out.println("___________________________"); //$NON-NLS-1$
0242: if (type == RT_ICON)
0243: sType = "RT_ICON"; //$NON-NLS-1$
0244: if (type == RT_GROUP_ICON)
0245: sType = "RT_GROUP_ICON"; //$NON-NLS-1$
0246: }
0247: System.out
0248: .println("Resource Directory [" + sType + "]" + " (Named " + imageResourceDirectory.NumberOfNamedEntries + ", ID " + imageResourceDirectory.NumberOfIdEntries + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
0249: }
0250:
0251: IMAGE_RESOURCE_DIRECTORY_ENTRY[] imageResourceDirectoryEntries = new IMAGE_RESOURCE_DIRECTORY_ENTRY[imageResourceDirectory.NumberOfIdEntries];
0252: for (int i = 0; i < imageResourceDirectoryEntries.length; i++) {
0253: imageResourceDirectoryEntries[i] = new IMAGE_RESOURCE_DIRECTORY_ENTRY();
0254: read(raf, imageResourceDirectoryEntries[i]);
0255: }
0256: for (int i = 0; i < imageResourceDirectoryEntries.length; i++) {
0257: if (imageResourceDirectoryEntries[i].DataIsDirectory) {
0258: dumpResourceDirectory(
0259: raf,
0260: imageResourceDirectoryEntries[i].OffsetToDirectory
0261: + resourceBase, resourceBase, delta,
0262: imageResourceDirectoryEntries[i].Id, level + 1,
0263: rt_icon_root ? true : type == RT_ICON);
0264: } else {
0265: // Resource found
0266: /// pResDirEntry->Name
0267: IMAGE_RESOURCE_DIRECTORY_ENTRY irde = imageResourceDirectoryEntries[i];
0268: IMAGE_RESOURCE_DATA_ENTRY data = new IMAGE_RESOURCE_DATA_ENTRY();
0269: raf.seek(imageResourceDirectoryEntries[i].OffsetToData
0270: + resourceBase);
0271: read(raf, data);
0272: if (DEBUG)
0273: System.out
0274: .println("Resource Id " + irde.Id + " Data Offset RVA " + data.OffsetToData + ", Size " + data.Size); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
0275: if (rt_icon_root) {
0276: if (DEBUG)
0277: System.out
0278: .println("iconcnt " + iconCnt + " |" + iconInfo.length); //$NON-NLS-1$ //$NON-NLS-2$
0279: iconInfo[iconCnt] = new IconResInfo();
0280: iconInfo[iconCnt].data = parseIcon(raf,
0281: data.OffsetToData - delta, data.Size);
0282: iconInfo[iconCnt].offset = data.OffsetToData
0283: - delta;
0284: iconInfo[iconCnt].size = data.Size;
0285: iconCnt++;
0286: if (iconCnt == iconInfo.length) {
0287: IconResInfo[] newArray = new IconResInfo[iconInfo.length + 4];
0288: System.arraycopy(iconInfo, 0, newArray, 0,
0289: iconInfo.length);
0290: iconInfo = newArray;
0291: }
0292: }
0293: }
0294: }
0295: }
0296:
0297: static ImageData parseIcon(RandomAccessFile raf, int offset,
0298: int size) throws IOException {
0299: raf.seek(offset);
0300: BITMAPINFO bitmapInfo = new BITMAPINFO();
0301: read(raf, bitmapInfo);
0302: bitmapInfo.bmiHeader.biHeight /= 2;
0303: int width = bitmapInfo.bmiHeader.biWidth;
0304: int height = bitmapInfo.bmiHeader.biHeight;
0305: int depth = bitmapInfo.bmiHeader.biBitCount;
0306:
0307: PaletteData palette = loadPalette(bitmapInfo.bmiHeader, raf);
0308: byte[] shapeData = loadData(bitmapInfo.bmiHeader, raf);
0309: bitmapInfo.bmiHeader.biBitCount = 1;
0310: byte[] maskData = loadData(bitmapInfo.bmiHeader, raf);
0311: maskData = convertPad(maskData, width, height, 1, 4, 2);
0312: bitInvertData(maskData, 0, maskData.length);
0313: return ImageData.internal_new(width, height, depth, palette, 4,
0314: shapeData, 2, maskData, null, -1, -1, SWT.IMAGE_ICO, 0,
0315: 0, 0, 0);
0316: }
0317:
0318: static byte[] bitInvertData(byte[] data, int startIndex,
0319: int endIndex) {
0320: // Destructively bit invert data in the given byte array.
0321: for (int i = startIndex; i < endIndex; i++) {
0322: data[i] = (byte) (255 - data[i - startIndex]);
0323: }
0324: return data;
0325: }
0326:
0327: static final byte[] convertPad(byte[] data, int width, int height,
0328: int depth, int pad, int newPad) {
0329: if (pad == newPad)
0330: return data;
0331: int stride = (width * depth + 7) / 8;
0332: int bpl = (stride + (pad - 1)) / pad * pad;
0333: int newBpl = (stride + (newPad - 1)) / newPad * newPad;
0334: byte[] newData = new byte[height * newBpl];
0335: int srcIndex = 0, destIndex = 0;
0336: for (int y = 0; y < height; y++) {
0337: System
0338: .arraycopy(data, srcIndex, newData, destIndex,
0339: newBpl);
0340: srcIndex += bpl;
0341: destIndex += newBpl;
0342: }
0343: return newData;
0344: }
0345:
0346: static PaletteData loadPalette(BITMAPINFOHEADER bih,
0347: RandomAccessFile raf) throws IOException {
0348: int depth = bih.biBitCount;
0349: if (depth <= 8) {
0350: int numColors = bih.biClrUsed;
0351: if (numColors == 0) {
0352: numColors = 1 << depth;
0353: } else {
0354: if (numColors > 256)
0355: numColors = 256;
0356: }
0357: byte[] buf = new byte[numColors * 4];
0358: raf.read(buf);
0359: return paletteFromBytes(buf, numColors);
0360: }
0361: if (depth == 16)
0362: return new PaletteData(0x7C00, 0x3E0, 0x1F);
0363: if (depth == 24)
0364: return new PaletteData(0xFF, 0xFF00, 0xFF0000);
0365: return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
0366: }
0367:
0368: static PaletteData paletteFromBytes(byte[] bytes, int numColors) {
0369: int bytesOffset = 0;
0370: RGB[] colors = new RGB[numColors];
0371: for (int i = 0; i < numColors; i++) {
0372: colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
0373: bytes[bytesOffset + 1] & 0xFF,
0374: bytes[bytesOffset] & 0xFF);
0375: bytesOffset += 4;
0376: }
0377: return new PaletteData(colors);
0378: }
0379:
0380: static byte[] loadData(BITMAPINFOHEADER bih, RandomAccessFile raf)
0381: throws IOException {
0382: int stride = (bih.biWidth * bih.biBitCount + 7) / 8;
0383: stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
0384: byte[] data = loadData(bih, raf, stride);
0385: flipScanLines(data, stride, bih.biHeight);
0386: return data;
0387: }
0388:
0389: static void flipScanLines(byte[] data, int stride, int height) {
0390: int i1 = 0;
0391: int i2 = (height - 1) * stride;
0392: for (int i = 0; i < height / 2; i++) {
0393: for (int index = 0; index < stride; index++) {
0394: byte b = data[index + i1];
0395: data[index + i1] = data[index + i2];
0396: data[index + i2] = b;
0397: }
0398: i1 += stride;
0399: i2 -= stride;
0400: }
0401: }
0402:
0403: static byte[] loadData(BITMAPINFOHEADER bih, RandomAccessFile raf,
0404: int stride) throws IOException {
0405: int dataSize = bih.biHeight * stride;
0406: byte[] data = new byte[dataSize];
0407: int cmp = bih.biCompression;
0408: if (cmp == 0) { // BMP_NO_COMPRESSION
0409: raf.read(data);
0410: } else {
0411: if (DEBUG)
0412: System.out.println("ICO cannot be compressed?"); //$NON-NLS-1$
0413: }
0414: return data;
0415: }
0416:
0417: static void unloadIcon(RandomAccessFile raf, ImageData icon)
0418: throws IOException {
0419: int sizeImage = (((icon.width * icon.depth + 31) / 32 * 4) + ((icon.width + 31) / 32 * 4))
0420: * icon.height;
0421: write4(raf, BMPHeaderFixedSize);
0422: write4(raf, icon.width);
0423: write4(raf, icon.height * 2);
0424: writeU2(raf, 1);
0425: writeU2(raf, icon.depth);
0426: write4(raf, 0);
0427: write4(raf, sizeImage);
0428: write4(raf, 0);
0429: write4(raf, 0);
0430: write4(
0431: raf,
0432: icon.palette.colors != null ? icon.palette.colors.length
0433: : 0);
0434: write4(raf, 0);
0435:
0436: byte[] rgbs = paletteToBytes(icon.palette);
0437: raf.write(rgbs);
0438: unloadShapeData(raf, icon);
0439: unloadMaskData(raf, icon);
0440: }
0441:
0442: static byte[] paletteToBytes(PaletteData pal) {
0443: int n = pal.colors == null ? 0
0444: : (pal.colors.length < 256 ? pal.colors.length : 256);
0445: byte[] bytes = new byte[n * 4];
0446: int offset = 0;
0447: for (int i = 0; i < n; i++) {
0448: RGB col = pal.colors[i];
0449: bytes[offset] = (byte) col.blue;
0450: bytes[offset + 1] = (byte) col.green;
0451: bytes[offset + 2] = (byte) col.red;
0452: offset += 4;
0453: }
0454: return bytes;
0455: }
0456:
0457: static void unloadMaskData(RandomAccessFile raf, ImageData icon) {
0458: ImageData mask = icon.getTransparencyMask();
0459: int bpl = (icon.width + 7) / 8;
0460: int pad = mask.scanlinePad;
0461: int srcBpl = (bpl + pad - 1) / pad * pad;
0462: int destBpl = (bpl + 3) / 4 * 4;
0463: byte[] buf = new byte[destBpl];
0464: int offset = (icon.height - 1) * srcBpl;
0465: byte[] data = mask.data;
0466: try {
0467: for (int i = 0; i < icon.height; i++) {
0468: System.arraycopy(data, offset, buf, 0, bpl);
0469: bitInvertData(buf, 0, bpl);
0470: raf.write(buf, 0, destBpl);
0471: offset -= srcBpl;
0472: }
0473: } catch (IOException e) {
0474: SWT.error(SWT.ERROR_IO, e);
0475: }
0476: }
0477:
0478: static void unloadShapeData(RandomAccessFile raf, ImageData icon) {
0479: int bpl = (icon.width * icon.depth + 7) / 8;
0480: int pad = icon.scanlinePad;
0481: int srcBpl = (bpl + pad - 1) / pad * pad;
0482: int destBpl = (bpl + 3) / 4 * 4;
0483: byte[] buf = new byte[destBpl];
0484: int offset = (icon.height - 1) * srcBpl;
0485: byte[] data = icon.data;
0486: try {
0487: for (int i = 0; i < icon.height; i++) {
0488: System.arraycopy(data, offset, buf, 0, bpl);
0489: raf.write(buf, 0, destBpl);
0490: offset -= srcBpl;
0491: }
0492: } catch (IOException e) {
0493: SWT.error(SWT.ERROR_IO, e);
0494: }
0495: }
0496:
0497: static boolean readIconGroup(RandomAccessFile raf, int offset,
0498: int size) throws IOException {
0499: raf.seek(offset);
0500: NEWHEADER newHeader = new NEWHEADER();
0501: read(raf, newHeader);
0502: if (newHeader.ResType != RES_ICON)
0503: return false;
0504: RESDIR[] resDir = new RESDIR[newHeader.ResCount];
0505: for (int i = 0; i < newHeader.ResCount; i++) {
0506: resDir[i] = new RESDIR();
0507: read(raf, resDir[i]);
0508: }
0509: return true;
0510: }
0511:
0512: static void copyFile(String src, String dst)
0513: throws FileNotFoundException, IOException {
0514: File srcFile = new File(src);
0515: File dstFile = new File(dst);
0516: InputStream in = new BufferedInputStream(new FileInputStream(
0517: srcFile));
0518: OutputStream out = new BufferedOutputStream(
0519: new FileOutputStream(dstFile));
0520: int c;
0521: while ((c = in.read()) != -1)
0522: out.write(c);
0523: in.close();
0524: out.close();
0525: }
0526:
0527: /* IO utilities to parse Windows executable */
0528: static final int IMAGE_DOS_SIGNATURE = 0x5a4d;
0529: static final int IMAGE_NT_SIGNATURE = 0x00004550;
0530: static final int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2;
0531: static final int RES_ICON = 1;
0532: static final int RT_ICON = 3;
0533: static final int RT_GROUP_ICON = 14;
0534: static final int BMPHeaderFixedSize = 40;
0535:
0536: public static class IMAGE_DOS_HEADER {
0537: int e_magic; // WORD
0538: int e_cblp; // WORD
0539: int e_cp; // WORD
0540: int e_crlc; // WORD
0541: int e_cparhdr; // WORD
0542: int e_minalloc; // WORD
0543: int e_maxalloc; // WORD
0544: int e_ss; // WORD
0545: int e_sp; // WORD
0546: int e_csum; // WORD
0547: int e_ip; // WORD
0548: int e_cs; // WORD
0549: int e_lfarlc; // WORD
0550: int e_ovno; // WORD
0551: int[] e_res = new int[4]; // WORD[4]
0552: int e_oemid; // WORD
0553: int e_oeminfo; // WORD
0554: int[] e_res2 = new int[10]; // WORD[10]
0555: int e_lfanew; // LONG
0556: }
0557:
0558: public static class IMAGE_FILE_HEADER {
0559: int Machine; // WORD
0560: int NumberOfSections; // WORD
0561: int TimeDateStamp; // DWORD
0562: int PointerToSymbolTable; // DWORD
0563: int NumberOfSymbols; // DWORD
0564: int SizeOfOptionalHeader; // WORD
0565: int Characteristics; // WORD
0566: }
0567:
0568: public static class IMAGE_DATA_DIRECTORY {
0569: int VirtualAddress; // DWORD
0570: int Size; // DWORD
0571: }
0572:
0573: public static class IMAGE_OPTIONAL_HEADER {
0574: int Magic; // WORD
0575: int MajorLinkerVersion; // BYTE
0576: int MinorLinkerVersion; // BYTE
0577: int SizeOfCode; // DWORD
0578: int SizeOfInitializedData; // DWORD
0579: int SizeOfUninitializedData; // DWORD
0580: int AddressOfEntryPoint; // DWORD
0581: int BaseOfCode; // DWORD
0582: int BaseOfData; // DWORD
0583: int ImageBase; // DWORD
0584: int SectionAlignment; // DWORD
0585: int FileAlignment; // DWORD
0586: int MajorOperatingSystemVersion; // WORD
0587: int MinorOperatingSystemVersion; // WORD
0588: int MajorImageVersion; // WORD
0589: int MinorImageVersion; // WORD
0590: int MajorSubsystemVersion; // WORD
0591: int MinorSubsystemVersion; // WORD
0592: int Win32VersionValue; // DWORD
0593: int SizeOfImage; // DWORD
0594: int SizeOfHeaders; // DWORD
0595: int CheckSum; // DWORD
0596: int Subsystem; // WORD
0597: int DllCharacteristics; // WORD
0598: int SizeOfStackReserve; // DWORD
0599: int SizeOfStackCommit; // DWORD
0600: int SizeOfHeapReserve; // DWORD
0601: int SizeOfHeapCommit; // DWORD
0602: int LoaderFlags; // DWORD
0603: int NumberOfRvaAndSizes; // DWORD
0604: IMAGE_DATA_DIRECTORY[] DataDirectory = new IMAGE_DATA_DIRECTORY[16];
0605: }
0606:
0607: public static class IMAGE_NT_HEADERS {
0608: int Signature; // DWORD
0609: IMAGE_FILE_HEADER FileHeader = new IMAGE_FILE_HEADER();
0610: IMAGE_OPTIONAL_HEADER OptionalHeader = new IMAGE_OPTIONAL_HEADER();
0611: final static int FIELD_OFFSET_OptionalHeader = 24;
0612: }
0613:
0614: public static class IMAGE_SECTION_HEADER {
0615: int[] Name = new int[8]; // BYTE[8]
0616: int Misc_VirtualSize; // DWORD (union Misc { DWORD PhysicalAddress; DWORD VirtualSize }
0617: int VirtualAddress; // DWORD
0618: int SizeOfRawData; // DWORD
0619: int PointerToRawData; // DWORD
0620: int PointerToRelocations; // DWORD
0621: int PointerToLinenumbers; // DWORD
0622: int NumberOfRelocations; // WORD
0623: int NumberOfLinenumbers; // WORD
0624: int Characteristics; // DWORD
0625: }
0626:
0627: public static class IMAGE_RESOURCE_DIRECTORY {
0628: int Characteristics; // DWORD
0629: int TimeDateStamp; // DWORD
0630: int MajorVersion; // WORD
0631: int MinorVersion; // WORD
0632: int NumberOfNamedEntries; // WORD - used
0633: int NumberOfIdEntries; // WORD - used
0634: final static int SIZEOF = 16;
0635: }
0636:
0637: public static class IMAGE_RESOURCE_DIRECTORY_ENTRY {
0638: // union
0639: int NameOffset; // DWORD 31 bits
0640: boolean NameIsString; // DWORD 1 bit
0641: int Name; // DWORD
0642: int Id; // WORD
0643: // union
0644: int OffsetToData; // DWORD
0645: int OffsetToDirectory; // DWORD 31 bits
0646: boolean DataIsDirectory; // DWORD 1 bit
0647: }
0648:
0649: public static class IMAGE_RESOURCE_DATA_ENTRY {
0650: int OffsetToData; // DWORD
0651: int Size; // DWORD
0652: int CodePage; // DWORD
0653: int Reserved; // DWORD
0654: }
0655:
0656: public static class NEWHEADER {
0657: int Reserved; // WORD
0658: int ResType; // WORD
0659: int ResCount; // WORD
0660: }
0661:
0662: public static class ICONRESDIR {
0663: int Width; // BYTE
0664: int Height; // BYTE
0665: int ColorCount; // BYTE
0666: int reserved; // BYTE
0667: }
0668:
0669: public static class CURSORDIR {
0670: int Width; // WORD
0671: int Height; // WORD
0672: }
0673:
0674: public static class RESDIR {
0675: // union
0676: ICONRESDIR Icon = new ICONRESDIR();
0677: CURSORDIR Cursor = new CURSORDIR();
0678: int Planes; // WORD
0679: int BitCount; // WORD
0680: int BytesInRes; // DWORD
0681: int IconCursorId; // WORD
0682: }
0683:
0684: public static class BITMAPINFOHEADER {
0685: int biSize; // DWORD
0686: int biWidth; // LONG
0687: int biHeight; // LONG
0688: int biPlanes; // WORD
0689: int biBitCount; // WORD
0690: int biCompression; // DWORD
0691: int biSizeImage; // DWORD
0692: int biXPelsPerMeter; // LONG
0693: int biYPelsPerMeter; // LONG
0694: int biClrUsed; // DWORD
0695: int biClrImportant; // DWORD
0696: }
0697:
0698: static class RGBQUAD {
0699: int rgBlue; // BYTE
0700: int rgbGreen; // BYTE
0701: int rgbRed; // BYTE
0702: int rgbReserved; // BYTE
0703: }
0704:
0705: static class BITMAPINFO {
0706: BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
0707: RGBQUAD[] bmiColors = null;
0708: }
0709:
0710: static void read(RandomAccessFile raf, BITMAPINFOHEADER bih)
0711: throws IOException {
0712: bih.biSize = read4(raf);
0713: bih.biWidth = read4(raf);
0714: bih.biHeight = read4(raf);
0715: bih.biPlanes = readU2(raf);
0716: bih.biBitCount = readU2(raf);
0717: bih.biCompression = read4(raf);
0718: bih.biSizeImage = read4(raf);
0719: bih.biXPelsPerMeter = read4(raf);
0720: bih.biYPelsPerMeter = read4(raf);
0721: bih.biClrUsed = read4(raf);
0722: bih.biClrImportant = read4(raf);
0723: }
0724:
0725: static void read(RandomAccessFile raf, BITMAPINFO bi)
0726: throws IOException {
0727: read(raf, bi.bmiHeader);
0728: }
0729:
0730: /* Little Endian helpers */
0731: static int readU2(RandomAccessFile raf) throws IOException {
0732: int b0 = raf.readByte() & 0xFF;
0733: int b1 = raf.readByte() & 0xFF;
0734: return (b1 << 8 | b0);
0735: }
0736:
0737: static int read4(RandomAccessFile raf) throws IOException {
0738: int b0 = raf.readByte() & 0xFF;
0739: int b1 = raf.readByte() & 0xFF;
0740: int b2 = raf.readByte() & 0xFF;
0741: int b3 = raf.readByte() & 0xFF;
0742: return b3 << 24 | b2 << 16 | b1 << 8 | b0;
0743: }
0744:
0745: static void write4(RandomAccessFile raf, int value)
0746: throws IOException {
0747: raf.write(value & 0xFF);
0748: raf.write((value >> 8) & 0xFF);
0749: raf.write((value >> 16) & 0xFF);
0750: raf.write((value >> 24) & 0xFF);
0751: }
0752:
0753: static void writeU2(RandomAccessFile raf, int value)
0754: throws IOException {
0755: raf.write(value & 0xFF);
0756: raf.write((value >> 8) & 0xFF);
0757: }
0758:
0759: static void read(RandomAccessFile raf, IMAGE_DOS_HEADER idh)
0760: throws IOException {
0761: idh.e_magic = readU2(raf);
0762: idh.e_cblp = readU2(raf);
0763: idh.e_cp = readU2(raf);
0764: idh.e_crlc = readU2(raf);
0765: idh.e_cparhdr = readU2(raf);
0766: idh.e_minalloc = readU2(raf);
0767: idh.e_maxalloc = readU2(raf);
0768: idh.e_ss = readU2(raf);
0769: idh.e_sp = readU2(raf);
0770: idh.e_csum = readU2(raf);
0771: idh.e_ip = readU2(raf);
0772: idh.e_cs = readU2(raf);
0773: idh.e_lfarlc = readU2(raf);
0774: idh.e_ovno = readU2(raf);
0775: for (int i = 0; i < idh.e_res.length; i++)
0776: idh.e_res[i] = readU2(raf);
0777: idh.e_oemid = readU2(raf);
0778: idh.e_oeminfo = readU2(raf);
0779: for (int i = 0; i < idh.e_res2.length; i++)
0780: idh.e_res2[i] = readU2(raf);
0781: idh.e_lfanew = read4(raf);
0782: }
0783:
0784: static void read(RandomAccessFile raf, IMAGE_FILE_HEADER ifh)
0785: throws IOException {
0786: ifh.Machine = readU2(raf);
0787: ifh.NumberOfSections = readU2(raf);
0788: ifh.TimeDateStamp = read4(raf);
0789: ifh.PointerToSymbolTable = read4(raf);
0790: ifh.NumberOfSymbols = read4(raf);
0791: ifh.SizeOfOptionalHeader = readU2(raf);
0792: ifh.Characteristics = readU2(raf);
0793: }
0794:
0795: static void read(RandomAccessFile raf, IMAGE_DATA_DIRECTORY idd)
0796: throws IOException {
0797: idd.VirtualAddress = read4(raf);
0798: idd.Size = read4(raf);
0799: }
0800:
0801: static void read(RandomAccessFile raf, IMAGE_OPTIONAL_HEADER ioh)
0802: throws IOException {
0803: ioh.Magic = readU2(raf);
0804: ioh.MajorLinkerVersion = raf.read();
0805: ioh.MinorLinkerVersion = raf.read();
0806: ioh.SizeOfCode = read4(raf);
0807: ioh.SizeOfInitializedData = read4(raf);
0808: ioh.SizeOfUninitializedData = read4(raf);
0809: ioh.AddressOfEntryPoint = read4(raf);
0810: ioh.BaseOfCode = read4(raf);
0811: ioh.BaseOfData = read4(raf);
0812: ioh.ImageBase = read4(raf);
0813: ioh.SectionAlignment = read4(raf);
0814: ioh.FileAlignment = read4(raf);
0815: ioh.MajorOperatingSystemVersion = readU2(raf);
0816: ioh.MinorOperatingSystemVersion = readU2(raf);
0817: ioh.MajorImageVersion = readU2(raf);
0818: ioh.MinorImageVersion = readU2(raf);
0819: ioh.MajorSubsystemVersion = readU2(raf);
0820: ioh.MinorSubsystemVersion = readU2(raf);
0821: ioh.Win32VersionValue = read4(raf);
0822: ioh.SizeOfImage = read4(raf);
0823: ioh.SizeOfHeaders = read4(raf);
0824: ioh.CheckSum = read4(raf);
0825: ioh.Subsystem = readU2(raf);
0826: ioh.DllCharacteristics = readU2(raf);
0827: ioh.SizeOfStackReserve = read4(raf);
0828: ioh.SizeOfStackCommit = read4(raf);
0829: ioh.SizeOfHeapReserve = read4(raf);
0830: ioh.SizeOfHeapCommit = read4(raf);
0831: ioh.LoaderFlags = read4(raf);
0832: ioh.NumberOfRvaAndSizes = read4(raf);
0833: for (int i = 0; i < ioh.DataDirectory.length; i++) {
0834: ioh.DataDirectory[i] = new IMAGE_DATA_DIRECTORY();
0835: read(raf, ioh.DataDirectory[i]);
0836: }
0837: }
0838:
0839: static void read(RandomAccessFile raf, IMAGE_NT_HEADERS inh)
0840: throws IOException {
0841: inh.Signature = read4(raf);
0842: read(raf, inh.FileHeader);
0843: read(raf, inh.OptionalHeader);
0844: }
0845:
0846: static void read(RandomAccessFile raf, IMAGE_SECTION_HEADER ish)
0847: throws IOException {
0848: for (int i = 0; i < ish.Name.length; i++)
0849: ish.Name[i] = raf.read();
0850: ish.Misc_VirtualSize = read4(raf);
0851: ish.VirtualAddress = read4(raf);
0852: ish.SizeOfRawData = read4(raf);
0853: ish.PointerToRawData = read4(raf);
0854: ish.PointerToRelocations = read4(raf);
0855: ish.PointerToLinenumbers = read4(raf);
0856: ish.NumberOfRelocations = readU2(raf);
0857: ish.NumberOfLinenumbers = readU2(raf);
0858: ish.Characteristics = read4(raf);
0859: }
0860:
0861: static void read(RandomAccessFile raf, IMAGE_RESOURCE_DIRECTORY ird)
0862: throws IOException {
0863: ird.Characteristics = read4(raf);
0864: ird.TimeDateStamp = read4(raf);
0865: ird.MajorVersion = readU2(raf);
0866: ird.MinorVersion = readU2(raf);
0867: ird.NumberOfNamedEntries = readU2(raf);
0868: ird.NumberOfIdEntries = readU2(raf);
0869: }
0870:
0871: static void read(RandomAccessFile raf,
0872: IMAGE_RESOURCE_DIRECTORY_ENTRY irde) throws IOException {
0873: irde.Name = read4(raf);
0874: irde.OffsetToData = read4(raf);
0875: // construct other union members
0876: irde.NameOffset = irde.Name & ~(1 << 31);
0877: irde.NameIsString = (irde.Name & (1 << 31)) != 0;
0878: irde.Id = irde.Name & 0xFFFF;
0879: irde.OffsetToDirectory = irde.OffsetToData & ~(1 << 31);
0880: irde.DataIsDirectory = (irde.OffsetToData & (1 << 31)) != 0;
0881: }
0882:
0883: static void read(RandomAccessFile raf,
0884: IMAGE_RESOURCE_DATA_ENTRY irde) throws IOException {
0885: irde.OffsetToData = read4(raf);
0886: irde.Size = read4(raf);
0887: irde.CodePage = read4(raf);
0888: irde.Reserved = read4(raf);
0889: }
0890:
0891: static void read(RandomAccessFile raf, NEWHEADER nh)
0892: throws IOException {
0893: nh.Reserved = readU2(raf);
0894: nh.ResType = readU2(raf);
0895: nh.ResCount = readU2(raf);
0896: }
0897:
0898: static void read(RandomAccessFile raf, ICONRESDIR i)
0899: throws IOException {
0900: i.Width = raf.read();
0901: i.Height = raf.read();
0902: i.ColorCount = raf.read();
0903: i.reserved = raf.read();
0904: }
0905:
0906: static void read(RandomAccessFile raf, CURSORDIR c)
0907: throws IOException {
0908: c.Width = readU2(raf);
0909: c.Height = readU2(raf);
0910: }
0911:
0912: static void read(RandomAccessFile raf, RESDIR rs)
0913: throws IOException {
0914: long start = raf.getFilePointer();
0915: read(raf, rs.Icon);
0916: raf.seek(start);
0917: read(raf, rs.Cursor);
0918: rs.Planes = readU2(raf);
0919: rs.BitCount = readU2(raf);
0920: rs.BytesInRes = read4(raf);
0921: rs.IconCursorId = readU2(raf);
0922: }
0923:
0924: /* ImageData and Image Decoder inlining to avoid dependency on SWT
0925: * The following section can be entirely removed if SWT can be used.
0926: */
0927:
0928: static class RGB {
0929:
0930: /**
0931: * the red component of the RGB
0932: */
0933: public int red;
0934:
0935: /**
0936: * the green component of the RGB
0937: */
0938: public int green;
0939:
0940: /**
0941: * the blue component of the RGB
0942: */
0943: public int blue;
0944:
0945: static final long serialVersionUID = 3258415023461249074L;
0946:
0947: /**
0948: * Constructs an instance of this class with the given
0949: * red, green and blue values.
0950: *
0951: * @param red the red component of the new instance
0952: * @param green the green component of the new instance
0953: * @param blue the blue component of the new instance
0954: *
0955: * @exception IllegalArgumentException <ul>
0956: * <li>ERROR_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li>
0957: * </ul>
0958: */
0959: public RGB(int red, int green, int blue) {
0960: if ((red > 255) || (red < 0) || (green > 255)
0961: || (green < 0) || (blue > 255) || (blue < 0))
0962: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
0963: this .red = red;
0964: this .green = green;
0965: this .blue = blue;
0966: }
0967:
0968: /**
0969: * Compares the argument to the receiver, and returns true
0970: * if they represent the <em>same</em> object using a class
0971: * specific comparison.
0972: *
0973: * @param object the object to compare with this object
0974: * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
0975: *
0976: * @see #hashCode()
0977: */
0978: public boolean equals(Object object) {
0979: if (object == this )
0980: return true;
0981: if (!(object instanceof RGB))
0982: return false;
0983: RGB rgb = (RGB) object;
0984: return (rgb.red == this .red) && (rgb.green == this .green)
0985: && (rgb.blue == this .blue);
0986: }
0987:
0988: /**
0989: * Returns an integer hash code for the receiver. Any two
0990: * objects which return <code>true</code> when passed to
0991: * <code>equals</code> must return the same value for this
0992: * method.
0993: *
0994: * @return the receiver's hash
0995: *
0996: * @see #equals(Object)
0997: */
0998: public int hashCode() {
0999: return (blue << 16) | (green << 8) | red;
1000: }
1001:
1002: /**
1003: * Returns a string containing a concise, human-readable
1004: * description of the receiver.
1005: *
1006: * @return a string representation of the <code>RGB</code>
1007: */
1008: public String toString() {
1009: return "RGB {" + red + ", " + green + ", " + blue + "}"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
1010:
1011: //$NON-NLS-4$
1012: }
1013:
1014: }
1015:
1016: static class PaletteData {
1017:
1018: /**
1019: * true if the receiver is a direct palette,
1020: * and false otherwise
1021: */
1022: public boolean isDirect;
1023:
1024: /**
1025: * the RGB values for an indexed palette, where the
1026: * indices of the array correspond to pixel values
1027: */
1028: public RGB[] colors;
1029:
1030: /**
1031: * the red mask for a direct palette
1032: */
1033: public int redMask;
1034:
1035: /**
1036: * the green mask for a direct palette
1037: */
1038: public int greenMask;
1039:
1040: /**
1041: * the blue mask for a direct palette
1042: */
1043: public int blueMask;
1044:
1045: /**
1046: * the red shift for a direct palette
1047: */
1048: public int redShift;
1049:
1050: /**
1051: * the green shift for a direct palette
1052: */
1053: public int greenShift;
1054:
1055: /**
1056: * the blue shift for a direct palette
1057: */
1058: public int blueShift;
1059:
1060: /**
1061: * Constructs a new indexed palette given an array of RGB values.
1062: *
1063: * @param colors the array of <code>RGB</code>s for the palette
1064: *
1065: * @exception IllegalArgumentException <ul>
1066: * <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
1067: * </ul>
1068: */
1069: public PaletteData(RGB[] colors) {
1070: if (colors == null)
1071: SWT.error(SWT.ERROR_NULL_ARGUMENT);
1072: this .colors = colors;
1073: this .isDirect = false;
1074: }
1075:
1076: /**
1077: * Constructs a new direct palette given the red, green and blue masks.
1078: *
1079: * @param redMask the red mask
1080: * @param greenMask the green mask
1081: * @param blueMask the blue mask
1082: */
1083: public PaletteData(int redMask, int greenMask, int blueMask) {
1084: this .redMask = redMask;
1085: this .greenMask = greenMask;
1086: this .blueMask = blueMask;
1087: this .isDirect = true;
1088: this .redShift = shiftForMask(redMask);
1089: this .greenShift = shiftForMask(greenMask);
1090: this .blueShift = shiftForMask(blueMask);
1091: }
1092:
1093: /**
1094: * Returns the pixel value corresponding to the given <code>RBG</code>.
1095: *
1096: * @param rgb the RGB to get the pixel value for
1097: * @return the pixel value for the given RGB
1098: *
1099: * @exception IllegalArgumentException <ul>
1100: * <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
1101: * <li>ERROR_INVALID_ARGUMENT - if the RGB is not found in the palette</li>
1102: * </ul>
1103: */
1104: public int getPixel(RGB rgb) {
1105: if (rgb == null)
1106: SWT.error(SWT.ERROR_NULL_ARGUMENT);
1107: if (isDirect) {
1108: int pixel = 0;
1109: pixel |= (redShift < 0 ? rgb.red << -redShift
1110: : rgb.red >>> redShift)
1111: & redMask;
1112: pixel |= (greenShift < 0 ? rgb.green << -greenShift
1113: : rgb.green >>> greenShift)
1114: & greenMask;
1115: pixel |= (blueShift < 0 ? rgb.blue << -blueShift
1116: : rgb.blue >>> blueShift)
1117: & blueMask;
1118: return pixel;
1119: }
1120:
1121: for (int i = 0; i < colors.length; i++) {
1122: if (colors[i].equals(rgb))
1123: return i;
1124: }
1125: /* The RGB did not exist in the palette */
1126: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1127: return 0;
1128: }
1129:
1130: /**
1131: * Returns an <code>RGB</code> corresponding to the given pixel value.
1132: *
1133: * @param pixel the pixel to get the RGB value for
1134: * @return the RGB value for the given pixel
1135: *
1136: * @exception IllegalArgumentException <ul>
1137: * <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
1138: * <li>ERROR_INVALID_ARGUMENT - if the pixel does not exist in the palette</li>
1139: * </ul>
1140: */
1141: public RGB getRGB(int pixel) {
1142: if (isDirect) {
1143: int r = pixel & redMask;
1144: r = (redShift < 0) ? r >>> -redShift : r << redShift;
1145: int g = pixel & greenMask;
1146: g = (greenShift < 0) ? g >>> -greenShift
1147: : g << greenShift;
1148: int b = pixel & blueMask;
1149: b = (blueShift < 0) ? b >>> -blueShift : b << blueShift;
1150: return new RGB(r, g, b);
1151: }
1152: if (pixel < 0 || pixel >= colors.length) {
1153: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1154: }
1155: return colors[pixel];
1156: }
1157:
1158: /**
1159: * Returns all the RGB values in the receiver if it is an
1160: * indexed palette, or null if it is a direct palette.
1161: *
1162: * @return the <code>RGB</code>s for the receiver or null
1163: */
1164: public RGB[] getRGBs() {
1165: return colors;
1166: }
1167:
1168: /**
1169: * Computes the shift value for a given mask.
1170: *
1171: * @param mask the mask to compute the shift for
1172: * @return the shift amount
1173: *
1174: * @see PaletteData
1175: */
1176: int shiftForMask(int mask) {
1177: for (int i = 31; i >= 0; i--) {
1178: if (((mask >> i) & 0x1) != 0)
1179: return 7 - i;
1180: }
1181: return 32;
1182: }
1183:
1184: }
1185:
1186: static class ImageLoader {
1187:
1188: /**
1189: * the array of ImageData objects in this ImageLoader.
1190: * This array is read in when the load method is called,
1191: * and it is written out when the save method is called
1192: */
1193: public ImageData[] data;
1194:
1195: /**
1196: * the width of the logical screen on which the images
1197: * reside, in pixels (this corresponds to the GIF89a
1198: * Logical Screen Width value)
1199: */
1200: public int logicalScreenWidth;
1201:
1202: /**
1203: * the height of the logical screen on which the images
1204: * reside, in pixels (this corresponds to the GIF89a
1205: * Logical Screen Height value)
1206: */
1207: public int logicalScreenHeight;
1208:
1209: /**
1210: * the background pixel for the logical screen (this
1211: * corresponds to the GIF89a Background Color Index value).
1212: * The default is -1 which means 'unspecified background'
1213: *
1214: */
1215: public int backgroundPixel;
1216:
1217: /**
1218: * the number of times to repeat the display of a sequence
1219: * of animated images (this corresponds to the commonly-used
1220: * GIF application extension for "NETSCAPE 2.0 01")
1221: */
1222: public int repeatCount;
1223:
1224: /*
1225: * the set of ImageLoader event listeners, created on demand
1226: */
1227: Vector imageLoaderListeners;
1228:
1229: /**
1230: * Construct a new empty ImageLoader.
1231: */
1232: public ImageLoader() {
1233: reset();
1234: }
1235:
1236: /**
1237: * Resets the fields of the ImageLoader, except for the
1238: * <code>imageLoaderListeners</code> field.
1239: */
1240: void reset() {
1241: data = null;
1242: logicalScreenWidth = 0;
1243: logicalScreenHeight = 0;
1244: backgroundPixel = -1;
1245: repeatCount = 1;
1246: }
1247:
1248: /**
1249: * Loads an array of <code>ImageData</code> objects from the
1250: * specified input stream. Throws an error if either an error
1251: * occurs while loading the images, or if the images are not
1252: * of a supported type. Returns the loaded image data array.
1253: *
1254: * @param stream the input stream to load the images from
1255: * @return an array of <code>ImageData</code> objects loaded from the specified input stream
1256: *
1257: * @exception IllegalArgumentException <ul>
1258: * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
1259: * </ul>
1260: * @exception SWTException <ul>
1261: * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
1262: * <li>ERROR_IO - if an input/output error occurs while reading data</li>
1263: * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
1264: * </ul>
1265: */
1266: public ImageData[] load(InputStream stream) {
1267: if (stream == null)
1268: SWT.error(SWT.ERROR_NULL_ARGUMENT);
1269: reset();
1270: data = FileFormat.load(stream, this );
1271: return data;
1272: }
1273:
1274: /**
1275: * Loads an array of <code>ImageData</code> objects from the
1276: * file with the specified name. Throws an error if either
1277: * an error occurs while loading the images, or if the images are
1278: * not of a supported type. Returns the loaded image data array.
1279: *
1280: * @param filename the name of the file to load the images from
1281: * @return an array of <code>ImageData</code> objects loaded from the specified file
1282: *
1283: * @exception IllegalArgumentException <ul>
1284: * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
1285: * </ul>
1286: * @exception SWTException <ul>
1287: * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
1288: * <li>ERROR_IO - if an IO error occurs while reading data</li>
1289: * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
1290: * </ul>
1291: */
1292: public ImageData[] load(String filename) {
1293: if (filename == null)
1294: SWT.error(SWT.ERROR_NULL_ARGUMENT);
1295: InputStream stream = null;
1296: try {
1297: stream = new BufferedInputStream(new FileInputStream(
1298: filename));
1299: return load(stream);
1300: } catch (IOException e) {
1301: SWT.error(SWT.ERROR_IO, e);
1302: } finally {
1303: try {
1304: if (stream != null)
1305: stream.close();
1306: } catch (IOException e) {
1307: // Ignore error
1308: }
1309: }
1310: return null;
1311: }
1312: }
1313:
1314: static class ImageData {
1315:
1316: /**
1317: * The width of the image, in pixels.
1318: */
1319: public int width;
1320:
1321: /**
1322: * The height of the image, in pixels.
1323: */
1324: public int height;
1325:
1326: /**
1327: * The color depth of the image, in bits per pixel.
1328: * <p>
1329: * Note that a depth of 8 or less does not necessarily
1330: * mean that the image is palette indexed, or
1331: * conversely that a depth greater than 8 means that
1332: * the image is direct color. Check the associated
1333: * PaletteData's isDirect field for such determinations.
1334: */
1335: public int depth;
1336:
1337: /**
1338: * The scanline padding.
1339: * <p>
1340: * If one scanline of the image is not a multiple of
1341: * this number, it will be padded with zeros until it is.
1342: * </p>
1343: */
1344: public int scanlinePad;
1345:
1346: /**
1347: * The number of bytes per scanline.
1348: * <p>
1349: * This is a multiple of the scanline padding.
1350: * </p>
1351: */
1352: public int bytesPerLine;
1353:
1354: /**
1355: * The pixel data of the image.
1356: * <p>
1357: * Note that for 16 bit depth images the pixel data is stored
1358: * in least significant byte order; however, for 24bit and
1359: * 32bit depth images the pixel data is stored in most
1360: * significant byte order.
1361: * </p>
1362: */
1363: public byte[] data;
1364:
1365: /**
1366: * The color table for the image.
1367: */
1368: public PaletteData palette;
1369:
1370: /**
1371: * The transparent pixel.
1372: * <p>
1373: * Pixels with this value are transparent.
1374: * </p><p>
1375: * The default is -1 which means 'no transparent pixel'.
1376: * </p>
1377: */
1378: public int transparentPixel;
1379:
1380: /**
1381: * An icon-specific field containing the data from the icon mask.
1382: * <p>
1383: * This is a 1 bit bitmap stored with the most significant
1384: * bit first. The number of bytes per scanline is
1385: * '((width + 7) / 8 + (maskPad - 1)) / maskPad * maskPad'.
1386: * </p><p>
1387: * The default is null which means 'no transparency mask'.
1388: * </p>
1389: */
1390: public byte[] maskData;
1391:
1392: /**
1393: * An icon-specific field containing the scanline pad of the mask.
1394: * <p>
1395: * If one scanline of the transparency mask is not a
1396: * multiple of this number, it will be padded with zeros until
1397: * it is.
1398: * </p>
1399: */
1400: public int maskPad;
1401:
1402: /**
1403: * The alpha data of the image.
1404: * <p>
1405: * Every pixel can have an <em>alpha blending</em> value that
1406: * varies from 0, meaning fully transparent, to 255 meaning
1407: * fully opaque. The number of bytes per scanline is
1408: * 'width'.
1409: * </p>
1410: */
1411: public byte[] alphaData;
1412:
1413: /**
1414: * The global alpha value to be used for every pixel.
1415: * <p>
1416: * If this value is set, the <code>alphaData</code> field
1417: * is ignored and when the image is rendered each pixel
1418: * will be blended with the background an amount
1419: * proportional to this value.
1420: * </p><p>
1421: * The default is -1 which means 'no global alpha value'
1422: * </p>
1423: */
1424: public int alpha;
1425:
1426: /**
1427: * The type of file from which the image was read.
1428: *
1429: * It is expressed as one of the following values:
1430: * <dl>
1431: * <dt><code>IMAGE_BMP</code></dt>
1432: * <dd>Windows BMP file format, no compression</dd>
1433: * <dt><code>IMAGE_BMP_RLE</code></dt>
1434: * <dd>Windows BMP file format, RLE compression if appropriate</dd>
1435: * <dt><code>IMAGE_GIF</code></dt>
1436: * <dd>GIF file format</dd>
1437: * <dt><code>IMAGE_ICO</code></dt>
1438: * <dd>Windows ICO file format</dd>
1439: * <dt><code>IMAGE_JPEG</code></dt>
1440: * <dd>JPEG file format</dd>
1441: * <dt><code>IMAGE_PNG</code></dt>
1442: * <dd>PNG file format</dd>
1443: * </dl>
1444: */
1445: public int type;
1446:
1447: /**
1448: * The x coordinate of the top left corner of the image
1449: * within the logical screen (this field corresponds to
1450: * the GIF89a Image Left Position value).
1451: */
1452: public int x;
1453:
1454: /**
1455: * The y coordinate of the top left corner of the image
1456: * within the logical screen (this field corresponds to
1457: * the GIF89a Image Top Position value).
1458: */
1459: public int y;
1460:
1461: /**
1462: * A description of how to dispose of the current image
1463: * before displaying the next.
1464: *
1465: * It is expressed as one of the following values:
1466: * <dl>
1467: * <dt><code>DM_UNSPECIFIED</code></dt>
1468: * <dd>disposal method not specified</dd>
1469: * <dt><code>DM_FILL_NONE</code></dt>
1470: * <dd>do nothing - leave the image in place</dd>
1471: * <dt><code>DM_FILL_BACKGROUND</code></dt>
1472: * <dd>fill with the background color</dd>
1473: * <dt><code>DM_FILL_PREVIOUS</code></dt>
1474: * <dd>restore the previous picture</dd>
1475: * </dl>
1476: * (this field corresponds to the GIF89a Disposal Method value)
1477: */
1478: public int disposalMethod;
1479:
1480: /**
1481: * The time to delay before displaying the next image
1482: * in an animation (this field corresponds to the GIF89a
1483: * Delay Time value).
1484: */
1485: public int delayTime;
1486:
1487: /**
1488: * Arbitrary channel width data to 8-bit conversion table.
1489: */
1490: static final byte[][] ANY_TO_EIGHT = new byte[9][];
1491: static {
1492: for (int b = 0; b < 9; ++b) {
1493: byte[] data = ANY_TO_EIGHT[b] = new byte[1 << b];
1494: if (b == 0)
1495: continue;
1496: int inc = 0;
1497: for (int bit = 0x10000; (bit >>= b) != 0;)
1498: inc |= bit;
1499: for (int v = 0, p = 0; v < 0x10000; v += inc)
1500: data[p++] = (byte) (v >> 8);
1501: }
1502: }
1503: static final byte[] ONE_TO_ONE_MAPPING = ANY_TO_EIGHT[8];
1504:
1505: /**
1506: * Scaled 8x8 Bayer dither matrix.
1507: */
1508: static final int[][] DITHER_MATRIX = {
1509: { 0xfc0000, 0x7c0000, 0xdc0000, 0x5c0000, 0xf40000,
1510: 0x740000, 0xd40000, 0x540000 },
1511: { 0x3c0000, 0xbc0000, 0x1c0000, 0x9c0000, 0x340000,
1512: 0xb40000, 0x140000, 0x940000 },
1513: { 0xcc0000, 0x4c0000, 0xec0000, 0x6c0000, 0xc40000,
1514: 0x440000, 0xe40000, 0x640000 },
1515: { 0x0c0000, 0x8c0000, 0x2c0000, 0xac0000, 0x040000,
1516: 0x840000, 0x240000, 0xa40000 },
1517: { 0xf00000, 0x700000, 0xd00000, 0x500000, 0xf80000,
1518: 0x780000, 0xd80000, 0x580000 },
1519: { 0x300000, 0xb00000, 0x100000, 0x900000, 0x380000,
1520: 0xb80000, 0x180000, 0x980000 },
1521: { 0xc00000, 0x400000, 0xe00000, 0x600000, 0xc80000,
1522: 0x480000, 0xe80000, 0x680000 },
1523: { 0x000000, 0x800000, 0x200000, 0xa00000, 0x080000,
1524: 0x880000, 0x280000, 0xa80000 } };
1525:
1526: /**
1527: * Constructs a new, empty ImageData with the given width, height,
1528: * depth and palette. The data will be initialized to an (all zero)
1529: * array of the appropriate size.
1530: *
1531: * @param width the width of the image
1532: * @param height the height of the image
1533: * @param depth the depth of the image
1534: * @param palette the palette of the image (must not be null)
1535: *
1536: * @exception IllegalArgumentException <ul>
1537: * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
1538: * one of 1, 2, 4, 8, 16, 24 or 32</li>
1539: * <li>ERROR_NULL_ARGUMENT - if the palette is null</li>
1540: * </ul>
1541: */
1542: public ImageData(int width, int height, int depth,
1543: PaletteData palette) {
1544: this (width, height, depth, palette, 4, null, 0, null, null,
1545: -1, -1, SWT.IMAGE_UNDEFINED, 0, 0, 0, 0);
1546: }
1547:
1548: /**
1549: * Constructs a new, empty ImageData with the given width, height,
1550: * depth, palette, scanlinePad and data.
1551: *
1552: * @param width the width of the image
1553: * @param height the height of the image
1554: * @param depth the depth of the image
1555: * @param palette the palette of the image
1556: * @param scanlinePad the padding of each line, in bytes
1557: * @param data the data of the image
1558: *
1559: * @exception IllegalArgumentException <ul>
1560: * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
1561: * one of 1, 2, 4, 8, 16, 24 or 32</li>
1562: * <li>ERROR_NULL_ARGUMENT - if the palette or data is null</li>
1563: * <li>ERROR_CANNOT_BE_ZERO - if the scanlinePad is zero</li>
1564: * </ul>
1565: */
1566: public ImageData(int width, int height, int depth,
1567: PaletteData palette, int scanlinePad, byte[] data) {
1568: this (width, height, depth, palette, scanlinePad,
1569: checkData(data), 0, null, null, -1, -1,
1570: SWT.IMAGE_UNDEFINED, 0, 0, 0, 0);
1571: }
1572:
1573: /**
1574: * Constructs an <code>ImageData</code> loaded from a file with the
1575: * specified name. Throws an error if an error occurs loading the
1576: * image, or if the image has an unsupported type.
1577: * <p>
1578: * This constructor is provided for convenience when loading a single
1579: * image only. If the file contains multiple images, only the first
1580: * one will be loaded. To load multiple images, use
1581: * <code>ImageLoader.load()</code>.
1582: * </p>
1583: *
1584: * @param filename the name of the file to load the image from (must not be null)
1585: *
1586: * @exception IllegalArgumentException <ul>
1587: * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
1588: * </ul>
1589: * @exception SWTException <ul>
1590: * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
1591: * <li>ERROR_IO if an IO error occurs while reading data</li>
1592: * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
1593: * </ul>
1594: */
1595: public ImageData(String filename) {
1596: ImageData[] data = new ImageLoader().load(filename);
1597: if (data.length < 1)
1598: SWT.error(SWT.ERROR_INVALID_IMAGE);
1599: ImageData i = data[0];
1600: setAllFields(i.width, i.height, i.depth, i.scanlinePad,
1601: i.bytesPerLine, i.data, i.palette,
1602: i.transparentPixel, i.maskData, i.maskPad,
1603: i.alphaData, i.alpha, i.type, i.x, i.y,
1604: i.disposalMethod, i.delayTime);
1605: }
1606:
1607: /**
1608: * Prevents uninitialized instances from being created outside the package.
1609: */
1610: ImageData() {
1611: //empty constructor
1612: }
1613:
1614: /**
1615: * Constructs an image data by giving values for all non-computable fields.
1616: * <p>
1617: * This method is for internal use, and is not described further.
1618: * </p>
1619: */
1620: ImageData(int width, int height, int depth,
1621: PaletteData palette, int scanlinePad, byte[] data,
1622: int maskPad, byte[] maskData, byte[] alphaData,
1623: int alpha, int transparentPixel, int type, int x,
1624: int y, int disposalMethod, int delayTime) {
1625:
1626: if (palette == null)
1627: SWT.error(SWT.ERROR_NULL_ARGUMENT);
1628: if (!(depth == 1 || depth == 2 || depth == 4 || depth == 8
1629: || depth == 16 || depth == 24 || depth == 32)) {
1630: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1631: }
1632: if (width <= 0 || height <= 0) {
1633: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1634: }
1635: if (scanlinePad == 0)
1636: SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
1637:
1638: int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1))
1639: / scanlinePad * scanlinePad;
1640: setAllFields(width, height, depth, scanlinePad,
1641: bytesPerLine, data != null ? data
1642: : new byte[bytesPerLine * height], palette,
1643: transparentPixel, maskData, maskPad, alphaData,
1644: alpha, type, x, y, disposalMethod, delayTime);
1645: }
1646:
1647: /**
1648: * Initializes all fields in the receiver. This method must be called
1649: * by all public constructors to ensure that all fields are initialized
1650: * for a new ImageData object. If a new field is added to the class,
1651: * then it must be added to this method.
1652: * <p>
1653: * This method is for internal use, and is not described further.
1654: * </p>
1655: */
1656: void setAllFields(int width, int height, int depth,
1657: int scanlinePad, int bytesPerLine, byte[] data,
1658: PaletteData palette, int transparentPixel,
1659: byte[] maskData, int maskPad, byte[] alphaData,
1660: int alpha, int type, int x, int y, int disposalMethod,
1661: int delayTime) {
1662:
1663: this .width = width;
1664: this .height = height;
1665: this .depth = depth;
1666: this .scanlinePad = scanlinePad;
1667: this .bytesPerLine = bytesPerLine;
1668: this .data = data;
1669: this .palette = palette;
1670: this .transparentPixel = transparentPixel;
1671: this .maskData = maskData;
1672: this .maskPad = maskPad;
1673: this .alphaData = alphaData;
1674: this .alpha = alpha;
1675: this .type = type;
1676: this .x = x;
1677: this .y = y;
1678: this .disposalMethod = disposalMethod;
1679: this .delayTime = delayTime;
1680: }
1681:
1682: /**
1683: * Invokes internal SWT functionality to create a new instance of
1684: * this class.
1685: * <p>
1686: * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
1687: * API for <code>ImageData</code>. It is marked public only so that it
1688: * can be shared within the packages provided by SWT. It is subject
1689: * to change without notice, and should never be called from
1690: * application code.
1691: * </p>
1692: * <p>
1693: * This method is for internal use, and is not described further.
1694: * </p>
1695: */
1696: public static ImageData internal_new(int width, int height,
1697: int depth, PaletteData palette, int scanlinePad,
1698: byte[] data, int maskPad, byte[] maskData,
1699: byte[] alphaData, int alpha, int transparentPixel,
1700: int type, int x, int y, int disposalMethod,
1701: int delayTime) {
1702: return new ImageData(width, height, depth, palette,
1703: scanlinePad, data, maskPad, maskData, alphaData,
1704: alpha, transparentPixel, type, x, y,
1705: disposalMethod, delayTime);
1706: }
1707:
1708: ImageData colorMaskImage(int pixel) {
1709: ImageData mask = new ImageData(width, height, 1,
1710: bwPalette(), 2, null, 0, null, null, -1, -1,
1711: SWT.IMAGE_UNDEFINED, 0, 0, 0, 0);
1712: int[] row = new int[width];
1713: for (int y = 0; y < height; y++) {
1714: getPixels(0, y, width, row, 0);
1715: for (int i = 0; i < width; i++) {
1716: if (pixel != -1 && row[i] == pixel) {
1717: row[i] = 0;
1718: } else {
1719: row[i] = 1;
1720: }
1721: }
1722: mask.setPixels(0, y, width, row, 0);
1723: }
1724: return mask;
1725: }
1726:
1727: static byte[] checkData(byte[] data) {
1728: if (data == null)
1729: SWT.error(SWT.ERROR_NULL_ARGUMENT);
1730: return data;
1731: }
1732:
1733: /**
1734: * Returns <code>getWidth</code> pixel values starting at offset
1735: * <code>x</code> in scanline <code>y</code> in the receiver's
1736: * data starting at <code>startIndex</code>.
1737: *
1738: * @param x the x position of the first pixel to get
1739: * @param y the y position of the first pixel to get
1740: * @param getWidth the width of the data to get
1741: * @param pixels the buffer in which to put the pixels
1742: * @param startIndex the offset into the byte array to begin storing pixels
1743: *
1744: * @exception IndexOutOfBoundsException if getWidth is too large
1745: * @exception IllegalArgumentException <ul>
1746: * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1747: * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1748: * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
1749: * </ul>
1750: * @exception SWTException <ul>
1751: * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4 or 8
1752: * (For higher depths, use the int[] version of this method.)</li>
1753: * </ul>
1754: */
1755: public void getPixels(int x, int y, int getWidth,
1756: byte[] pixels, int startIndex) {
1757: if (pixels == null)
1758: SWT.error(SWT.ERROR_NULL_ARGUMENT);
1759: if (getWidth < 0 || x >= width || y >= height || x < 0
1760: || y < 0)
1761: SWT.error
1762:
1763: (SWT.ERROR_INVALID_ARGUMENT);
1764: if (getWidth == 0)
1765: return;
1766: int index;
1767: int theByte;
1768: int mask = 0;
1769: int n = getWidth;
1770: int i = startIndex;
1771: int srcX = x, srcY = y;
1772: if (depth == 1) {
1773: index = (y * bytesPerLine) + (x >> 3);
1774: theByte = data[index] & 0xFF;
1775: while (n > 0) {
1776: mask = 1 << (7 - (srcX & 0x7));
1777: if ((theByte & mask) == 0) {
1778: pixels[i] = 0;
1779: } else {
1780: pixels[i] = 1;
1781: }
1782: i++;
1783: n--;
1784: srcX++;
1785: if (srcX >= width) {
1786: srcY++;
1787: index = srcY * bytesPerLine;
1788: if (n > 0)
1789: theByte = data[index] & 0xFF;
1790: srcX = 0;
1791: } else {
1792: if (mask == 1) {
1793: index++;
1794: if (n > 0)
1795: theByte = data[index] & 0xFF;
1796: }
1797: }
1798: }
1799: return;
1800: }
1801: if (depth == 2) {
1802: index = (y * bytesPerLine) + (x >> 2);
1803: theByte = data[index] & 0xFF;
1804: int offset;
1805: while (n > 0) {
1806: offset = 3 - (srcX % 4);
1807: mask = 3 << (offset * 2);
1808: pixels[i] = (byte) ((theByte & mask) >> (offset * 2));
1809: i++;
1810: n--;
1811: srcX++;
1812: if (srcX >= width) {
1813: srcY++;
1814: index = srcY * bytesPerLine;
1815: if (n > 0)
1816: theByte = data[index] & 0xFF;
1817: srcX = 0;
1818: } else {
1819: if (offset == 0) {
1820: index++;
1821: theByte = data[index] & 0xFF;
1822: }
1823: }
1824: }
1825: return;
1826: }
1827: if (depth == 4) {
1828: index = (y * bytesPerLine) + (x >> 1);
1829: if ((x & 0x1) == 1) {
1830: theByte = data[index] & 0xFF;
1831: pixels[i] = (byte) (theByte & 0x0F);
1832: i++;
1833: n--;
1834: srcX++;
1835: if (srcX >= width) {
1836: srcY++;
1837: index = srcY * bytesPerLine;
1838: srcX = 0;
1839: } else {
1840: index++;
1841: }
1842: }
1843: while (n > 1) {
1844: theByte = data[index] & 0xFF;
1845: pixels[i] = (byte) (theByte >> 4);
1846: i++;
1847: n--;
1848: srcX++;
1849: if (srcX >= width) {
1850: srcY++;
1851: index = srcY * bytesPerLine;
1852: srcX = 0;
1853: } else {
1854: pixels[i] = (byte) (theByte & 0x0F);
1855: i++;
1856: n--;
1857: srcX++;
1858: if (srcX >= width) {
1859: srcY++;
1860: index = srcY * bytesPerLine;
1861: srcX = 0;
1862: } else {
1863: index++;
1864: }
1865: }
1866: }
1867: if (n > 0) {
1868: theByte = data[index] & 0xFF;
1869: pixels[i] = (byte) (theByte >> 4);
1870: }
1871: return;
1872: }
1873: if (depth == 8) {
1874: index = (y * bytesPerLine) + x;
1875: for (int j = 0; j < getWidth; j++) {
1876: pixels[i] = data[index];
1877: i++;
1878: srcX++;
1879: if (srcX >= width) {
1880: srcY++;
1881: index = srcY * bytesPerLine;
1882: srcX = 0;
1883: } else {
1884: index++;
1885: }
1886: }
1887: return;
1888: }
1889: SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
1890: }
1891:
1892: /**
1893: * Returns <code>getWidth</code> pixel values starting at offset
1894: * <code>x</code> in scanline <code>y</code> in the receiver's
1895: * data starting at <code>startIndex</code>.
1896: *
1897: * @param x the x position of the first pixel to get
1898: * @param y the y position of the first pixel to get
1899: * @param getWidth the width of the data to get
1900: * @param pixels the buffer in which to put the pixels
1901: * @param startIndex the offset into the buffer to begin storing pixels
1902: *
1903: * @exception IndexOutOfBoundsException if getWidth is too large
1904: * @exception IllegalArgumentException <ul>
1905: * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1906: * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1907: * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
1908: * </ul>
1909: * @exception SWTException <ul>
1910: * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
1911: * </ul>
1912: */
1913: public void getPixels(int x, int y, int getWidth, int[] pixels,
1914: int startIndex) {
1915: if (pixels == null)
1916: SWT.error(SWT.ERROR_NULL_ARGUMENT);
1917: if (getWidth < 0 || x >= width || y >= height || x < 0
1918: || y < 0)
1919: SWT.error
1920:
1921: (SWT.ERROR_INVALID_ARGUMENT);
1922: if (getWidth == 0)
1923: return;
1924: int index;
1925: int theByte;
1926: int mask;
1927: int n = getWidth;
1928: int i = startIndex;
1929: int srcX = x, srcY = y;
1930: if (depth == 1) {
1931: index = (y * bytesPerLine) + (x >> 3);
1932: theByte = data[index] & 0xFF;
1933: while (n > 0) {
1934: mask = 1 << (7 - (srcX & 0x7));
1935: if ((theByte & mask) == 0) {
1936: pixels[i] = 0;
1937: } else {
1938: pixels[i] = 1;
1939: }
1940: i++;
1941: n--;
1942: srcX++;
1943: if (srcX >= width) {
1944: srcY++;
1945: index = srcY * bytesPerLine;
1946: if (n > 0)
1947: theByte = data[index] & 0xFF;
1948: srcX = 0;
1949: } else {
1950: if (mask == 1) {
1951: index++;
1952: if (n > 0)
1953: theByte = data[index] & 0xFF;
1954: }
1955: }
1956: }
1957: return;
1958: }
1959: if (depth == 2) {
1960: index = (y * bytesPerLine) + (x >> 2);
1961: theByte = data[index] & 0xFF;
1962: int offset;
1963: while (n > 0) {
1964: offset = 3 - (srcX % 4);
1965: mask = 3 << (offset * 2);
1966: pixels[i] = (byte) ((theByte & mask) >> (offset * 2));
1967: i++;
1968: n--;
1969: srcX++;
1970: if (srcX >= width) {
1971: srcY++;
1972: index = srcY * bytesPerLine;
1973: if (n > 0)
1974: theByte = data[index] & 0xFF;
1975: srcX = 0;
1976: } else {
1977: if (offset == 0) {
1978: index++;
1979: theByte = data[index] & 0xFF;
1980: }
1981: }
1982: }
1983: return;
1984: }
1985: if (depth == 4) {
1986: index = (y * bytesPerLine) + (x >> 1);
1987: if ((x & 0x1) == 1) {
1988: theByte = data[index] & 0xFF;
1989: pixels[i] = theByte & 0x0F;
1990: i++;
1991: n--;
1992: srcX++;
1993: if (srcX >= width) {
1994: srcY++;
1995: index = srcY * bytesPerLine;
1996: srcX = 0;
1997: } else {
1998: index++;
1999: }
2000: }
2001: while (n > 1) {
2002: theByte = data[index] & 0xFF;
2003: pixels[i] = theByte >> 4;
2004: i++;
2005: n--;
2006: srcX++;
2007: if (srcX >= width) {
2008: srcY++;
2009: index = srcY * bytesPerLine;
2010: srcX = 0;
2011: } else {
2012: pixels[i] = theByte & 0x0F;
2013: i++;
2014: n--;
2015: srcX++;
2016: if (srcX >= width) {
2017: srcY++;
2018: index = srcY * bytesPerLine;
2019: srcX = 0;
2020: } else {
2021: index++;
2022: }
2023: }
2024: }
2025: if (n > 0) {
2026: theByte = data[index] & 0xFF;
2027: pixels[i] = theByte >> 4;
2028: }
2029: return;
2030: }
2031: if (depth == 8) {
2032: index = (y * bytesPerLine) + x;
2033: for (int j = 0; j < getWidth; j++) {
2034: pixels[i] = data[index] & 0xFF;
2035: i++;
2036: srcX++;
2037: if (srcX >= width) {
2038: srcY++;
2039: index = srcY * bytesPerLine;
2040: srcX = 0;
2041: } else {
2042: index++;
2043: }
2044: }
2045: return;
2046: }
2047: if (depth == 16) {
2048: index = (y * bytesPerLine) + (x * 2);
2049: for (int j = 0; j < getWidth; j++) {
2050: pixels[i] = ((data[index + 1] & 0xFF) << 8)
2051: + (data[index] & 0xFF);
2052: i++;
2053: srcX++;
2054: if (srcX >= width) {
2055: srcY++;
2056: index = srcY * bytesPerLine;
2057: srcX = 0;
2058: } else {
2059: index += 2;
2060: }
2061: }
2062: return;
2063: }
2064: if (depth == 24) {
2065: index = (y * bytesPerLine) + (x * 3);
2066: for (int j = 0; j < getWidth; j++) {
2067: pixels[i] = ((data[index] & 0xFF) << 16)
2068: | ((data[index + 1] & 0xFF) << 8)
2069: | (data[index + 2] & 0xFF);
2070: i++;
2071: srcX++;
2072: if (srcX >= width) {
2073: srcY++;
2074: index = srcY * bytesPerLine;
2075: srcX = 0;
2076: } else {
2077: index += 3;
2078: }
2079: }
2080: return;
2081: }
2082: if (depth == 32) {
2083: index = (y * bytesPerLine) + (x * 4);
2084: i = startIndex;
2085: for (int j = 0; j < getWidth; j++) {
2086: pixels[i] = ((data[index] & 0xFF) << 24)
2087: | ((data[index + 1] & 0xFF) << 16)
2088: | ((data[index + 2] & 0xFF) << 8)
2089: | (data[index + 3] & 0xFF);
2090: i++;
2091: srcX++;
2092: if (srcX >= width) {
2093: srcY++;
2094: index = srcY * bytesPerLine;
2095: srcX = 0;
2096: } else {
2097: index += 4;
2098: }
2099: }
2100: return;
2101: }
2102: SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
2103: }
2104:
2105: /**
2106: * Returns an array of <code>RGB</code>s which comprise the
2107: * indexed color table of the receiver, or null if the receiver
2108: * has a direct color model.
2109: *
2110: * @return the RGB values for the image or null if direct color
2111: *
2112: * @see PaletteData#getRGBs()
2113: */
2114: public RGB[] getRGBs() {
2115: return palette.getRGBs();
2116: }
2117:
2118: /**
2119: * Returns an <code>ImageData</code> which specifies the
2120: * transparency mask information for the receiver, or null if the
2121: * receiver has no transparency and is not an icon.
2122: *
2123: * @return the transparency mask or null if none exists
2124: */
2125: public ImageData getTransparencyMask() {
2126: if (getTransparencyType() == SWT.TRANSPARENCY_MASK) {
2127: return new ImageData(width, height, 1, bwPalette(),
2128: maskPad, maskData);
2129: }
2130: return colorMaskImage(transparentPixel);
2131: }
2132:
2133: /**
2134: * Returns the image transparency type.
2135: *
2136: * @return the receiver's transparency type
2137: */
2138: public int getTransparencyType() {
2139: if (maskData != null)
2140: return SWT.TRANSPARENCY_MASK;
2141: if (transparentPixel != -1)
2142: return SWT.TRANSPARENCY_PIXEL;
2143: if (alphaData != null)
2144: return SWT.TRANSPARENCY_ALPHA;
2145: return SWT.TRANSPARENCY_NONE;
2146: }
2147:
2148: /**
2149: * Returns the byte order of the receiver.
2150: *
2151: * @return MSB_FIRST or LSB_FIRST
2152: */
2153: int getByteOrder() {
2154: return depth != 16 ? MSB_FIRST : LSB_FIRST;
2155: }
2156:
2157: /**
2158: * Sets the pixel values starting at offset <code>x</code> in
2159: * scanline <code>y</code> in the receiver's data to the
2160: * values from the array <code>pixels</code> starting at
2161: * <code>startIndex</code>.
2162: *
2163: * @param x the x position of the pixel to set
2164: * @param y the y position of the pixel to set
2165: * @param putWidth the width of the pixels to set
2166: * @param pixels the pixels to set
2167: * @param startIndex the index at which to begin setting
2168: *
2169: * @exception IndexOutOfBoundsException if putWidth is too large
2170: * @exception IllegalArgumentException <ul>
2171: * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
2172: * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
2173: * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
2174: * </ul>
2175: * @exception SWTException <ul>
2176: * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8
2177: * (For higher depths, use the int[] version of this method.)</li>
2178: * </ul>
2179: */
2180: public void setPixels(int x, int y, int putWidth,
2181: byte[] pixels, int startIndex) {
2182: if (pixels == null)
2183: SWT.error(SWT.ERROR_NULL_ARGUMENT);
2184: if (putWidth < 0 || x >= width || y >= height || x < 0
2185: || y < 0)
2186: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2187: if (putWidth == 0)
2188: return;
2189: int index;
2190: int theByte;
2191: int mask;
2192: int n = putWidth;
2193: int i = startIndex;
2194: int srcX = x, srcY = y;
2195: if (depth == 1) {
2196: index = (y * bytesPerLine) + (x >> 3);
2197: while (n > 0) {
2198: mask = 1 << (7 - (srcX & 0x7));
2199: if ((pixels[i] & 0x1) == 1) {
2200: data[index] = (byte) ((data[index] & 0xFF) | mask);
2201: } else {
2202: data[index] = (byte) ((data[index] & 0xFF) & (mask ^ -1));
2203: }
2204: i++;
2205: n--;
2206: srcX++;
2207: if (srcX >= width) {
2208: srcY++;
2209: index = srcY * bytesPerLine;
2210: srcX = 0;
2211: } else {
2212: if (mask == 1) {
2213: index++;
2214: }
2215: }
2216: }
2217: return;
2218: }
2219: if (depth == 2) {
2220: byte[] masks = { (byte) 0xFC, (byte) 0xF3, (byte) 0xCF,
2221: (byte) 0x3F };
2222: index = (y * bytesPerLine) + (x >> 2);
2223: int offset = 3 - (x % 4);
2224: while (n > 0) {
2225: theByte = pixels[i] & 0x3;
2226: data[index] = (byte) ((data[index] & masks[offset]) | (theByte << (offset * 2)));
2227: i++;
2228: n--;
2229: srcX++;
2230: if (srcX >= width) {
2231: srcY++;
2232: index = srcY * bytesPerLine;
2233: offset = 0;
2234: srcX = 0;
2235: } else {
2236: if (offset == 0) {
2237: index++;
2238: offset = 3;
2239: } else {
2240: offset--;
2241: }
2242: }
2243: }
2244: return;
2245: }
2246: if (depth == 4) {
2247: index = (y * bytesPerLine) + (x >> 1);
2248: boolean high = (x & 0x1) == 0;
2249: while (n > 0) {
2250: theByte = pixels[i] & 0x0F;
2251: if (high) {
2252: data[index] = (byte) ((data[index] & 0x0F) | (theByte << 4));
2253: } else {
2254: data[index] = (byte) ((data[index] & 0xF0) | theByte);
2255: }
2256: i++;
2257: n--;
2258: srcX++;
2259: if (srcX >= width) {
2260: srcY++;
2261: index = srcY * bytesPerLine;
2262: high = true;
2263: srcX = 0;
2264: } else {
2265: if (!high)
2266: index++;
2267: high = !high;
2268: }
2269: }
2270: return;
2271: }
2272: if (depth == 8) {
2273: index = (y * bytesPerLine) + x;
2274: for (int j = 0; j < putWidth; j++) {
2275: data[index] = (byte) (pixels[i] & 0xFF);
2276: i++;
2277: srcX++;
2278: if (srcX >= width) {
2279: srcY++;
2280: index = srcY * bytesPerLine;
2281: srcX = 0;
2282: } else {
2283: index++;
2284: }
2285: }
2286: return;
2287: }
2288: SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
2289: }
2290:
2291: /**
2292: * Sets the pixel values starting at offset <code>x</code> in
2293: * scanline <code>y</code> in the receiver's data to the
2294: * values from the array <code>pixels</code> starting at
2295: * <code>startIndex</code>.
2296: *
2297: * @param x the x position of the pixel to set
2298: * @param y the y position of the pixel to set
2299: * @param putWidth the width of the pixels to set
2300: * @param pixels the pixels to set
2301: * @param startIndex the index at which to begin setting
2302: *
2303: * @exception IndexOutOfBoundsException if putWidth is too large
2304: * @exception IllegalArgumentException <ul>
2305: * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
2306: * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
2307: * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
2308: * </ul>
2309: * @exception SWTException <ul>
2310: * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
2311: * </ul>
2312: */
2313: public void setPixels(int x, int y, int putWidth, int[] pixels,
2314: int startIndex) {
2315: if (pixels == null)
2316: SWT.error(SWT.ERROR_NULL_ARGUMENT);
2317: if (putWidth < 0 || x >= width || y >= height || x < 0
2318: || y < 0)
2319: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2320: if (putWidth == 0)
2321: return;
2322: int index;
2323: int theByte;
2324: int mask;
2325: int n = putWidth;
2326: int i = startIndex;
2327: int pixel;
2328: int srcX = x, srcY = y;
2329: if (depth == 1) {
2330: index = (y * bytesPerLine) + (x >> 3);
2331: while (n > 0) {
2332: mask = 1 << (7 - (srcX & 0x7));
2333: if ((pixels[i] & 0x1) == 1) {
2334: data[index] = (byte) ((data[index] & 0xFF) | mask);
2335: } else {
2336: data[index] = (byte) ((data[index] & 0xFF) & (mask ^ -1));
2337: }
2338: i++;
2339: n--;
2340: srcX++;
2341: if (srcX >= width) {
2342: srcY++;
2343: index = srcY * bytesPerLine;
2344: srcX = 0;
2345: } else {
2346: if (mask == 1) {
2347: index++;
2348: }
2349: }
2350: }
2351: return;
2352: }
2353: if (depth == 2) {
2354: byte[] masks = { (byte) 0xFC, (byte) 0xF3, (byte) 0xCF,
2355: (byte) 0x3F };
2356: index = (y * bytesPerLine) + (x >> 2);
2357: int offset = 3 - (x % 4);
2358: while (n > 0) {
2359: theByte = pixels[i] & 0x3;
2360: data[index] = (byte) ((data[index] & masks[offset]) | (theByte << (offset * 2)));
2361: i++;
2362: n--;
2363: srcX++;
2364: if (srcX >= width) {
2365: srcY++;
2366: index = srcY * bytesPerLine;
2367: offset = 3;
2368: srcX = 0;
2369: } else {
2370: if (offset == 0) {
2371: index++;
2372: offset = 3;
2373: } else {
2374: offset--;
2375: }
2376: }
2377: }
2378: return;
2379: }
2380: if (depth == 4) {
2381: index = (y * bytesPerLine) + (x >> 1);
2382: boolean high = (x & 0x1) == 0;
2383: while (n > 0) {
2384: theByte = pixels[i] & 0x0F;
2385: if (high) {
2386: data[index] = (byte) ((data[index] & 0x0F) | (theByte << 4));
2387: } else {
2388: data[index] = (byte) ((data[index] & 0xF0) | theByte);
2389: }
2390: i++;
2391: n--;
2392: srcX++;
2393: if (srcX >= width) {
2394: srcY++;
2395: index = srcY * bytesPerLine;
2396: high = true;
2397: srcX = 0;
2398: } else {
2399: if (!high)
2400: index++;
2401: high = !high;
2402: }
2403: }
2404: return;
2405: }
2406: if (depth == 8) {
2407: index = (y * bytesPerLine) + x;
2408: for (int j = 0; j < putWidth; j++) {
2409: data[index] = (byte) (pixels[i] & 0xFF);
2410: i++;
2411: srcX++;
2412: if (srcX >= width) {
2413: srcY++;
2414: index = srcY * bytesPerLine;
2415: srcX = 0;
2416: } else {
2417: index++;
2418: }
2419: }
2420: return;
2421:
2422: }
2423: if (depth == 16) {
2424: index = (y * bytesPerLine) + (x * 2);
2425: for (int j = 0; j < putWidth; j++) {
2426: pixel = pixels[i];
2427: data[index] = (byte) (pixel & 0xFF);
2428: data[index + 1] = (byte) ((pixel >> 8) & 0xFF);
2429: i++;
2430: srcX++;
2431: if (srcX >= width) {
2432: srcY++;
2433: index = srcY * bytesPerLine;
2434: srcX = 0;
2435: } else {
2436: index += 2;
2437: }
2438: }
2439: return;
2440: }
2441: if (depth == 24) {
2442: index = (y * bytesPerLine) + (x * 3);
2443: for (int j = 0; j < putWidth; j++) {
2444: pixel = pixels[i];
2445: data[index] = (byte) ((pixel >> 16) & 0xFF);
2446: data[index + 1] = (byte) ((pixel >> 8) & 0xFF);
2447: data[index + 2] = (byte) (pixel & 0xFF);
2448: i++;
2449: srcX++;
2450: if (srcX >= width) {
2451: srcY++;
2452: index = srcY * bytesPerLine;
2453: srcX = 0;
2454: } else {
2455: index += 3;
2456: }
2457: }
2458: return;
2459: }
2460: if (depth == 32) {
2461: index = (y * bytesPerLine) + (x * 4);
2462: for (int j = 0; j < putWidth; j++) {
2463: pixel = pixels[i];
2464: data[index] = (byte) ((pixel >> 24) & 0xFF);
2465: data[index + 1] = (byte) ((pixel >> 16) & 0xFF);
2466: data[index + 2] = (byte) ((pixel >> 8) & 0xFF);
2467: data[index + 3] = (byte) (pixel & 0xFF);
2468: i++;
2469: srcX++;
2470: if (srcX >= width) {
2471: srcY++;
2472: index = srcY * bytesPerLine;
2473: srcX = 0;
2474: } else {
2475: index += 4;
2476: }
2477: }
2478: return;
2479: }
2480: SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
2481: }
2482:
2483: /**
2484: * Returns a palette with 2 colors: black & white.
2485: */
2486: static PaletteData bwPalette() {
2487: return new PaletteData(new RGB[] { new RGB(0, 0, 0),
2488: new RGB(255, 255, 255) });
2489: }
2490:
2491: /**
2492: * Gets the offset of the most significant bit for
2493: * the given mask.
2494: */
2495: static int getMSBOffset(int mask) {
2496: for (int i = 31; i >= 0; i--) {
2497: if (((mask >> i) & 0x1) != 0)
2498: return i + 1;
2499: }
2500: return 0;
2501: }
2502:
2503: /**
2504: * Finds the closest match.
2505: */
2506: static int closestMatch(int depth, byte red, byte green,
2507: byte blue, int redMask, int greenMask, int blueMask,
2508: byte[] reds, byte[] greens, byte[] blues) {
2509: if (depth > 8) {
2510: int rshift = 32 - getMSBOffset(redMask);
2511: int gshift = 32 - getMSBOffset(greenMask);
2512: int bshift = 32 - getMSBOffset(blueMask);
2513: return (((red << 24) >>> rshift) & redMask)
2514: | (((green << 24) >>> gshift) & greenMask)
2515: | (((blue << 24) >>> bshift) & blueMask);
2516: }
2517: int r, g, b;
2518: int minDistance = 0x7fffffff;
2519: int nearestPixel = 0;
2520: int n = reds.length;
2521: for (int j = 0; j < n; j++) {
2522: r = (reds[j] & 0xFF) - (red & 0xFF);
2523: g = (greens[j] & 0xFF) - (green & 0xFF);
2524: b = (blues[j] & 0xFF) - (blue & 0xFF);
2525: int distance = r * r + g * g + b * b;
2526: if (distance < minDistance) {
2527: nearestPixel = j;
2528: if (distance == 0)
2529: break;
2530: minDistance = distance;
2531: }
2532: }
2533: return nearestPixel;
2534: }
2535:
2536: static final ImageData convertMask(ImageData mask) {
2537: if (mask.depth == 1)
2538: return mask;
2539: PaletteData palette = new PaletteData(new RGB[] {
2540: new RGB(0, 0, 0), new RGB(255, 255, 255) });
2541: ImageData newMask = new ImageData(mask.width, mask.height,
2542: 1, palette);
2543: /* Find index of black in mask palette */
2544: int blackIndex = 0;
2545: RGB[] rgbs = mask.getRGBs();
2546: if (rgbs != null) {
2547: while (blackIndex < rgbs.length) {
2548: if (rgbs[blackIndex].equals(palette.colors[0]))
2549: break;
2550: blackIndex++;
2551: }
2552: }
2553: int[] pixels = new int[mask.width];
2554: for (int y = 0; y < mask.height; y++) {
2555: mask.getPixels(0, y, mask.width, pixels, 0);
2556: for (int i = 0; i < pixels.length; i++) {
2557: if (pixels[i] == blackIndex) {
2558: pixels[i] = 0;
2559: } else {
2560: pixels[i] = 1;
2561: }
2562: }
2563: newMask.setPixels(0, y, mask.width, pixels, 0);
2564: }
2565: return newMask;
2566: }
2567:
2568: static final byte[] convertPad(byte[] data, int width,
2569: int height, int depth, int pad, int newPad) {
2570: if (pad == newPad)
2571: return data;
2572: int stride = (width * depth + 7) / 8;
2573: int bpl = (stride + (pad - 1)) / pad * pad;
2574: int newBpl = (stride + (newPad - 1)) / newPad * newPad;
2575: byte[] newData = new byte[height * newBpl];
2576: int srcIndex = 0, destIndex = 0;
2577: for (int y = 0; y < height; y++) {
2578: System.arraycopy(data, srcIndex, newData, destIndex,
2579: stride);
2580: srcIndex += bpl;
2581: destIndex += newBpl;
2582: }
2583: return newData;
2584: }
2585:
2586: /**
2587: * Blit operation bits to be OR'ed together to specify the desired operation.
2588: */
2589: static final int BLIT_SRC = 1, // copy source directly, else applies logic operations
2590: BLIT_ALPHA = 2, // enable alpha blending
2591: BLIT_DITHER = 4; // enable dithering in low color modes
2592:
2593: /**
2594: * Alpha mode, values 0 - 255 specify global alpha level
2595: */
2596: static final int ALPHA_OPAQUE = 255, // Fully opaque (ignores any alpha data)
2597: ALPHA_TRANSPARENT = 0, // Fully transparent (ignores any alpha data)
2598: ALPHA_CHANNEL_SEPARATE = -1, // Use alpha channel from separate alphaData
2599: ALPHA_CHANNEL_SOURCE = -2, // Use alpha channel embedded in sourceData
2600: ALPHA_MASK_UNPACKED = -3, // Use transparency mask formed by bytes in alphaData (non-zero is opaque)
2601: ALPHA_MASK_PACKED = -4, // Use transparency mask formed by packed bits in alphaData
2602: ALPHA_MASK_INDEX = -5, // Consider source palette indices transparent if in alphaData array
2603: ALPHA_MASK_RGB = -6; // Consider source RGBs transparent if in RGB888 format alphaData array
2604:
2605: /**
2606: * Byte and bit order constants.
2607: */
2608: static final int LSB_FIRST = 0;
2609: static final int MSB_FIRST = 1;
2610:
2611: /**
2612: * Data types (internal)
2613: */
2614: /*
2615: private static final int
2616: // direct / true color formats with arbitrary masks & shifts
2617: TYPE_GENERIC_8 = 0,
2618: TYPE_GENERIC_16_MSB = 1,
2619: TYPE_GENERIC_16_LSB = 2,
2620: TYPE_GENERIC_24 = 3,
2621: TYPE_GENERIC_32_MSB = 4,
2622: TYPE_GENERIC_32_LSB = 5,
2623: // palette indexed color formats
2624: TYPE_INDEX_8 = 6,
2625: TYPE_INDEX_4 = 7,
2626: TYPE_INDEX_2 = 8,
2627: TYPE_INDEX_1_MSB = 9,
2628: TYPE_INDEX_1_LSB = 10;
2629: */
2630: /**
2631: * Computes the required channel shift from a mask.
2632: */
2633: static int getChannelShift(int mask) {
2634: if (mask == 0)
2635: return 0;
2636: int i;
2637: for (i = 0; ((mask & 1) == 0) && (i < 32); ++i) {
2638: mask >>>= 1;
2639: }
2640: return i;
2641: }
2642:
2643: /**
2644: * Computes the required channel width (depth) from a mask.
2645: */
2646: static int getChannelWidth(int mask, int shift) {
2647: if (mask == 0)
2648: return 0;
2649: int i;
2650: mask >>>= shift;
2651: for (i = shift; ((mask & 1) != 0) && (i < 32); ++i) {
2652: mask >>>= 1;
2653: }
2654: return i - shift;
2655: }
2656:
2657: /**
2658: * Extracts a field from packed RGB data given a mask for that field.
2659: */
2660: static byte getChannelField(int data, int mask) {
2661: final int shift = getChannelShift(mask);
2662: return ANY_TO_EIGHT[getChannelWidth(mask, shift)][(data & mask) >>> shift];
2663: }
2664:
2665: /*
2666: * Fill in dithered gradated values for a color channel
2667: */
2668: static final void buildDitheredGradientChannel(int from,
2669: int to, int steps, int bandWidth, int bandHeight,
2670: boolean vertical, byte[] bitmapData, int dp,
2671: int bytesPerLine, int bits) {
2672: final int mask = 0xff00 >>> bits;
2673: int val = from << 16;
2674: final int inc = ((to << 16) - val) / steps + 1;
2675: if (vertical) {
2676: for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
2677: for (int dx = 0, dptr = dp; dx < bandWidth; ++dx, dptr += 4) {
2678: final int thresh = DITHER_MATRIX[dy & 7][dx] >>> bits;
2679: int temp = val + thresh;
2680: if (temp > 0xffffff)
2681: bitmapData[dptr] = -1;
2682: else
2683: bitmapData[dptr] = (byte) ((temp >>> 16) & mask);
2684: }
2685: val += inc;
2686: }
2687: } else {
2688: for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
2689: for (int dy = 0, dptr = dp; dy < bandHeight; ++dy, dptr += bytesPerLine) {
2690: final int thresh = DITHER_MATRIX[dy][dx & 7] >>> bits;
2691: int temp = val + thresh;
2692: if (temp > 0xffffff)
2693: bitmapData[dptr] = -1;
2694: else
2695: bitmapData[dptr] = (byte) ((temp >>> 16) & mask);
2696: }
2697: val += inc;
2698: }
2699: }
2700: }
2701: }
2702:
2703: static class LEDataInputStream extends InputStream {
2704: int position;
2705: InputStream in;
2706:
2707: /**
2708: * The byte array containing the bytes to read.
2709: */
2710: protected byte[] buf;
2711:
2712: /**
2713: * The current position within the byte array <code>buf</code>. A value
2714: * equal to buf.length indicates no bytes available. A value of
2715: * 0 indicates the buffer is full.
2716: */
2717: protected int pos;
2718:
2719: public LEDataInputStream(InputStream input) {
2720: this (input, 512);
2721: }
2722:
2723: public LEDataInputStream(InputStream input, int bufferSize) {
2724: this .in = input;
2725: if (bufferSize > 0) {
2726: buf = new byte[bufferSize];
2727: pos = bufferSize;
2728: } else
2729: throw new IllegalArgumentException();
2730: }
2731:
2732: public void close() throws IOException {
2733: buf = null;
2734: if (in != null) {
2735: in.close();
2736: in = null;
2737: }
2738: }
2739:
2740: /**
2741: * Answer how many bytes were read.
2742: */
2743: public int getPosition() {
2744: return position;
2745: }
2746:
2747: /**
2748: * Answers how many bytes are available for reading without blocking
2749: */
2750: public int available() throws IOException {
2751: if (buf == null)
2752: throw new IOException();
2753: return (buf.length - pos) + in.available();
2754: }
2755:
2756: /**
2757: * Answer the next byte of the input stream.
2758: */
2759: public int read() throws IOException {
2760: if (buf == null)
2761: throw new IOException();
2762: position++;
2763: if (pos < buf.length)
2764: return (buf[pos++] & 0xFF);
2765: return in.read();
2766: }
2767:
2768: /**
2769: * Don't imitate the JDK behaviour of reading a random number
2770: * of bytes when you can actually read them all.
2771: */
2772: public int read(byte b[], int off, int len) throws IOException {
2773: int result;
2774: int left = len;
2775: result = readData(b, off, len);
2776: while (true) {
2777: if (result == -1)
2778: return -1;
2779: position += result;
2780: if (result == left)
2781: return len;
2782: left -= result;
2783: off += result;
2784: result = readData(b, off, left);
2785: }
2786: }
2787:
2788: /**
2789: * Reads at most <code>length</code> bytes from this LEDataInputStream and
2790: * stores them in byte array <code>buffer</code> starting at <code>offset</code>.
2791: * <p>
2792: * Answer the number of bytes actually read or -1 if no bytes were read and
2793: * end of stream was encountered. This implementation reads bytes from
2794: * the pushback buffer first, then the target stream if more bytes are required
2795: * to satisfy <code>count</code>.
2796: * </p>
2797: * @param buffer the byte array in which to store the read bytes.
2798: * @param offset the offset in <code>buffer</code> to store the read bytes.
2799: * @param length the maximum number of bytes to store in <code>buffer</code>.
2800: *
2801: * @return int the number of bytes actually read or -1 if end of stream.
2802: *
2803: * @exception java.io.IOException if an IOException occurs.
2804: */
2805: private int readData(byte[] buffer, int offset, int length)
2806: throws IOException {
2807: if (buf == null)
2808: throw new IOException();
2809: if (offset < 0 || offset > buffer.length || length < 0
2810: || (length > buffer.length - offset)) {
2811: throw new ArrayIndexOutOfBoundsException();
2812: }
2813:
2814: int cacheCopied = 0;
2815: int newOffset = offset;
2816:
2817: // Are there pushback bytes available?
2818: int available = buf.length - pos;
2819: if (available > 0) {
2820: cacheCopied = (available >= length) ? length
2821: : available;
2822: System.arraycopy(buf, pos, buffer, newOffset,
2823: cacheCopied);
2824: newOffset += cacheCopied;
2825: pos += cacheCopied;
2826: }
2827:
2828: // Have we copied enough?
2829: if (cacheCopied == length)
2830: return length;
2831:
2832: int inCopied = in.read(buffer, newOffset, length
2833: - cacheCopied);
2834:
2835: if (inCopied > 0)
2836: return inCopied + cacheCopied;
2837: if (cacheCopied == 0)
2838: return inCopied;
2839: return cacheCopied;
2840: }
2841:
2842: /**
2843: * Answer an integer comprised of the next
2844: * four bytes of the input stream.
2845: */
2846: public int readInt() throws IOException {
2847: byte[] buf = new byte[4];
2848: read(buf);
2849: return ((((((buf[3] & 0xFF) << 8) | (buf[2] & 0xFF)) << 8) | (buf[1] & 0xFF)) << 8)
2850: | (buf[0] & 0xFF);
2851: }
2852:
2853: /**
2854: * Answer a short comprised of the next
2855: * two bytes of the input stream.
2856: */
2857: public short readShort() throws IOException {
2858: byte[] buf = new byte[2];
2859: read(buf);
2860: return (short) (((buf[1] & 0xFF) << 8) | (buf[0] & 0xFF));
2861: }
2862:
2863: /**
2864: * Push back the entire content of the given buffer <code>b</code>.
2865: * <p>
2866: * The bytes are pushed so that they would be read back b[0], b[1], etc.
2867: * If the push back buffer cannot handle the bytes copied from <code>b</code>,
2868: * an IOException will be thrown and no byte will be pushed back.
2869: * </p>
2870: *
2871: * @param b the byte array containing bytes to push back into the stream
2872: *
2873: * @exception java.io.IOException if the pushback buffer is too small
2874: */
2875: public void unread(byte[] b) throws IOException {
2876: int length = b.length;
2877: if (length > pos)
2878: throw new IOException();
2879: position -= length;
2880: pos -= length;
2881: System.arraycopy(b, 0, buf, pos, length);
2882: }
2883: }
2884:
2885: public static abstract class FileFormat {
2886: LEDataInputStream inputStream;
2887: ImageLoader loader;
2888: int compression;
2889:
2890: byte[] bitInvertData(byte[] data, int startIndex, int endIndex) {
2891: // Destructively bit invert data in the given byte array.
2892: for (int i = startIndex; i < endIndex; i++) {
2893: data[i] = (byte) (255 - data[i - startIndex]);
2894: }
2895: return data;
2896: }
2897:
2898: /**
2899: * Return whether or not the specified input stream
2900: * represents a supported file format.
2901: */
2902: abstract boolean isFileFormat(LEDataInputStream stream);
2903:
2904: abstract ImageData[] loadFromByteStream();
2905:
2906: public ImageData[] loadFromStream(LEDataInputStream stream) {
2907: try {
2908: inputStream = stream;
2909: return loadFromByteStream();
2910: } catch (Exception e) {
2911: SWT.error(SWT.ERROR_IO, e);
2912: return null;
2913: }
2914: }
2915:
2916: public static ImageData[] load(InputStream is,
2917: ImageLoader loader) {
2918: LEDataInputStream stream = new LEDataInputStream(is);
2919: boolean isSupported = false;
2920: FileFormat fileFormat = new WinICOFileFormat();
2921: if (fileFormat.isFileFormat(stream))
2922: isSupported = true;
2923: else {
2924: fileFormat = new WinBMPFileFormat();
2925: if (fileFormat.isFileFormat(stream))
2926: isSupported = true;
2927: }
2928: if (!isSupported)
2929: SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
2930: fileFormat.loader = loader;
2931: return fileFormat.loadFromStream(stream);
2932: }
2933: }
2934:
2935: static class WinBMPFileFormat extends FileFormat {
2936: static final int BMPFileHeaderSize = 14;
2937: static final int BMPHeaderFixedSize = 40;
2938: int importantColors;
2939:
2940: void decompressData(byte[] src, byte[] dest, int stride, int cmp) {
2941: if (cmp == 1) { // BMP_RLE8_COMPRESSION
2942: if (decompressRLE8Data(src, src.length, stride, dest,
2943: dest.length) <= 0)
2944: SWT.error(SWT.ERROR_INVALID_IMAGE);
2945: return;
2946: }
2947: if (cmp == 2) { // BMP_RLE4_COMPRESSION
2948: if (decompressRLE4Data(src, src.length, stride, dest,
2949: dest.length) <= 0)
2950: SWT.error(SWT.ERROR_INVALID_IMAGE);
2951: return;
2952: }
2953: SWT.error(SWT.ERROR_INVALID_IMAGE);
2954: }
2955:
2956: int decompressRLE4Data(byte[] src, int numBytes, int stride,
2957: byte[] dest, int destSize) {
2958: int sp = 0;
2959: int se = numBytes;
2960: int dp = 0;
2961: int de = destSize;
2962: int x = 0, y = 0;
2963: while (sp < se) {
2964: int len = src[sp] & 0xFF;
2965: sp++;
2966: if (len == 0) {
2967: len = src[sp] & 0xFF;
2968: sp++;
2969: switch (len) {
2970: case 0: /* end of line */
2971: y++;
2972: x = 0;
2973: dp = y * stride;
2974: if (dp >= de)
2975: return -1;
2976: break;
2977: case 1: /* end of bitmap */
2978: return 1;
2979: case 2: /* delta */
2980: x += src[sp] & 0xFF;
2981: sp++;
2982: y += src[sp] & 0xFF;
2983: sp++;
2984: dp = y * stride + x / 2;
2985: if (dp >= de)
2986: return -1;
2987: break;
2988: default: /* absolute mode run */
2989: if ((len & 1) != 0) /* odd run lengths not currently supported */
2990: return -1;
2991: x += len;
2992: len = len / 2;
2993: if (len > (se - sp))
2994: return -1;
2995: if (len > (de - dp))
2996: return -1;
2997: for (int i = 0; i < len; i++) {
2998: dest[dp] = src[sp];
2999: dp++;
3000: sp++;
3001: }
3002: if ((sp & 1) != 0)
3003: sp++; /* word align sp? */
3004: break;
3005: }
3006: } else {
3007: if ((len & 1) != 0)
3008: return -1;
3009: x += len;
3010: len = len / 2;
3011: byte theByte = src[sp];
3012: sp++;
3013: if (len > (de - dp))
3014: return -1;
3015: for (int i = 0; i < len; i++) {
3016: dest[dp] = theByte;
3017: dp++;
3018: }
3019: }
3020: }
3021: return 1;
3022: }
3023:
3024: int decompressRLE8Data(byte[] src, int numBytes, int stride,
3025: byte[] dest, int destSize) {
3026: int sp = 0;
3027: int se = numBytes;
3028: int dp = 0;
3029: int de = destSize;
3030: int x = 0, y = 0;
3031: while (sp < se) {
3032: int len = src[sp] & 0xFF;
3033: sp++;
3034: if (len == 0) {
3035: len = src[sp] & 0xFF;
3036: sp++;
3037: switch (len) {
3038: case 0: /* end of line */
3039: y++;
3040: x = 0;
3041: dp = y * stride;
3042: if (dp >= de)
3043: return -1;
3044: break;
3045: case 1: /* end of bitmap */
3046: return 1;
3047: case 2: /* delta */
3048: x += src[sp] & 0xFF;
3049: sp++;
3050: y += src[sp] & 0xFF;
3051: sp++;
3052: dp = y * stride + x;
3053: if (dp >= de)
3054: return -1;
3055: break;
3056: default: /* absolute mode run */
3057: if (len > (se - sp))
3058: return -1;
3059: if (len > (de - dp))
3060: return -1;
3061: for (int i = 0; i < len; i++) {
3062: dest[dp] = src[sp];
3063: dp++;
3064: sp++;
3065: }
3066: if ((sp & 1) != 0)
3067: sp++; /* word align sp? */
3068: x += len;
3069: break;
3070: }
3071: } else {
3072: byte theByte = src[sp];
3073: sp++;
3074: if (len > (de - dp))
3075: return -1;
3076: for (int i = 0; i < len; i++) {
3077: dest[dp] = theByte;
3078: dp++;
3079: }
3080: x += len;
3081: }
3082: }
3083: return 1;
3084: }
3085:
3086: boolean isFileFormat(LEDataInputStream stream) {
3087: try {
3088: byte[] header = new byte[18];
3089: stream.read(header);
3090: stream.unread(header);
3091: int infoHeaderSize = (header[14] & 0xFF)
3092: | ((header[15] & 0xFF) << 8)
3093: | ((header[16] & 0xFF) << 16)
3094: | ((header[17] & 0xFF) << 24);
3095: return header[0] == 0x42 && header[1] == 0x4D
3096: && infoHeaderSize >= BMPHeaderFixedSize;
3097: } catch (Exception e) {
3098: return false;
3099: }
3100: }
3101:
3102: byte[] loadData(byte[] infoHeader) {
3103: int width = (infoHeader[4] & 0xFF)
3104: | ((infoHeader[5] & 0xFF) << 8)
3105: | ((infoHeader[6] & 0xFF) << 16)
3106: | ((infoHeader[7] & 0xFF) << 24);
3107: int height = (infoHeader[8] & 0xFF)
3108: | ((infoHeader[9] & 0xFF) << 8)
3109: | ((infoHeader[10] & 0xFF) << 16)
3110: | ((infoHeader[11] & 0xFF) << 24);
3111: int bitCount = (infoHeader[14] & 0xFF)
3112: | ((infoHeader[15] & 0xFF) << 8);
3113: int stride = (width * bitCount + 7) / 8;
3114: stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
3115: byte[] data = loadData(infoHeader, stride);
3116: flipScanLines(data, stride, height);
3117: return data;
3118: }
3119:
3120: byte[] loadData(byte[] infoHeader, int stride) {
3121: int height = (infoHeader[8] & 0xFF)
3122: | ((infoHeader[9] & 0xFF) << 8)
3123: | ((infoHeader[10] & 0xFF) << 16)
3124: | ((infoHeader[11] & 0xFF) << 24);
3125: int dataSize = height * stride;
3126: byte[] data = new byte[dataSize];
3127: int cmp = (infoHeader[16] & 0xFF)
3128: | ((infoHeader[17] & 0xFF) << 8)
3129: | ((infoHeader[18] & 0xFF) << 16)
3130: | ((infoHeader[19] & 0xFF) << 24);
3131: if (cmp == 0) { // BMP_NO_COMPRESSION
3132: try {
3133: if (inputStream.read(data) != dataSize)
3134: SWT.error(SWT.ERROR_INVALID_IMAGE);
3135: } catch (IOException e) {
3136: SWT.error(SWT.ERROR_IO, e);
3137: }
3138: } else {
3139: int compressedSize = (infoHeader[20] & 0xFF)
3140: | ((infoHeader[21] & 0xFF) << 8)
3141: | ((infoHeader[22] & 0xFF) << 16)
3142: | ((infoHeader[23] & 0xFF) << 24);
3143: byte[] compressed = new byte[compressedSize];
3144: try {
3145: if (inputStream.read(compressed) != compressedSize)
3146: SWT.error(SWT.ERROR_INVALID_IMAGE);
3147: } catch (IOException e) {
3148: SWT.error(SWT.ERROR_IO, e);
3149: }
3150: decompressData(compressed, data, stride, cmp);
3151: }
3152: return data;
3153: }
3154:
3155: int[] loadFileHeader() {
3156: int[] header = new int[5];
3157: try {
3158: header[0] = inputStream.readShort();
3159: header[1] = inputStream.readInt();
3160: header[2] = inputStream.readShort();
3161: header[3] = inputStream.readShort();
3162: header[4] = inputStream.readInt();
3163: } catch (IOException e) {
3164: SWT.error(SWT.ERROR_IO, e);
3165: }
3166: if (header[0] != 0x4D42)
3167: SWT.error(SWT.ERROR_INVALID_IMAGE);
3168: return header;
3169: }
3170:
3171: ImageData[] loadFromByteStream() {
3172: int[] fileHeader = loadFileHeader();
3173: byte[] infoHeader = new byte[BMPHeaderFixedSize];
3174: try {
3175: inputStream.read(infoHeader);
3176: } catch (Exception e) {
3177: SWT.error(SWT.ERROR_IO, e);
3178: }
3179: int width = (infoHeader[4] & 0xFF)
3180: | ((infoHeader[5] & 0xFF) << 8)
3181: | ((infoHeader[6] & 0xFF) << 16)
3182: | ((infoHeader[7] & 0xFF) << 24);
3183: int height = (infoHeader[8] & 0xFF)
3184: | ((infoHeader[9] & 0xFF) << 8)
3185: | ((infoHeader[10] & 0xFF) << 16)
3186: | ((infoHeader[11] & 0xFF) << 24);
3187: int bitCount = (infoHeader[14] & 0xFF)
3188: | ((infoHeader[15] & 0xFF) << 8);
3189: PaletteData palette = loadPalette(infoHeader);
3190: if (inputStream.getPosition() < fileHeader[4]) {
3191: // Seek to the specified offset
3192: try {
3193: inputStream.skip(fileHeader[4]
3194: - inputStream.getPosition());
3195: } catch (IOException e) {
3196: SWT.error(SWT.ERROR_IO, e);
3197: }
3198: }
3199: byte[] data = loadData(infoHeader);
3200: this .compression = (infoHeader[16] & 0xFF)
3201: | ((infoHeader[17] & 0xFF) << 8)
3202: | ((infoHeader[18] & 0xFF) << 16)
3203: | ((infoHeader[19] & 0xFF) << 24);
3204: this .importantColors = (infoHeader[36] & 0xFF)
3205: | ((infoHeader[37] & 0xFF) << 8)
3206: | ((infoHeader[38] & 0xFF) << 16)
3207: | ((infoHeader[39] & 0xFF) << 24);
3208: // int xPelsPerMeter = (infoHeader[24] & 0xFF) | ((infoHeader[25] & 0xFF) << 8) | ((infoHeader[26] & 0xFF) << 16) | ((infoHeader[27] & 0xFF) << 24);
3209: // int yPelsPerMeter = (infoHeader[28] & 0xFF) | ((infoHeader[29] & 0xFF) << 8) | ((infoHeader[30] & 0xFF) << 16) | ((infoHeader[31] & 0xFF) << 24);
3210: int type = (this .compression == 1 /*BMP_RLE8_COMPRESSION*/)
3211: || (this .compression == 2 /*BMP_RLE4_COMPRESSION*/) ? SWT.IMAGE_BMP_RLE
3212: : SWT.IMAGE_BMP;
3213: return new ImageData[] { ImageData.internal_new(width,
3214: height, bitCount, palette, 4, data, 0, null, null,
3215: -1, -1, type, 0, 0, 0, 0) };
3216: }
3217:
3218: PaletteData loadPalette(byte[] infoHeader) {
3219: int depth = (infoHeader[14] & 0xFF)
3220: | ((infoHeader[15] & 0xFF) << 8);
3221: if (depth <= 8) {
3222: int numColors = (infoHeader[32] & 0xFF)
3223: | ((infoHeader[33] & 0xFF) << 8)
3224: | ((infoHeader[34] & 0xFF) << 16)
3225: | ((infoHeader[35] & 0xFF) << 24);
3226: if (numColors == 0) {
3227: numColors = 1 << depth;
3228: } else {
3229: if (numColors > 256)
3230: numColors = 256;
3231: }
3232: byte[] buf = new byte[numColors * 4];
3233: try {
3234: if (inputStream.read(buf) != buf.length)
3235: SWT.error(SWT.ERROR_INVALID_IMAGE);
3236: } catch (IOException e) {
3237: SWT.error(SWT.ERROR_IO, e);
3238: }
3239: return paletteFromBytes(buf, numColors);
3240: }
3241: if (depth == 16)
3242: return new PaletteData(0x7C00, 0x3E0, 0x1F);
3243: if (depth == 24)
3244: return new PaletteData(0xFF, 0xFF00, 0xFF0000);
3245: return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
3246: }
3247:
3248: PaletteData paletteFromBytes(byte[] bytes, int numColors) {
3249: int bytesOffset = 0;
3250: RGB[] colors = new RGB[numColors];
3251: for (int i = 0; i < numColors; i++) {
3252: colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
3253: bytes[bytesOffset + 1] & 0xFF,
3254: bytes[bytesOffset] & 0xFF);
3255: bytesOffset += 4;
3256: }
3257: return new PaletteData(colors);
3258: }
3259:
3260: /**
3261: * Answer a byte array containing the BMP representation of
3262: * the given device independent palette.
3263: */
3264: static byte[] paletteToBytes(PaletteData pal) {
3265: int n = pal.colors == null ? 0
3266: : (pal.colors.length < 256 ? pal.colors.length
3267: : 256);
3268: byte[] bytes = new byte[n * 4];
3269: int offset = 0;
3270: for (int i = 0; i < n; i++) {
3271: RGB col = pal.colors[i];
3272: bytes[offset] = (byte) col.blue;
3273: bytes[offset + 1] = (byte) col.green;
3274: bytes[offset + 2] = (byte) col.red;
3275: offset += 4;
3276: }
3277: return bytes;
3278: }
3279:
3280: void flipScanLines(byte[] data, int stride, int height) {
3281: int i1 = 0;
3282: int i2 = (height - 1) * stride;
3283: for (int i = 0; i < height / 2; i++) {
3284: for (int index = 0; index < stride; index++) {
3285: byte b = data[index + i1];
3286: data[index + i1] = data[index + i2];
3287: data[index + i2] = b;
3288: }
3289: i1 += stride;
3290: i2 -= stride;
3291: }
3292: }
3293:
3294: }
3295:
3296: static class WinICOFileFormat extends FileFormat {
3297:
3298: static final byte[] convertPad(byte[] data, int width,
3299: int height, int depth, int pad, int newPad) {
3300: if (pad == newPad)
3301: return data;
3302: int stride = (width * depth + 7) / 8;
3303: int bpl = (stride + (pad - 1)) / pad * pad;
3304: int newBpl = (stride + (newPad - 1)) / newPad * newPad;
3305: byte[] newData = new byte[height * newBpl];
3306: int srcIndex = 0, destIndex = 0;
3307: for (int y = 0; y < height; y++) {
3308: System.arraycopy(data, srcIndex, newData, destIndex,
3309: newBpl);
3310: srcIndex += bpl;
3311: destIndex += newBpl;
3312: }
3313: return newData;
3314: }
3315:
3316: /**
3317: * Answer the size in bytes of the file representation of the given
3318: * icon
3319: */
3320: int iconSize(ImageData i) {
3321: int shapeDataStride = (i.width * i.depth + 31) / 32 * 4;
3322: int maskDataStride = (i.width + 31) / 32 * 4;
3323: int dataSize = (shapeDataStride + maskDataStride)
3324: * i.height;
3325: int paletteSize = i.palette.colors != null ? i.palette.colors.length * 4
3326: : 0;
3327: return WinBMPFileFormat.BMPHeaderFixedSize + paletteSize
3328: + dataSize;
3329: }
3330:
3331: boolean isFileFormat(LEDataInputStream stream) {
3332: try {
3333: byte[] header = new byte[4];
3334: stream.read(header);
3335: stream.unread(header);
3336: return header[0] == 0 && header[1] == 0
3337: && header[2] == 1 && header[3] == 0;
3338: } catch (Exception e) {
3339: return false;
3340: }
3341: }
3342:
3343: boolean isValidIcon(ImageData i) {
3344: switch (i.depth) {
3345: case 1:
3346: case 4:
3347: case 8:
3348: if (i.palette.isDirect)
3349: return false;
3350: int size = i.palette.colors.length;
3351: return size == 2 || size == 16 || size == 32
3352: || size == 256;
3353: case 24:
3354: case 32:
3355: return i.palette.isDirect;
3356: }
3357: return false;
3358: }
3359:
3360: int loadFileHeader(LEDataInputStream byteStream) {
3361: int[] fileHeader = new int[3];
3362: try {
3363: fileHeader[0] = byteStream.readShort();
3364: fileHeader[1] = byteStream.readShort();
3365: fileHeader[2] = byteStream.readShort();
3366: } catch (IOException e) {
3367: SWT.error(SWT.ERROR_IO, e);
3368: }
3369: if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
3370: SWT.error(SWT.ERROR_INVALID_IMAGE);
3371: int numIcons = fileHeader[2];
3372: if (numIcons <= 0)
3373: SWT.error(SWT.ERROR_INVALID_IMAGE);
3374: return numIcons;
3375: }
3376:
3377: int loadFileHeader(LEDataInputStream byteStream,
3378: boolean hasHeader) {
3379: int[] fileHeader = new int[3];
3380: try {
3381: if (hasHeader) {
3382: fileHeader[0] = byteStream.readShort();
3383: fileHeader[1] = byteStream.readShort();
3384: } else {
3385: fileHeader[0] = 0;
3386: fileHeader[1] = 1;
3387: }
3388: fileHeader[2] = byteStream.readShort();
3389: } catch (IOException e) {
3390: SWT.error(SWT.ERROR_IO, e);
3391: }
3392: if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
3393: SWT.error(SWT.ERROR_INVALID_IMAGE);
3394: int numIcons = fileHeader[2];
3395: if (numIcons <= 0)
3396: SWT.error(SWT.ERROR_INVALID_IMAGE);
3397: return numIcons;
3398: }
3399:
3400: ImageData[] loadFromByteStream() {
3401: int numIcons = loadFileHeader(inputStream);
3402: int[][] headers = loadIconHeaders(numIcons);
3403: ImageData[] icons = new ImageData[headers.length];
3404: for (int i = 0; i < icons.length; i++) {
3405: icons[i] = loadIcon(headers[i]);
3406: }
3407: return icons;
3408: }
3409:
3410: /**
3411: * Load one icon from the byte stream.
3412: */
3413: ImageData loadIcon(int[] iconHeader) {
3414: byte[] infoHeader = loadInfoHeader(iconHeader);
3415: WinBMPFileFormat bmpFormat = new WinBMPFileFormat();
3416: bmpFormat.inputStream = inputStream;
3417: PaletteData palette = bmpFormat.loadPalette(infoHeader);
3418: byte[] shapeData = bmpFormat.loadData(infoHeader);
3419: int width = (infoHeader[4] & 0xFF)
3420: | ((infoHeader[5] & 0xFF) << 8)
3421: | ((infoHeader[6] & 0xFF) << 16)
3422: | ((infoHeader[7] & 0xFF) << 24);
3423: int height = (infoHeader[8] & 0xFF)
3424: | ((infoHeader[9] & 0xFF) << 8)
3425: | ((infoHeader[10] & 0xFF) << 16)
3426: | ((infoHeader[11] & 0xFF) << 24);
3427: int depth = (infoHeader[14] & 0xFF)
3428: | ((infoHeader[15] & 0xFF) << 8);
3429: infoHeader[14] = 1;
3430: infoHeader[15] = 0;
3431: byte[] maskData = bmpFormat.loadData(infoHeader);
3432: maskData = convertPad(maskData, width, height, 1, 4, 2);
3433: bitInvertData(maskData, 0, maskData.length);
3434: return ImageData.internal_new(width, height, depth,
3435: palette, 4, shapeData, 2, maskData, null, -1, -1,
3436: SWT.IMAGE_ICO, 0, 0, 0, 0);
3437: }
3438:
3439: int[][] loadIconHeaders(int numIcons) {
3440: int[][] headers = new int[numIcons][7];
3441: try {
3442: for (int i = 0; i < numIcons; i++) {
3443: headers[i][0] = inputStream.read();
3444: headers[i][1] = inputStream.read();
3445: headers[i][2] = inputStream.readShort();
3446: headers[i][3] = inputStream.readShort();
3447: headers[i][4] = inputStream.readShort();
3448: headers[i][5] = inputStream.readInt();
3449: headers[i][6] = inputStream.readInt();
3450: }
3451: } catch (IOException e) {
3452: SWT.error(SWT.ERROR_IO, e);
3453: }
3454: return headers;
3455: }
3456:
3457: byte[] loadInfoHeader(int[] iconHeader) {
3458: int width = iconHeader[0];
3459: int height = iconHeader[1];
3460: int numColors = iconHeader[2]; // the number of colors is in the low byte, but the high byte must be 0
3461: if (numColors == 0)
3462: numColors = 256; // this is specified: '00' represents '256' (0x100) colors
3463: if ((numColors != 2) && (numColors != 8)
3464: && (numColors != 16) && (numColors != 32)
3465: && (numColors != 256))
3466: SWT.error(SWT.ERROR_INVALID_IMAGE);
3467: if (inputStream.getPosition() < iconHeader[6]) {
3468: // Seek to the specified offset
3469: try {
3470: inputStream.skip(iconHeader[6]
3471: - inputStream.getPosition());
3472: } catch (IOException e) {
3473: SWT.error(SWT.ERROR_IO, e);
3474: return null;
3475: }
3476: }
3477: byte[] infoHeader = new byte[WinBMPFileFormat.BMPHeaderFixedSize];
3478: try {
3479: inputStream.read(infoHeader);
3480: } catch (IOException e) {
3481: SWT.error(SWT.ERROR_IO, e);
3482: }
3483: if (((infoHeader[12] & 0xFF) | ((infoHeader[13] & 0xFF) << 8)) != 1)
3484: SWT.error(SWT.ERROR_INVALID_IMAGE);
3485: int infoWidth = (infoHeader[4] & 0xFF)
3486: | ((infoHeader[5] & 0xFF) << 8)
3487: | ((infoHeader[6] & 0xFF) << 16)
3488: | ((infoHeader[7] & 0xFF) << 24);
3489: int infoHeight = (infoHeader[8] & 0xFF)
3490: | ((infoHeader[9] & 0xFF) << 8)
3491: | ((infoHeader[10] & 0xFF) << 16)
3492: | ((infoHeader[11] & 0xFF) << 24);
3493: int bitCount = (infoHeader[14] & 0xFF)
3494: | ((infoHeader[15] & 0xFF) << 8);
3495: if (height == infoHeight && bitCount == 1)
3496: height /= 2;
3497: if (!((width == infoWidth) && (height * 2 == infoHeight) && (bitCount == 1
3498: || bitCount == 4 || bitCount == 8 || bitCount == 24 || bitCount == 32)))
3499: SWT.error(SWT.ERROR_INVALID_IMAGE);
3500: infoHeader[8] = (byte) (height & 0xFF);
3501: infoHeader[9] = (byte) ((height >> 8) & 0xFF);
3502: infoHeader[10] = (byte) ((height >> 16) & 0xFF);
3503: infoHeader[11] = (byte) ((height >> 24) & 0xFF);
3504: return infoHeader;
3505: }
3506: }
3507:
3508: static class SWT {
3509: public static final int IMAGE_ICO = 3;
3510: public static final int ERROR_IO = 39;
3511: public static final int ERROR_INVALID_IMAGE = 40;
3512: public static final int ERROR_NULL_ARGUMENT = 4;
3513: public static final int ERROR_INVALID_ARGUMENT = 5;
3514: public static final int ERROR_CANNOT_BE_ZERO = 7;
3515: public static final int IMAGE_UNDEFINED = -1;
3516: public static final int ERROR_UNSUPPORTED_DEPTH = 38;
3517: public static final int TRANSPARENCY_MASK = 1 << 1;
3518: public static final int ERROR_UNSUPPORTED_FORMAT = 42;
3519: public static final int TRANSPARENCY_ALPHA = 1 << 0;
3520: public static final int TRANSPARENCY_NONE = 0x0;
3521: public static final int TRANSPARENCY_PIXEL = 1 << 2;
3522: public static final int IMAGE_BMP = 0;
3523: public static final int IMAGE_BMP_RLE = 1;
3524:
3525: public static void error(int code) {
3526: throw new RuntimeException("Error " + code); //$NON-NLS-1$
3527: }
3528:
3529: public static void error(int code, Throwable t) {
3530: throw new RuntimeException(t);
3531: }
3532: }
3533: }
|