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