imported and butchered SWTs JPEG decoder
authoradam <adam@megacz.com>
Mon, 10 Jan 2005 10:38:37 +0000 (10:38 +0000)
committeradam <adam@megacz.com>
Mon, 10 Jan 2005 10:38:37 +0000 (10:38 +0000)
darcs-hash:20050110103837-5007d-7bfb77acf496c26038ee18656c04114c0d5a29e4.gz

src/org/ibex/graphics/JPEG.java [new file with mode: 0644]

diff --git a/src/org/ibex/graphics/JPEG.java b/src/org/ibex/graphics/JPEG.java
new file mode 100644 (file)
index 0000000..1857e82
--- /dev/null
@@ -0,0 +1,7694 @@
+package org.ibex.graphics;
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.*;
+import java.io.*;
+import java.util.*;
+
+// Big thanks to the SWT project for this!
+
+// The code below was badly butchered by Adam Megacz in order to make
+// it work outside SWT.
+
+
+public class JPEG {
+
+    private static class SWT {
+        public static final int ERROR_INVALID_ARGUMENT = 0;
+        public static final int ERROR_NULL_ARGUMENT = 1;
+        public static final int ERROR_CANNOT_BE_ZERO = 2;
+        public static final int ERROR_INVALID_IMAGE = 3;
+        public static final int IMAGE_UNDEFINED = 4;
+        public static final int TRANSPARENCY_PIXEL = 5;
+        public static final int TRANSPARENCY_ALPHA = 6;
+        public static final int ERROR_UNSUPPORTED_DEPTH = 7;
+        public static final int ERROR_IO = 8;
+        public static final int TRANSPARENCY_MASK = 9;
+        public static final int TRANSPARENCY_NONE = 10;
+        public static final int ERROR_UNSUPPORTED_FORMAT = 11;
+        public static final int IMAGE_BMP_RLE = 12;
+        public static final int IMAGE_JPEG = 13;
+        public static void error(int i) { }
+        public static void error(int i, Throwable t) { }
+    }
+
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+    static final class LEDataOutputStream extends OutputStream {
+       OutputStream out;
+        public LEDataOutputStream(OutputStream output) {
+            this.out = output;
+        }
+        public void close() throws IOException {
+            out.close();
+        }
+        public void write(byte b[], int off, int len) throws IOException {
+            out.write(b, off, len);
+        }
+        /**
+         * Write the given byte to the output stream.
+         */
+        public void write(int b) throws IOException {
+            out.write(b);
+        }
+        /**
+         * Write the given byte to the output stream.
+         */
+        public void writeByte(byte b) throws IOException {
+            out.write(b & 0xFF);
+        }
+        /**
+         * Write the four bytes of the given integer
+         * to the output stream.
+         */
+        public void writeInt(int theInt) throws IOException {
+            out.write(theInt & 0xFF);
+            out.write((theInt >> 8) & 0xFF);
+            out.write((theInt >> 16) & 0xFF);
+            out.write((theInt >> 24) & 0xFF);
+        }
+        /**
+         * Write the two bytes of the given short
+         * to the output stream.
+         */
+        public void writeShort(int theShort) throws IOException {
+            out.write(theShort & 0xFF);
+            out.write((theShort >> 8) & 0xFF);
+        }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+    public static abstract class FileFormat {
+       static final String FORMAT_PACKAGE = "org.eclipse.swt.internal.image"; //$NON-NLS-1$
+       static final String FORMAT_SUFFIX = "FileFormat"; //$NON-NLS-1$
+       static final String[] FORMATS = {"WinBMP", "WinBMP", "GIF", "WinICO", "JPEG", "PNG", "TIFF", "OS2BMP"}; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$//$NON-NLS-5$ //$NON-NLS-6$//$NON-NLS-7$//$NON-NLS-8$
+       
+       LEDataInputStream inputStream;
+       LEDataOutputStream outputStream;
+       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) {
+            FileFormat fileFormat = null;
+            LEDataInputStream stream = new LEDataInputStream(is);
+            boolean isSupported = false;       
+            for (int i = 1; i < FORMATS.length; i++) {
+               if (FORMATS[i] != null) {
+                    try {
+                        Class clazz = Class.forName(FORMAT_PACKAGE + '.' + FORMATS[i] + FORMAT_SUFFIX);
+                        fileFormat = (FileFormat) clazz.newInstance();
+                        if (fileFormat.isFileFormat(stream)) {
+                            isSupported = true;
+                            break;
+                        }
+                    } catch (ClassNotFoundException e) {
+                        FORMATS[i] = null;
+                    } catch (Exception e) {
+                    }
+               }
+            }
+            if (!isSupported) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+            fileFormat.loader = loader;
+            return fileFormat.loadFromStream(stream);
+        }
+
+        public static void save(OutputStream os, int format, ImageLoader loader) {
+            if (format < 0 || format >= FORMATS.length) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            if (FORMATS[format] == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+
+            /* We do not currently support writing multi-image files,
+             * so we use the first image data in the loader's array. */
+            ImageData data = loader.data[0];
+            LEDataOutputStream stream = new LEDataOutputStream(os);
+            FileFormat fileFormat = null;
+            try {
+               Class clazz = Class.forName(FORMAT_PACKAGE + '.' + FORMATS[format] + FORMAT_SUFFIX);
+               fileFormat = (FileFormat) clazz.newInstance();
+            } catch (Exception e) {
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            }
+            if (format == SWT.IMAGE_BMP_RLE) {
+               switch (data.depth) {
+                    case 8: fileFormat.compression = 1; break;
+                    case 4: fileFormat.compression = 2; break;
+               }
+            }
+            fileFormat.unloadIntoStream(data, stream);
+        }
+
+        abstract void unloadIntoByteStream(ImageData image);
+
+        public void unloadIntoStream(ImageData image, LEDataOutputStream stream) {
+            try {
+               outputStream = stream;
+               unloadIntoByteStream(image);
+               outputStream.close();
+            } catch (Exception e) {
+               try {outputStream.close();} catch (Exception f) {}
+               SWT.error(SWT.ERROR_IO, e);
+            }
+        }
+    }
+
+
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+    /**
+     * Classes which implement this interface provide methods
+     * that deal with the incremental loading of image data. 
+     * <p>
+     * After creating an instance of a class that implements
+     * this interface it can be added to an image loader using the
+     * <code>addImageLoaderListener</code> method and removed using
+     * the <code>removeImageLoaderListener</code> method. When
+     * image data is either partially or completely loaded, this
+     * method will be invoked.
+     * </p>
+     *
+     * @see ImageLoader
+     * @see ImageLoaderEvent
+     */
+
+    public static interface ImageLoaderListener {
+
+        /**
+         * Sent when image data is either partially or completely loaded.
+         * <p>
+         * The timing of when this method is called varies depending on
+         * the format of the image being loaded.
+         * </p>
+         *
+         * @param e an event containing information about the image loading operation
+         */
+        public void imageDataLoaded(ImageLoaderEvent e);
+
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+    /**
+     * Instances of this class are sent as a result of the incremental
+     * loading of image data.
+     * <p>
+     * <b>Notes:</b>
+     * </p><ul>
+     * <li>The number of events which will be sent when loading images
+     * is not constant. It varies by image type, and for JPEG images it 
+     * varies from image to image.</li>
+     * <li>For image sources which contain multiple images, the 
+     * <code>endOfImage</code> flag in the event will be set to true
+     * after each individual image is loaded.</li>
+     * </ul>
+     * 
+     * @see ImageLoader
+     * @see ImageLoaderListener
+     */
+
+    public static class ImageLoaderEvent  {
+       
+       /**
+        * if the <code>endOfImage</code> flag is false, then this is a
+        * partially complete copy of the current <code>ImageData</code>,
+        * otherwise this is a completely loaded <code>ImageData</code>
+        */
+       public ImageData imageData;
+
+       /**
+        * the zero-based count of image data increments -- this is
+        * equivalent to the number of events that have been generated
+        * while loading a particular image
+        */
+       public int incrementCount;
+
+       /**
+        * If this flag is true, then the current image data has been
+        * completely loaded, otherwise the image data is only partially
+        * loaded, and further ImageLoader events will occur unless an
+        * exception is thrown
+        */
+       public boolean endOfImage;
+       
+        /**
+         * Constructs a new instance of this class given the event source and
+         * the values to store in its fields.
+         *
+         * @param source the ImageLoader that was loading when the event occurred
+         * @param imageData the image data for the event
+         * @param incrementCount the image data increment for the event
+         * @param endOfImage the end of image flag for the event
+         */
+        public ImageLoaderEvent(ImageLoader source, ImageData imageData, int incrementCount, boolean endOfImage) {
+            //super(source);
+            this.imageData = imageData;
+            this.incrementCount = incrementCount;
+            this.endOfImage = endOfImage;
+        }
+
+        /**
+         * Returns a string containing a concise, human-readable
+         * description of the receiver.
+         *
+         * @return a string representation of the event
+         */
+        /*
+          public String toString () {
+          return "ImageLoaderEvent {source=" + source + " imageData=" + imageData + " incrementCount=" + incrementCount + " endOfImage=" + endOfImage + "}";
+          }
+        */
+    }
+
+
+    /*******************************************************************************
+     * Copyright (c) 2000, 2004 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+    /**
+     * Instances of this class are used to load images from,
+     * and save images to, a file or stream.
+     * <p>
+     * Currently supported image formats are:
+     * </p><ul>
+     * <li>BMP (Windows Bitmap)</li>
+     * <li>ICO (Windows Icon)</li>
+     * <li>JPEG</li>
+     * <li>GIF</li>
+     * <li>PNG</li>
+     * </ul>
+     * <code>ImageLoaders</code> can be used to:
+     * <ul>
+     * <li>load/save single images in all formats</li>
+     * <li>load/save multiple images (GIF/ICO)</li>
+     * <li>load/save animated GIF images</li>
+     * <li>load interlaced GIF/PNG images</li>
+     * <li>load progressive JPEG images</li>
+     * </ul>
+     */
+    public static class ImageLoader {
+
+       /**
+        * the array of ImageData objects in this ImageLoader.
+        * This array is read in when the load method is called,
+        * and it is written out when the save method is called
+        */
+       public ImageData[] data;
+       
+       /**
+        * the width of the logical screen on which the images
+        * reside, in pixels (this corresponds to the GIF89a
+        * Logical Screen Width value)
+        */
+       public int logicalScreenWidth;
+
+       /**
+        * the height of the logical screen on which the images
+        * reside, in pixels (this corresponds to the GIF89a
+        * Logical Screen Height value)
+        */
+       public int logicalScreenHeight;
+
+       /**
+        * the background pixel for the logical screen (this 
+        * corresponds to the GIF89a Background Color Index value).
+        * The default is -1 which means 'unspecified background'
+        * 
+        */
+       public int backgroundPixel;
+
+       /**
+        * the number of times to repeat the display of a sequence
+        * of animated images (this corresponds to the commonly-used
+        * GIF application extension for "NETSCAPE 2.0 01")
+        */
+       public int repeatCount;
+               
+       /*
+        * the set of ImageLoader event listeners, created on demand
+        */
+       Vector imageLoaderListeners;
+
+        /**
+         * Construct a new empty ImageLoader.
+         */
+        public ImageLoader() {
+            reset();
+        }
+
+        /**
+         * Resets the fields of the ImageLoader, except for the
+         * <code>imageLoaderListeners</code> field.
+         */
+        void reset() {
+            data = null;
+            logicalScreenWidth = 0;
+            logicalScreenHeight = 0;
+            backgroundPixel = -1;
+            repeatCount = 1;
+        }
+
+        /**
+         * Loads an array of <code>ImageData</code> objects from the
+         * specified input stream. Throws an error if either an error
+         * occurs while loading the images, or if the images are not
+         * of a supported type. Returns the loaded image data array.
+         *
+         * @param stream the input stream to load the images from
+         * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
+         *    <li>ERROR_IO - if an input/output error occurs while reading data</li>
+         *    <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
+         * </ul>
+         */
+        public ImageData[] load(InputStream stream) {
+            if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            reset();
+            data = FileFormat.load(stream, this);
+            return data;
+        }
+
+        /**
+         * Loads an array of <code>ImageData</code> objects from the
+         * file with the specified name. Throws an error if either
+         * an error occurs while loading the images, or if the images are
+         * not of a supported type. Returns the loaded image data array.
+         *
+         * @param filename the name of the file to load the images from
+         * @return an array of <code>ImageData</code> objects loaded from the specified file
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
+         *    <li>ERROR_IO - if an IO error occurs while reading data</li>
+         *    <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
+         * </ul>
+         */
+        public ImageData[] load(String filename) {
+            throw new Error("not implemented");
+            /*
+              if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+              InputStream stream = null;
+              try {
+              stream = Compatibility.newFileInputStream(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;
+            */
+        }
+
+        /**
+         * Saves the image data in this ImageLoader to the specified stream.
+         * The format parameter can have 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>
+         *
+         * @param stream the output stream to write the images to
+         * @param format the format to write the images in
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_INVALID_IMAGE if the image data contains invalid data</li>
+         *    <li>ERROR_IO if an IO error occurs while writing to the stream</li>
+         * </ul>
+         */
+        public void save(OutputStream stream, int format) {
+            if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            FileFormat.save(stream, format, this);
+        }
+
+        /**
+         * Saves the image data in this ImageLoader to a file with the specified name.
+         * The format parameter can have 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>
+         *
+         * @param filename the name of the file to write the images to
+         * @param format the format to write the images in
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_INVALID_IMAGE if the image data contains invalid data</li>
+         *    <li>ERROR_IO if an IO error occurs while writing to the file</li>
+         * </ul>
+         */
+        public void save(String filename, int format) {
+            throw new Error("not implemented");
+            /*
+              if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+              OutputStream stream = null;
+              try {
+              stream = Compatibility.newFileOutputStream(filename);
+              } catch (IOException e) {
+              SWT.error(SWT.ERROR_IO, e);
+              }
+              save(stream, format);
+            */
+        }
+
+        /**     
+         * Adds a listener to receive image loader events.
+         * <p>
+         * An ImageLoaderListener should be added before invoking
+         * one of the receiver's load methods. The listener's 
+         * <code>imageDataLoaded</code> method is called when image
+         * data has been partially loaded, as is supported by interlaced
+         * GIF/PNG or progressive JPEG images.
+         *
+         * @param listener the ImageLoaderListener to add
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+         * </ul>
+         * 
+         * @see ImageLoaderListener
+         * @see ImageLoaderEvent
+         */
+        public void addImageLoaderListener(ImageLoaderListener listener) {
+            if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+            if (imageLoaderListeners == null) {
+               imageLoaderListeners = new Vector();
+            }
+            imageLoaderListeners.addElement(listener);
+        }
+
+        /**     
+         * Removes a listener that was receiving image loader events.
+         *
+         * @param listener the ImageLoaderListener to remove
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+         * </ul>
+         * 
+         * @see #addImageLoaderListener(ImageLoaderListener)
+         */
+        public void removeImageLoaderListener(ImageLoaderListener listener) {
+            if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+            if (imageLoaderListeners == null) return;
+            imageLoaderListeners.removeElement(listener);
+        }
+
+        /**     
+         * Returns <code>true</code> if the receiver has image loader
+         * listeners, and <code>false</code> otherwise.
+         *
+         * @return <code>true</code> if there are <code>ImageLoaderListener</code>s, and <code>false</code> otherwise
+         *
+         * @see #addImageLoaderListener(ImageLoaderListener)
+         * @see #removeImageLoaderListener(ImageLoaderListener)
+         */
+        public boolean hasListeners() {
+            return imageLoaderListeners != null && imageLoaderListeners.size() > 0;
+        }
+
+        /**     
+         * Notifies all image loader listeners that an image loader event
+         * has occurred. Pass the specified event object to each listener.
+         *
+         * @param event the <code>ImageLoaderEvent</code> to send to each <code>ImageLoaderListener</code>
+         */
+        public void notifyListeners(ImageLoaderEvent event) {
+            if (!hasListeners()) return;
+            int size = imageLoaderListeners.size();
+            for (int i = 0; i < size; i++) {
+               ImageLoaderListener listener = (ImageLoaderListener) imageLoaderListeners.elementAt(i);
+               listener.imageDataLoaded(event);
+            }
+        }
+
+    }
+
+    /*******************************************************************************
+     * Copyright (c) 2000, 2004 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+    /**
+     * Instances of this class are descriptions of colors in
+     * terms of the primary additive color model (red, green and
+     * blue). A color may be described in terms of the relative
+     * intensities of these three primary colors. The brightness
+     * of each color is specified by a value in the range 0 to 255,
+     * where 0 indicates no color (blackness) and 255 indicates
+     * maximum intensity.
+     * <p>
+     * The hashCode() method in this class uses the values of the public
+     * fields to compute the hash value. When storing instances of the
+     * class in hashed collections, do not modify these fields after the
+     * object has been inserted.  
+     * </p>
+     * <p>
+     * Application code does <em>not</em> need to explicitly release the
+     * resources managed by each instance when those instances are no longer
+     * required, and thus no <code>dispose()</code> method is provided.
+     * </p>
+     *
+     * @see Color
+     */
+
+    public static final 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;
+       
+        /**
+         * 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$
+        }
+
+    }
+
+
+    /*******************************************************************************
+     * Copyright (c) 2000, 2004 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+    /**
+     * Instances of this class describe the color data used by an image.
+     * <p>
+     * Depending on the depth of the image, the PaletteData can take one
+     * of two forms, indicated by the isDirect field:
+     * </p>
+     * <dl>
+     * <dt>
+     * <em>isDirect is false</em>
+     * </dt>
+     * <dd>
+     * If isDirect is <code>false</code>, this palette is an indexed
+     * palette which maps pixel values to RGBs. The actual RGB values
+     * may be retrieved by using the getRGBs() method.
+     * </dd>
+     * <dt>
+     * <em>isDirect is true</em>
+     * </dt>
+     * <dd>
+     * If isDirect is <code>true</code>, this palette is a direct color
+     * palette. Instead of containing RGB values, it contains red,
+     * green and blue mask and shift information which indicates how
+     * the color components may be extracted from a given pixel.
+     * This means that the RGB value is actually encoded in the pixel value.
+     * <p>
+     * In this case, the shift data is the number of bits required to shift
+     * the RGB value to the left in order to align the high bit of the
+     * corresponding mask with the high bit of the first byte. This number
+     * may be negative, so care must be taken when shifting. For example,
+     * with a red mask of 0xFF0000, the red shift would be -16. With a red
+     * mask of 0x1F, the red shift would be 3.
+     * </p>
+     * </dd>
+     * </dl>
+     *
+     * @see Image
+     * @see RGB
+     */
+    public static final class PaletteData {
+       
+       /**
+        * true if the receiver is a direct palette, 
+        * and false otherwise
+        */
+       public boolean isDirect;
+       
+       /**
+        * the RGB values for an indexed palette, where the
+        * indices of the array correspond to pixel values
+        */
+       public RGB[] colors;
+       
+       /**
+        * the red mask for a direct palette
+        */
+       public int redMask;
+       
+       /**
+        * the green mask for a direct palette
+        */
+       public int greenMask;
+       
+       /**
+        * the blue mask for a direct palette
+        */
+       public int blueMask;
+       
+       /**
+        * the red shift for a direct palette
+        */
+       public int redShift;
+       
+       /**
+        * the green shift for a direct palette
+        */
+       public int greenShift;
+       
+       /**
+        * the blue shift for a direct palette
+        */
+       public int blueShift;
+
+        /**
+         * Constructs a new indexed palette given an array of RGB values.
+         *
+         * @param colors the array of <code>RGB</code>s for the palette
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
+         * </ul>
+         */
+        public PaletteData(RGB[] colors) {
+            if (colors == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            this.colors = colors;
+            this.isDirect = false;
+        }
+
+        /**
+         * Constructs a new direct palette given the red, green and blue masks.
+         *
+         * @param redMask the red mask
+         * @param greenMask the green mask
+         * @param blueMask the blue mask
+         */
+        public PaletteData(int redMask, int greenMask, int blueMask) {
+            this.redMask = redMask;
+            this.greenMask = greenMask;
+            this.blueMask = blueMask;
+            this.isDirect = true;
+            this.redShift = shiftForMask(redMask);
+            this.greenShift = shiftForMask(greenMask);
+            this.blueShift = shiftForMask(blueMask);
+        }
+
+        /**
+         * Returns the pixel value corresponding to the given <code>RBG</code>.
+         *
+         * @param rgb the RGB to get the pixel value for
+         * @return the pixel value for the given RGB
+         * 
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if the RGB is not found in the palette</li>
+         * </ul>
+         */
+        public int getPixel(RGB rgb) {
+            if (rgb == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            if (isDirect) {
+               int pixel = 0;
+               pixel |= (redShift < 0 ? rgb.red << -redShift : rgb.red >>> redShift) & redMask;
+               pixel |= (greenShift < 0 ? rgb.green << -greenShift : rgb.green >>> greenShift) & greenMask;
+               pixel |= (blueShift < 0 ? rgb.blue << -blueShift : rgb.blue >>> blueShift) & blueMask;
+               return pixel;
+            } else {
+               for (int i = 0; i < colors.length; i++) {
+                    if (colors[i].equals(rgb)) return i;
+               }
+               /* The RGB did not exist in the palette */
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+               return 0;
+            }
+        }
+
+        /**
+         * Returns an <code>RGB</code> corresponding to the given pixel value.
+         *
+         * @param pixel the pixel to get the RGB value for
+         * @return the RGB value for the given pixel
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if the pixel does not exist in the palette</li>
+         * </ul>
+         */
+        public RGB getRGB(int pixel) {
+            if (isDirect) {
+               int r = pixel & redMask;
+               r = (redShift < 0) ? r >>> -redShift : r << redShift;
+               int g = pixel & greenMask;
+               g = (greenShift < 0) ? g >>> -greenShift : g << greenShift;
+               int b = pixel & blueMask;
+               b = (blueShift < 0) ? b >>> -blueShift : b << blueShift;
+               return new RGB(r, g, b);
+            } else {
+               if (pixel < 0 || pixel >= colors.length) {
+                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+               }
+               return colors[pixel];
+            }
+        }
+
+        /**
+         * Returns all the RGB values in the receiver if it is an
+         * indexed palette, or null if it is a direct palette.
+         *
+         * @return the <code>RGB</code>s for the receiver or null
+         */
+        public RGB[] getRGBs() {
+            return colors;
+        }
+
+        /**
+         * Computes the shift value for a given mask.
+         *
+         * @param mask the mask to compute the shift for
+         * @return the shift amount
+         *
+         * @see PaletteData
+         */
+        int shiftForMask(int mask) {
+            for (int i = 31; i >= 0; i--) { 
+               if (((mask >> i) & 0x1) != 0) return 7 - i;
+            }
+            return 32;
+        }
+
+    }
+
+
+    /*******************************************************************************
+     * Copyright (c) 2000, 2004 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+    /**
+     * Instances of this class are device-independent descriptions
+     * of images. They are typically used as an intermediate format
+     * between loading from or writing to streams and creating an 
+     * <code>Image</code>.
+     * <p>
+     * Note that the public fields <code>x</code>, <code>y</code>, 
+     * <code>disposalMethod</code> and <code>delayTime</code> are
+     * typically only used when the image is in a set of images used
+     * for animation.
+     * </p>
+     *
+     * @see Image
+     * @see ImageLoader
+     */
+
+    public static final 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 the specified
+         * input stream. Throws an error if an error occurs while loading
+         * the image, or if the image has an unsupported type.  Application
+         * code is still responsible for closing the input stream.
+         * <p>
+         * This constructor is provided for convenience when loading a single
+         * image only. If the stream contains multiple images, only the first
+         * one will be loaded. To load multiple images, use 
+         * <code>ImageLoader.load()</code>.
+         * </p>
+         *
+         * @param stream the input stream to load the image from (must not be null)
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
+         *    <li>ERROR_IO - if an IO error occurs while reading data</li>
+         * </ul>
+         *
+         * @see ImageLoader#load(InputStream)
+         */
+        public ImageData(InputStream stream) {
+            ImageData[] data = new ImageLoader().load(stream);
+            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);
+        }
+
+        /**
+         * Constructs an <code>ImageData</code> loaded from a file with the
+         * specified name. Throws an error if an error occurs loading the
+         * image, or if the image has an unsupported type.
+         * <p>
+         * This constructor is provided for convenience when loading a single
+         * image only. If the file contains multiple images, only the first
+         * one will be loaded. To load multiple images, use 
+         * <code>ImageLoader.load()</code>.
+         * </p>
+         *
+         * @param filename the name of the file to load the image from (must not be null)
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
+         *    <li>ERROR_IO if an IO error occurs while reading data</li>
+         *    <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
+         * </ul>
+         */
+        public ImageData(String filename) {
+            ImageData[] data = new ImageLoader().load(filename);
+            if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
+            ImageData i = data[0];
+            setAllFields(
+                         i.width,
+                         i.height,
+                         i.depth,
+                         i.scanlinePad,
+                         i.bytesPerLine,
+                         i.data,
+                         i.palette,
+                         i.transparentPixel,
+                         i.maskData,
+                         i.maskPad,
+                         i.alphaData,
+                         i.alpha,
+                         i.type,
+                         i.x,
+                         i.y,
+                         i.disposalMethod,
+                         i.delayTime);
+        }
+
+        /**
+         * Prevents uninitialized instances from being created outside the package.
+         */
+        ImageData() {
+        }
+
+        /**
+         * Constructs an image data by giving values for all non-computable fields.
+         * <p>
+         * This method is for internal use, and is not described further.
+         * </p>
+         */
+        ImageData(
+                  int width, int height, int depth, PaletteData palette,
+                  int scanlinePad, byte[] data, int maskPad, byte[] maskData,
+                  byte[] alphaData, int alpha, int transparentPixel, int type,
+                  int x, int y, int disposalMethod, int delayTime)
+        {
+
+            if (palette == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            if (!(depth == 1 || depth == 2 || depth == 4 || depth == 8
+                  || depth == 16 || depth == 24 || depth == 32)) {
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            }
+            if (width <= 0 || height <= 0) {
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            }
+            if (scanlinePad == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO);
+
+            int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1))
+               / scanlinePad * scanlinePad;
+            setAllFields(
+                         width,
+                         height,
+                         depth,
+                         scanlinePad,
+                         bytesPerLine,
+                         data != null ? data : new byte[bytesPerLine * height],
+                         palette,
+                         transparentPixel,
+                         maskData,
+                         maskPad,
+                         alphaData,
+                         alpha,
+                         type,
+                         x,
+                         y,
+                         disposalMethod,
+                         delayTime);
+        }
+
+        /**
+         * Initializes all fields in the receiver. This method must be called
+         * by all public constructors to ensure that all fields are initialized
+         * for a new ImageData object. If a new field is added to the class,
+         * then it must be added to this method.
+         * <p>
+         * This method is for internal use, and is not described further.
+         * </p>
+         */
+        void setAllFields(int width, int height, int depth, int scanlinePad,
+                          int bytesPerLine, byte[] data, PaletteData palette, int transparentPixel,
+                          byte[] maskData, int maskPad, byte[] alphaData, int alpha,
+                          int type, int x, int y, int disposalMethod, int delayTime) {
+
+            this.width = width;
+            this.height = height;
+            this.depth = depth;
+            this.scanlinePad = scanlinePad;
+            this.bytesPerLine = bytesPerLine;
+            this.data = data;
+            this.palette = palette;
+            this.transparentPixel = transparentPixel;
+            this.maskData = maskData;
+            this.maskPad = maskPad;
+            this.alphaData = alphaData;
+            this.alpha = alpha;
+            this.type = type;
+            this.x = x;
+            this.y = y;
+            this.disposalMethod = disposalMethod;
+            this.delayTime = delayTime;
+        }
+
+        /**     
+         * Invokes internal SWT functionality to create a new instance of
+         * this class.
+         * <p>
+         * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+         * API for <code>ImageData</code>. It is marked public only so that it
+         * can be shared within the packages provided by SWT. It is subject
+         * to change without notice, and should never be called from
+         * application code.
+         * </p>
+         * <p>
+         * This method is for internal use, and is not described further.
+         * </p>
+         */
+        public static ImageData internal_new(
+                                             int width, int height, int depth, PaletteData palette,
+                                             int scanlinePad, byte[] data, int maskPad, byte[] maskData,
+                                             byte[] alphaData, int alpha, int transparentPixel, int type,
+                                             int x, int y, int disposalMethod, int delayTime)
+        {
+            return new ImageData(
+                                 width, height, depth, palette, scanlinePad, data, maskPad, maskData,
+                                 alphaData, alpha, transparentPixel, type, x, y, disposalMethod, delayTime);
+        }
+
+        ImageData colorMaskImage(int pixel) {
+            ImageData mask = new ImageData(width, height, 1, bwPalette(),
+                                           2, null, 0, null, null, -1, -1, SWT.IMAGE_UNDEFINED,
+                                           0, 0, 0, 0);
+            int[] row = new int[width];
+            for (int y = 0; y < height; y++) {
+               getPixels(0, y, width, row, 0);
+               for (int i = 0; i < width; i++) {
+                    if (pixel != -1 && row[i] == pixel) {
+                        row[i] = 0;
+                    } else {
+                        row[i] = 1;
+                    }
+               }
+               mask.setPixels(0, y, width, row, 0);
+            }
+            return mask;
+        }
+
+        static byte[] checkData(byte [] data) {
+            if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            return data;
+        }
+
+        /**
+         * Returns a new instance of the same class as the receiver,
+         * whose slots have been filled in with <em>copies</em> of
+         * the values in the slots of the receiver. That is, the
+         * returned object is a <em>deep copy</em> of the receiver.
+         *
+         * @return a copy of the receiver.
+         */
+        public Object clone() {
+            byte[] cloneData = new byte[data.length];
+            System.arraycopy(data, 0, cloneData, 0, data.length);
+            byte[] cloneMaskData = null;
+            if (maskData != null) {
+               cloneMaskData = new byte[maskData.length];
+               System.arraycopy(maskData, 0, cloneMaskData, 0, maskData.length);
+            }
+            byte[] cloneAlphaData = null;
+            if (alphaData != null) {
+               cloneAlphaData = new byte[alphaData.length];
+               System.arraycopy(alphaData, 0, cloneAlphaData, 0, alphaData.length);
+            }
+            return new ImageData(
+                                 width,
+                                 height,
+                                 depth,
+                                 palette,
+                                 scanlinePad,
+                                 cloneData,
+                                 maskPad,
+                                 cloneMaskData,
+                                 cloneAlphaData,
+                                 alpha,
+                                 transparentPixel,
+                                 type,
+                                 x,
+                                 y,
+                                 disposalMethod,
+                                 delayTime);
+        }
+
+        /**
+         * Returns the alpha value at offset <code>x</code> in
+         * scanline <code>y</code> in the receiver's alpha data.
+         *
+         * @param x the x coodinate of the pixel to get the alpha value of
+         * @param y the y coordinate of the pixel to get the alpha value of
+         * @return the alpha value at the given coordinates
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_INVALID_ARGUMENT - if either argument is out of range</li>
+         * </ul>
+         */
+        public int getAlpha(int x, int y) {
+            if (x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+
+            if (alphaData == null) return 255;
+            return alphaData[y * width + x] & 0xFF;
+        }
+
+        /**
+         * Returns <code>getWidth</code> alpha values starting at offset
+         * <code>x</code> in scanline <code>y</code> in the receiver's alpha
+         * data starting at <code>startIndex</code>.
+         *
+         * @param x the x position of the pixel to begin getting alpha values
+         * @param y the y position of the pixel to begin getting alpha values
+         * @param getWidth the width of the data to get
+         * @param alphas the buffer in which to put the alpha values
+         * @param startIndex the offset into the image to begin getting alpha values
+         *
+         * @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>
+         */
+        public void getAlphas(int x, int y, int getWidth, byte[] alphas, int startIndex) {
+            if (alphas == 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;
+
+            if (alphaData == null) {
+               int endIndex = startIndex + getWidth;
+               for (int i = startIndex; i < endIndex; i++) {
+                    alphas[i] = (byte)255;
+               }
+               return;
+            }
+            // may throw an IndexOutOfBoundsException
+            System.arraycopy(alphaData, y * width + x, alphas, startIndex, getWidth);
+        }
+
+        /**
+         * Returns the pixel value at offset <code>x</code> in
+         * scanline <code>y</code> in the receiver's data.
+         *
+         * @param x the x position of the pixel to get
+         * @param y the y position of the pixel to get
+         * @return the pixel at the given coordinates
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_INVALID_ARGUMENT - if either argument is out of bounds</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
+         * </ul>
+         */
+        public int getPixel(int x, int y) {
+            if (x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            int index;
+            int theByte;
+            int mask;
+            if (depth == 1) {
+               index = (y * bytesPerLine) + (x >> 3);
+               theByte = data[index] & 0xFF;
+               mask = 1 << (7 - (x & 0x7));
+               if ((theByte & mask) == 0) {
+                    return 0;
+               } else {
+                    return 1;
+               }
+            }
+            if (depth == 2) {
+               index = (y * bytesPerLine) + (x >> 2);
+               theByte = data[index] & 0xFF;
+               int offset = 3 - (x % 4);
+               mask = 3 << (offset * 2);
+               return (theByte & mask) >> (offset * 2);
+            }
+            if (depth == 4) {
+               index = (y * bytesPerLine) + (x >> 1);
+               theByte = data[index] & 0xFF;
+               if ((x & 0x1) == 0) {
+                    return theByte >> 4;
+               } else {
+                    return theByte & 0x0F;
+               }
+            }
+            if (depth == 8) {
+               index = (y * bytesPerLine) + x ;
+               return data[index] & 0xFF;
+            }
+            if (depth == 16) {
+               index = (y * bytesPerLine) + (x * 2);
+               return ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
+            }
+            if (depth == 24) {
+               index = (y * bytesPerLine) + (x * 3);
+               return ((data[index] & 0xFF) << 16) + ((data[index+1] & 0xFF) << 8) +
+                    (data[index+2] & 0xFF);
+            }
+            if (depth == 32) {
+               index = (y * bytesPerLine) + (x * 4);
+               return ((data[index] & 0xFF) << 24) + ((data[index+1] & 0xFF) << 16) +
+                    ((data[index+2] & 0xFF) << 8) + (data[index+3] & 0xFF);
+            }
+            SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+            return 0;
+        }
+
+        /**
+         * Returns <code>getWidth</code> pixel values starting at offset
+         * <code>x</code> in scanline <code>y</code> in the receiver's
+         * data starting at <code>startIndex</code>.
+         *
+         * @param x the x position of the first pixel to get
+         * @param y the y position of the first pixel to get
+         * @param getWidth the width of the data to get
+         * @param pixels the buffer in which to put the pixels
+         * @param startIndex the offset into the byte array to begin storing pixels
+         *
+         * @exception IndexOutOfBoundsException if getWidth is too large
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4 or 8
+         *        (For higher depths, use the int[] version of this method.)</li>
+         * </ul>
+         */
+        public void getPixels(int x, int y, int getWidth, byte[] pixels, int startIndex) {
+            if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            if (getWidth == 0) return;
+            int index;
+            int theByte;
+            int mask = 0;
+            int n = getWidth;
+            int i = startIndex;
+            int srcX = x, srcY = y;
+            if (depth == 1) {
+               index = (y * bytesPerLine) + (x >> 3);
+               theByte = data[index] & 0xFF;
+               while (n > 0) {
+                    mask = 1 << (7 - (srcX & 0x7));
+                    if ((theByte & mask) == 0) {
+                        pixels[i] = 0;
+                    } else {
+                        pixels[i] = 1;
+                    }
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        if (n > 0) theByte = data[index] & 0xFF;
+                        srcX = 0;
+                    } else {
+                        if (mask == 1) {
+                            index++;
+                            if (n > 0) theByte = data[index] & 0xFF;
+                        }
+                    }
+               }
+               return;
+            }
+            if (depth == 2) {
+               index = (y * bytesPerLine) + (x >> 2);
+               theByte = data[index] & 0xFF;
+               int offset;
+               while (n > 0) {
+                    offset = 3 - (srcX % 4);
+                    mask = 3 << (offset * 2);
+                    pixels[i] = (byte)((theByte & mask) >> (offset * 2));
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        if (n > 0) theByte = data[index] & 0xFF;
+                        srcX = 0;
+                    } else {
+                        if (offset == 0) {
+                            index++;
+                            theByte = data[index] & 0xFF;
+                        }
+                    }
+               }
+               return;
+            }
+            if (depth == 4) {
+               index = (y * bytesPerLine) + (x >> 1);
+               if ((x & 0x1) == 1) {
+                    theByte = data[index] & 0xFF;
+                    pixels[i] = (byte)(theByte & 0x0F);
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index++;
+                    }
+               }
+               while (n > 1) {
+                    theByte = data[index] & 0xFF;
+                    pixels[i] = (byte)(theByte >> 4);
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        pixels[i] = (byte)(theByte & 0x0F);
+                        i++;
+                        n--;
+                        srcX++;
+                        if (srcX >= width) {
+                            srcY++;
+                            index = srcY * bytesPerLine;
+                            srcX = 0;
+                        } else {
+                            index++;
+                        }
+                    }
+               }
+               if (n > 0) {
+                    theByte = data[index] & 0xFF;
+                    pixels[i] = (byte)(theByte >> 4);
+               }
+               return;
+            }
+            if (depth == 8) {
+               index = (y * bytesPerLine) + x;
+               for (int j = 0; j < getWidth; j++) {
+                    pixels[i] = data[index];
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index++;
+                    }
+               }
+               return;
+            }
+            SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+        }
+
+        /**
+         * Returns <code>getWidth</code> pixel values starting at offset
+         * <code>x</code> in scanline <code>y</code> in the receiver's
+         * data starting at <code>startIndex</code>.
+         *
+         * @param x the x position of the first pixel to get
+         * @param y the y position of the first pixel to get
+         * @param getWidth the width of the data to get
+         * @param pixels the buffer in which to put the pixels
+         * @param startIndex the offset into the buffer to begin storing pixels
+         *
+         * @exception IndexOutOfBoundsException if getWidth is too large
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
+         * </ul>
+         */
+        public void getPixels(int x, int y, int getWidth, int[] pixels, int startIndex) {
+            if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            if (getWidth == 0) return;
+            int index;
+            int theByte;
+            int mask;
+            int n = getWidth;
+            int i = startIndex;
+            int srcX = x, srcY = y;
+            if (depth == 1) {
+               index = (y * bytesPerLine) + (x >> 3);
+               theByte = data[index] & 0xFF;
+               while (n > 0) {
+                    mask = 1 << (7 - (srcX & 0x7));
+                    if ((theByte & mask) == 0) {
+                        pixels[i] = 0;
+                    } else {
+                        pixels[i] = 1;
+                    }
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        if (n > 0) theByte = data[index] & 0xFF;
+                        srcX = 0;
+                    } else {
+                        if (mask == 1) {
+                            index++;
+                            if (n > 0) theByte = data[index] & 0xFF;
+                        }
+                    }
+               }
+               return;
+            }          
+            if (depth == 2) {
+               index = (y * bytesPerLine) + (x >> 2);
+               theByte = data[index] & 0xFF;
+               int offset;
+               while (n > 0) {
+                    offset = 3 - (srcX % 4);
+                    mask = 3 << (offset * 2);
+                    pixels[i] = (byte)((theByte & mask) >> (offset * 2));
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        if (n > 0) theByte = data[index] & 0xFF;
+                        srcX = 0;
+                    } else {
+                        if (offset == 0) {
+                            index++;
+                            theByte = data[index] & 0xFF;
+                        }
+                    }
+               }
+               return;
+            }
+            if (depth == 4) {
+               index = (y * bytesPerLine) + (x >> 1);
+               if ((x & 0x1) == 1) {
+                    theByte = data[index] & 0xFF;
+                    pixels[i] = theByte & 0x0F;
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index++;
+                    }
+               }
+               while (n > 1) {
+                    theByte = data[index] & 0xFF;
+                    pixels[i] = theByte >> 4;
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        pixels[i] = theByte & 0x0F;
+                        i++;
+                        n--;
+                        srcX++;
+                        if (srcX >= width) {
+                            srcY++;
+                            index = srcY * bytesPerLine;
+                            srcX = 0;
+                        } else {
+                            index++;
+                        }
+                    }
+               }
+               if (n > 0) {
+                    theByte = data[index] & 0xFF;
+                    pixels[i] = theByte >> 4;
+               }
+               return;
+            }
+            if (depth == 8) {
+               index = (y * bytesPerLine) + x;
+               for (int j = 0; j < getWidth; j++) {
+                    pixels[i] = data[index] & 0xFF;
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index++;
+                    }
+               }
+               return;
+            }
+            if (depth == 16) {
+               index = (y * bytesPerLine) + (x * 2);
+               for (int j = 0; j < getWidth; j++) {
+                    pixels[i] = ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index += 2;
+                    }
+               }
+               return;
+            }
+            if (depth == 24) {
+               index = (y * bytesPerLine) + (x * 3);
+               for (int j = 0; j < getWidth; j++) {
+                    pixels[i] = ((data[index] & 0xFF) << 16) | ((data[index+1] & 0xFF) << 8)
+                        | (data[index+2] & 0xFF);
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index += 3;
+                    }
+               }
+               return;
+            }
+            if (depth == 32) {
+               index = (y * bytesPerLine) + (x * 4);
+               i = startIndex;
+               for (int j = 0; j < getWidth; j++) {
+                    pixels[i] = ((data[index] & 0xFF) << 24) | ((data[index+1] & 0xFF) << 16)
+                        | ((data[index+2] & 0xFF) << 8) | (data[index+3] & 0xFF);
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index += 4;
+                    }
+               }
+               return;
+            }
+            SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+        }
+
+        /**
+         * Returns an array of <code>RGB</code>s which comprise the
+         * indexed color table of the receiver, or null if the receiver
+         * has a direct color model.
+         *
+         * @return the RGB values for the image or null if direct color
+         *
+         * @see PaletteData#getRGBs()
+         */
+        public RGB[] getRGBs() {
+            return palette.getRGBs();
+        }
+
+        /**
+         * Returns an <code>ImageData</code> which specifies the
+         * transparency mask information for the receiver, or null if the
+         * receiver has no transparency and is not an icon.
+         *
+         * @return the transparency mask or null if none exists
+         */
+        public ImageData getTransparencyMask() {
+            if (getTransparencyType() == SWT.TRANSPARENCY_MASK) {
+               return new ImageData(width, height, 1, bwPalette(), maskPad, maskData);
+            } else {
+               return colorMaskImage(transparentPixel);
+            }
+        }
+
+        /**
+         * Returns the image transparency type.
+         *
+         * @return the receiver's transparency type
+         */
+        public int getTransparencyType() {
+            if (maskData != null) return SWT.TRANSPARENCY_MASK;
+            if (transparentPixel != -1) return SWT.TRANSPARENCY_PIXEL;
+            if (alphaData != null) return SWT.TRANSPARENCY_ALPHA;
+            return SWT.TRANSPARENCY_NONE;
+        }
+
+        /**
+         * Returns the byte order of the receiver.
+         * 
+         * @return MSB_FIRST or LSB_FIRST
+         */
+        int getByteOrder() {
+            return depth != 16 ? MSB_FIRST : LSB_FIRST;
+        }
+
+        /**
+         * Returns a copy of the receiver which has been stretched or
+         * shrunk to the specified size. If either the width or height
+         * is negative, the resulting image will be inverted in the
+         * associated axis.
+         *
+         * @param width the width of the new ImageData
+         * @param height the height of the new ImageData
+         * @return a scaled copy of the image
+         */
+        public ImageData scaledTo(int width, int height) {
+            /* Create a destination image with no data */
+            final boolean flipX = (width < 0);
+            if (flipX) width = - width;
+            final boolean flipY = (height < 0);
+            if (flipY) height = - height;
+
+            ImageData dest = new ImageData(
+                                           width, height, depth, palette,
+                                           scanlinePad, null, 0, null,
+                                           null, -1, transparentPixel, type,
+                                           x, y, disposalMethod, delayTime);
+
+            /* Scale the image contents */
+            if (palette.isDirect) blit(BLIT_SRC,
+                                       this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, 0, 0, 0,
+                                       ALPHA_OPAQUE, null, 0, 0, 0,
+                                       dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, 0, 0, 0,
+                                       flipX, flipY);
+            else blit(BLIT_SRC,
+                      this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, null, null, null,
+                      ALPHA_OPAQUE, null, 0, 0, 0,
+                      dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, null, null, null,
+                      flipX, flipY);
+       
+            /* Scale the image mask or alpha */
+            if (maskData != null) {
+               dest.maskPad = this.maskPad;
+               int destBpl = (dest.width + 7) / 8;
+               destBpl = (destBpl + (dest.maskPad - 1)) / dest.maskPad * dest.maskPad;
+               dest.maskData = new byte[destBpl * dest.height];
+               int srcBpl = (this.width + 7) / 8;
+               srcBpl = (srcBpl + (this.maskPad - 1)) / this.maskPad * this.maskPad;
+               blit(BLIT_SRC,
+                     this.maskData, 1, srcBpl, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
+                     ALPHA_OPAQUE, null, 0, 0, 0,
+                     dest.maskData, 1, destBpl, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
+                     flipX, flipY);
+            } else if (alpha != -1) {
+               dest.alpha = this.alpha;
+            } else if (alphaData != null) {
+               dest.alphaData = new byte[dest.width * dest.height];
+               blit(BLIT_SRC,
+                     this.alphaData, 8, this.width, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
+                     ALPHA_OPAQUE, null, 0, 0, 0,
+                     dest.alphaData, 8, dest.width, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
+                     flipX, flipY);
+            }
+            return dest;
+        }
+
+        /**
+         * Sets the alpha value at offset <code>x</code> in
+         * scanline <code>y</code> in the receiver's alpha data.
+         *
+         * @param x the x coordinate of the alpha value to set
+         * @param y the y coordinate of the alpha value to set
+         * @param alpha the value to set the alpha to
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+         *  </ul>
+         */
+        public void setAlpha(int x, int y, int alpha) {
+            if (x >= width || y >= height || x < 0 || y < 0 || alpha < 0 || alpha > 255)
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+       
+            if (alphaData == null) alphaData = new byte[width * height];
+            alphaData[y * width + x] = (byte)alpha;    
+        }
+
+        /**
+         * Sets the alpha values starting at offset <code>x</code> in
+         * scanline <code>y</code> in the receiver's alpha data to the
+         * values from the array <code>alphas</code> starting at
+         * <code>startIndex</code>.
+         *
+         * @param x the x coordinate of the pixel to being setting the alpha values
+         * @param y the y coordinate of the pixel to being setting the alpha values
+         * @param putWidth the width of the alpha values to set
+         * @param alphas the alpha values 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>
+         */
+        public void setAlphas(int x, int y, int putWidth, byte[] alphas, int startIndex) {
+            if (alphas == 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;
+       
+            if (alphaData == null) alphaData = new byte[width * height];
+            // may throw an IndexOutOfBoundsException
+            System.arraycopy(alphas, startIndex, alphaData, y * width + x, putWidth);
+        }
+
+        /**
+         * Sets the pixel value at offset <code>x</code> in
+         * scanline <code>y</code> in the receiver's data.
+         *
+         * @param x the x coordinate of the pixel to set
+         * @param y the y coordinate of the pixel to set
+         * @param pixelValue the value to set the pixel to
+         *
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
+         * </ul>
+         */
+        public void setPixel(int x, int y, int pixelValue) {
+            if (x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            int index;
+            byte theByte;
+            int mask;
+            if (depth == 1) {
+               index = (y * bytesPerLine) + (x >> 3);
+               theByte = data[index];
+               mask = 1 << (7 - (x & 0x7));
+               if ((pixelValue & 0x1) == 1) {
+                    data[index] = (byte)(theByte | mask);
+               } else {
+                    data[index] = (byte)(theByte & (mask ^ -1));
+               }
+               return;
+            }
+            if (depth == 2) {
+               index = (y * bytesPerLine) + (x >> 2);
+               theByte = data[index];
+               int offset = 3 - (x % 4);
+               mask = 0xFF ^ (3 << (offset * 2));
+               data[index] = (byte)((data[index] & mask) | (pixelValue << (offset * 2)));
+               return;
+            }
+            if (depth == 4) {
+               index = (y * bytesPerLine) + (x >> 1);
+               if ((x & 0x1) == 0) {
+                    data[index] = (byte)((data[index] & 0x0F) | ((pixelValue & 0x0F) << 4));
+               } else {
+                    data[index] = (byte)((data[index] & 0xF0) | (pixelValue & 0x0F));
+               }
+               return;
+            }
+            if (depth == 8) {
+               index = (y * bytesPerLine) + x ;
+               data[index] = (byte)(pixelValue & 0xFF);
+               return;
+            }
+            if (depth == 16) {
+               index = (y * bytesPerLine) + (x * 2);
+               data[index + 1] = (byte)((pixelValue >> 8) & 0xFF);
+               data[index] = (byte)(pixelValue & 0xFF);
+               return;
+            }
+            if (depth == 24) {
+               index = (y * bytesPerLine) + (x * 3);
+               data[index] = (byte)((pixelValue >> 16) & 0xFF);
+               data[index + 1] = (byte)((pixelValue >> 8) & 0xFF);
+               data[index + 2] = (byte)(pixelValue & 0xFF);
+               return;
+            }
+            if (depth == 32) {
+               index = (y * bytesPerLine) + (x * 4);
+               data[index]  = (byte)((pixelValue >> 24) & 0xFF);
+               data[index + 1] = (byte)((pixelValue >> 16) & 0xFF);
+               data[index + 2] = (byte)((pixelValue >> 8) & 0xFF);
+               data[index + 3] = (byte)(pixelValue & 0xFF);
+               return;
+            }
+            SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+        }
+
+        /**
+         * Sets the pixel values starting at offset <code>x</code> in
+         * scanline <code>y</code> in the receiver's data to the
+         * values from the array <code>pixels</code> starting at
+         * <code>startIndex</code>.
+         *
+         * @param x the x position of the pixel to set
+         * @param y the y position of the pixel to set
+         * @param putWidth the width of the pixels to set
+         * @param pixels the pixels to set
+         * @param startIndex the index at which to begin setting
+         *
+         * @exception IndexOutOfBoundsException if putWidth is too large
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8
+         *        (For higher depths, use the int[] version of this method.)</li>
+         * </ul>
+         */
+        public void setPixels(int x, int y, int putWidth, byte[] pixels, int startIndex) {
+            if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            if (putWidth == 0) return;
+            int index;
+            int theByte;
+            int mask;
+            int n = putWidth;
+            int i = startIndex;
+            int srcX = x, srcY = y;
+            if (depth == 1) {
+               index = (y * bytesPerLine) + (x >> 3);
+               while (n > 0) {
+                    mask = 1 << (7 - (srcX & 0x7));
+                    if ((pixels[i] & 0x1) == 1) {
+                        data[index] = (byte)((data[index] & 0xFF) | mask);
+                    } else {
+                        data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
+                    }
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        if (mask == 1) {
+                            index++;
+                        }
+                    }
+               }
+               return;
+            }
+            if (depth == 2) {
+               byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
+               index = (y * bytesPerLine) + (x >> 2);
+               int offset = 3 - (x % 4);
+               while (n > 0) {
+                    theByte = pixels[i] & 0x3;
+                    data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        offset = 0;
+                        srcX = 0;
+                    } else {
+                        if (offset == 0) {
+                            index++;
+                            offset = 3;
+                        } else {
+                            offset--;
+                        }
+                    }
+               }
+               return;
+            }
+            if (depth == 4) {
+               index = (y * bytesPerLine) + (x >> 1);
+               boolean high = (x & 0x1) == 0;
+               while (n > 0) {
+                    theByte = pixels[i] & 0x0F;
+                    if (high) {
+                        data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
+                    } else {
+                        data[index] = (byte)((data[index] & 0xF0) | theByte);
+                    }
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        high = true;
+                        srcX = 0;
+                    } else {
+                        if (!high) index++;
+                        high = !high;
+                    }
+               }
+               return;
+            }
+            if (depth == 8) {
+               index = (y * bytesPerLine) + x;
+               for (int j = 0; j < putWidth; j++) {
+                    data[index] = (byte)(pixels[i] & 0xFF);
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index++;
+                    }
+               }
+               return;
+            }
+            SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+        }
+
+        /**
+         * Sets the pixel values starting at offset <code>x</code> in
+         * scanline <code>y</code> in the receiver's data to the
+         * values from the array <code>pixels</code> starting at
+         * <code>startIndex</code>.
+         *
+         * @param x the x position of the pixel to set
+         * @param y the y position of the pixel to set
+         * @param putWidth the width of the pixels to set
+         * @param pixels the pixels to set
+         * @param startIndex the index at which to begin setting
+         *
+         * @exception IndexOutOfBoundsException if putWidth is too large
+         * @exception IllegalArgumentException <ul>
+         *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+         *    <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
+         * </ul>
+         * @exception SWTException <ul>
+         *    <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
+         * </ul>
+         */
+        public void setPixels(int x, int y, int putWidth, int[] pixels, int startIndex) {
+            if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+            if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            if (putWidth == 0) return;
+            int index;
+            int theByte;
+            int mask;
+            int n = putWidth;
+            int i = startIndex;
+            int pixel;
+            int srcX = x, srcY = y;
+            if (depth == 1) {
+               index = (y * bytesPerLine) + (x >> 3);
+               while (n > 0) {
+                    mask = 1 << (7 - (srcX & 0x7));
+                    if ((pixels[i] & 0x1) == 1) {
+                        data[index] = (byte)((data[index] & 0xFF) | mask);
+                    } else {
+                        data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
+                    }
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        if (mask == 1) {
+                            index++;
+                        }
+                    }
+               }
+               return;
+            }
+            if (depth == 2) {
+               byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
+               index = (y * bytesPerLine) + (x >> 2);
+               int offset = 3 - (x % 4);
+               while (n > 0) {
+                    theByte = pixels[i] & 0x3;
+                    data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        offset = 3;
+                        srcX = 0;
+                    } else {
+                        if (offset == 0) {
+                            index++;
+                            offset = 3;
+                        } else {
+                            offset--;
+                        }
+                    }
+               }
+               return;
+            }
+            if (depth == 4) {
+               index = (y * bytesPerLine) + (x >> 1);
+               boolean high = (x & 0x1) == 0;
+               while (n > 0) {
+                    theByte = pixels[i] & 0x0F;
+                    if (high) {
+                        data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
+                    } else {
+                        data[index] = (byte)((data[index] & 0xF0) | theByte);
+                    }
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        high = true;
+                        srcX = 0;
+                    } else {
+                        if (!high) index++;
+                        high = !high;
+                    }
+               }
+               return;
+            }
+            if (depth == 8) {
+               index = (y * bytesPerLine) + x;
+               for (int j = 0; j < putWidth; j++) {
+                    data[index] = (byte)(pixels[i] & 0xFF);
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index++;
+                    }
+               }
+               return;
+
+            }
+            if (depth == 16) {
+               index = (y * bytesPerLine) + (x * 2);
+               for (int j = 0; j < putWidth; j++) {
+                    pixel = pixels[i];
+                    data[index] = (byte)(pixel & 0xFF);
+                    data[index + 1] = (byte)((pixel >> 8) & 0xFF);
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index += 2;
+                    }
+               }
+               return;
+            }
+            if (depth == 24) {
+               index = (y * bytesPerLine) + (x * 3);
+               for (int j = 0; j < putWidth; j++) {
+                    pixel = pixels[i];
+                    data[index] = (byte)((pixel >> 16) & 0xFF);
+                    data[index + 1] = (byte)((pixel >> 8) & 0xFF);
+                    data[index + 2] = (byte)(pixel & 0xFF);
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index += 3;
+                    }
+               }
+               return;
+            }
+            if (depth == 32) {
+               index = (y * bytesPerLine) + (x * 4);
+               for (int j = 0; j < putWidth; j++) {
+                    pixel = pixels[i];
+                    data[index] = (byte)((pixel >> 24) & 0xFF);
+                    data[index + 1] = (byte)((pixel >> 16) & 0xFF);
+                    data[index + 2] = (byte)((pixel >> 8) & 0xFF);
+                    data[index + 3] = (byte)(pixel & 0xFF);
+                    i++;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index += 4;
+                    }
+               }
+               return;
+            }
+            SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+        }
+
+        /**
+         * Returns a palette with 2 colors: black & white.
+         */
+        static PaletteData bwPalette() {
+            return new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255, 255, 255)});
+        }
+
+        /**
+         * Gets the offset of the most significant bit for
+         * the given mask.
+         */
+        static int getMSBOffset(int mask) {
+            for (int i = 31; i >= 0; i--) {
+               if (((mask >> i) & 0x1) != 0) return i + 1;
+            }
+            return 0;
+        }
+
+        /**
+         * Finds the closest match.
+         */
+        static int closestMatch(int depth, byte red, byte green, byte blue, int redMask, int greenMask, int blueMask, byte[] reds, byte[] greens, byte[] blues) {
+            if (depth > 8) {
+               int rshift = 32 - getMSBOffset(redMask);
+               int gshift = 32 - getMSBOffset(greenMask);
+               int bshift = 32 - getMSBOffset(blueMask);
+               return (((red << 24) >>> rshift) & redMask) |
+                    (((green << 24) >>> gshift) & greenMask) |
+                    (((blue << 24) >>> bshift) & blueMask);
+            }
+            int r, g, b;
+            int minDistance = 0x7fffffff;
+            int nearestPixel = 0;
+            int n = reds.length;
+            for (int j = 0; j < n; j++) {
+               r = (reds[j] & 0xFF) - (red & 0xFF);
+               g = (greens[j] & 0xFF) - (green & 0xFF);
+               b = (blues[j] & 0xFF) - (blue & 0xFF);
+               int distance = r*r + g*g + b*b;
+               if (distance < minDistance) {
+                    nearestPixel = j;
+                    if (distance == 0) break;
+                    minDistance = distance;
+               }
+            }
+            return nearestPixel;
+        }
+
+        static final ImageData convertMask(ImageData mask) {
+            if (mask.depth == 1) return mask;
+            PaletteData palette = new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255,255,255)});
+            ImageData newMask = new ImageData(mask.width, mask.height, 1, palette);
+            /* Find index of black in mask palette */
+            int blackIndex = 0;
+            RGB[] rgbs = mask.getRGBs();
+            if (rgbs != null) {
+               while (blackIndex < rgbs.length) {
+                    if (rgbs[blackIndex].equals(palette.colors[0])) break;
+                    blackIndex++;
+               }
+            }
+            int[] pixels = new int[mask.width];
+            for (int y = 0; y < mask.height; y++) {
+               mask.getPixels(0, y, mask.width, pixels, 0);
+               for (int i = 0; i < pixels.length; i++) {
+                    if (pixels[i] == blackIndex) {
+                        pixels[i] = 0;
+                    } else {
+                        pixels[i] = 1;
+                    }
+               }
+               newMask.setPixels(0, y, mask.width, pixels, 0);
+            }
+            return newMask;
+        }
+
+        static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
+            if (pad == newPad) return data;
+            int stride = (width * depth + 7) / 8;
+            int bpl = (stride + (pad - 1)) / pad * pad;        
+            int newBpl = (stride + (newPad - 1)) / newPad * newPad;
+            byte[] newData = new byte[height * newBpl];
+            int srcIndex = 0, destIndex = 0;
+            for (int y = 0; y < height; y++) {
+               System.arraycopy(data, srcIndex, newData, destIndex, stride);
+               srcIndex += bpl;
+               destIndex += newBpl;
+            }
+            return newData;
+        }
+
+        /**
+         * Blit operation bits to be OR'ed together to specify the desired operation.
+         */
+        static final int
+            BLIT_SRC = 1,     // copy source directly, else applies logic operations
+            BLIT_ALPHA = 2,   // enable alpha blending
+            BLIT_DITHER = 4;  // enable dithering in low color modes
+
+        /**
+         * Alpha mode, values 0 - 255 specify global alpha level
+         */
+        static final int
+            ALPHA_OPAQUE = 255,           // Fully opaque (ignores any alpha data)
+            ALPHA_TRANSPARENT = 0,        // Fully transparent (ignores any alpha data)
+            ALPHA_CHANNEL_SEPARATE = -1,  // Use alpha channel from separate alphaData
+            ALPHA_CHANNEL_SOURCE = -2,    // Use alpha channel embedded in sourceData
+            ALPHA_MASK_UNPACKED = -3,     // Use transparency mask formed by bytes in alphaData (non-zero is opaque)
+            ALPHA_MASK_PACKED = -4,       // Use transparency mask formed by packed bits in alphaData
+            ALPHA_MASK_INDEX = -5,        // Consider source palette indices transparent if in alphaData array
+            ALPHA_MASK_RGB = -6;          // Consider source RGBs transparent if in RGB888 format alphaData array
+
+        /**
+         * Byte and bit order constants.
+         */
+        static final int LSB_FIRST = 0;
+        static final int MSB_FIRST = 1;
+
+        /**
+         * Data types (internal)
+         */
+        private static final int
+            // direct / true color formats with arbitrary masks & shifts
+            TYPE_GENERIC_8 = 0,
+            TYPE_GENERIC_16_MSB = 1,
+            TYPE_GENERIC_16_LSB = 2,
+            TYPE_GENERIC_24 = 3,
+            TYPE_GENERIC_32_MSB = 4,
+            TYPE_GENERIC_32_LSB = 5,
+            // palette indexed color formats
+            TYPE_INDEX_8 = 6,
+            TYPE_INDEX_4 = 7,
+            TYPE_INDEX_2 = 8,
+            TYPE_INDEX_1_MSB = 9,
+            TYPE_INDEX_1_LSB = 10;
+
+        /**
+         * Blits a direct palette image into a direct palette image.
+         * <p>
+         * Note: When the source and destination depth, order and masks
+         * are pairwise equal and the blitter operation is BLIT_SRC,
+         * the masks are ignored.  Hence when not changing the image
+         * data format, 0 may be specified for the masks.
+         * </p>
+         * 
+         * @param op the blitter operation: a combination of BLIT_xxx flags
+         *        (see BLIT_xxx constants)
+         * @param srcData the source byte array containing image data
+         * @param srcDepth the source depth: one of 8, 16, 24, 32
+         * @param srcStride the source number of bytes per line
+         * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
+         *        ignored if srcDepth is not 16 or 32
+         * @param srcX the top-left x-coord of the source blit region
+         * @param srcY the top-left y-coord of the source blit region
+         * @param srcWidth the width of the source blit region
+         * @param srcHeight the height of the source blit region
+         * @param srcRedMask the source red channel mask
+         * @param srcGreenMask the source green channel mask
+         * @param srcBlueMask the source blue channel mask
+         * @param alphaMode the alpha blending or mask mode, may be
+         *        an integer 0-255 for global alpha; ignored if BLIT_ALPHA
+         *        not specified in the blitter operations
+         *        (see ALPHA_MODE_xxx constants)
+         * @param alphaData the alpha blending or mask data, varies depending
+         *        on the value of alphaMode and sometimes ignored
+         * @param alphaStride the alpha data number of bytes per line
+         * @param alphaX the top-left x-coord of the alpha blit region
+         * @param alphaY the top-left y-coord of the alpha blit region
+         * @param destData the destination byte array containing image data
+         * @param destDepth the destination depth: one of 8, 16, 24, 32
+         * @param destStride the destination number of bytes per line
+         * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
+         *        ignored if destDepth is not 16 or 32
+         * @param destX the top-left x-coord of the destination blit region
+         * @param destY the top-left y-coord of the destination blit region
+         * @param destWidth the width of the destination blit region
+         * @param destHeight the height of the destination blit region
+         * @param destRedMask the destination red channel mask
+         * @param destGreenMask the destination green channel mask
+         * @param destBlueMask the destination blue channel mask
+         * @param flipX if true the resulting image is flipped along the vertical axis
+         * @param flipY if true the resulting image is flipped along the horizontal axis
+         */
+        static void blit(int op,
+                         byte[] srcData, int srcDepth, int srcStride, int srcOrder,
+                         int srcX, int srcY, int srcWidth, int srcHeight,
+                         int srcRedMask, int srcGreenMask, int srcBlueMask,
+                         int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
+                         byte[] destData, int destDepth, int destStride, int destOrder,
+                         int destX, int destY, int destWidth, int destHeight,
+                         int destRedMask, int destGreenMask, int destBlueMask,
+                         boolean flipX, boolean flipY) {
+            if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode == ALPHA_TRANSPARENT)) return;
+
+            // these should be supplied as params later
+            final int srcAlphaMask = 0, destAlphaMask = 0;
+
+            /*** Prepare scaling data ***/
+            final int dwm1 = destWidth - 1;
+            final int sfxi = (dwm1 != 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
+            final int dhm1 = destHeight - 1;
+            final int sfyi = (dhm1 != 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
+
+            /*** Prepare source-related data ***/
+            final int sbpp, stype;
+            switch (srcDepth) {
+               case 8:
+                    sbpp = 1;
+                    stype = TYPE_GENERIC_8;
+                    break;
+               case 16:
+                    sbpp = 2;
+                    stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
+                    break;
+               case 24:
+                    sbpp = 3;
+                    stype = TYPE_GENERIC_24;
+                    break;
+               case 32:
+                    sbpp = 4;
+                    stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
+                    break;
+               default:
+                    //throw new IllegalArgumentException("Invalid source type");
+                    return;
+            }                  
+            int spr = srcY * srcStride + srcX * sbpp;
+
+            /*** Prepare destination-related data ***/
+            final int dbpp, dtype;
+            switch (destDepth) {
+               case 8:
+                    dbpp = 1;
+                    dtype = TYPE_GENERIC_8;
+                    break;
+               case 16:
+                    dbpp = 2;
+                    dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
+                    break;
+               case 24:
+                    dbpp = 3;
+                    dtype = TYPE_GENERIC_24;
+                    break;
+               case 32:
+                    dbpp = 4;
+                    dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
+                    break;
+               default:
+                    //throw new IllegalArgumentException("Invalid destination type");
+                    return;
+            }                  
+            int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp;
+            final int dprxi = (flipX) ? -dbpp : dbpp;
+            final int dpryi = (flipY) ? -destStride : destStride;
+
+            /*** Prepare special processing data ***/
+            int apr;
+            if ((op & BLIT_ALPHA) != 0) {
+               switch (alphaMode) {
+                    case ALPHA_MASK_UNPACKED:
+                    case ALPHA_CHANNEL_SEPARATE:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        apr = alphaY * alphaStride + alphaX;
+                        break;
+                    case ALPHA_MASK_PACKED:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        alphaStride <<= 3;
+                        apr = alphaY * alphaStride + alphaX;
+                        break;
+                    case ALPHA_MASK_INDEX:
+                        //throw new IllegalArgumentException("Invalid alpha type");
+                        return;
+                    case ALPHA_MASK_RGB:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        apr = 0;
+                        break;
+                    default:
+                        alphaMode = (alphaMode << 16) / 255; // prescale
+                    case ALPHA_CHANNEL_SOURCE:
+                        apr = 0;
+                        break;
+               }
+            } else {
+               alphaMode = 0x10000;
+               apr = 0;
+            }
+
+            /*** Blit ***/
+            int dp = dpr;
+            int sp = spr;
+            if ((alphaMode == 0x10000) && (stype == dtype) &&
+               (srcRedMask == destRedMask) && (srcGreenMask == destGreenMask) &&
+               (srcBlueMask == destBlueMask) && (srcAlphaMask == destAlphaMask)) {
+               /*** Fast blit (straight copy) ***/
+               switch (sbpp) {
+                    case 1:
+                        for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                            for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                                destData[dp] = srcData[sp];
+                                sp += (sfx >>> 16);
+                            }
+                        }
+                        break;                                 
+                    case 2:
+                        for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                            for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                                destData[dp] = srcData[sp];
+                                destData[dp + 1] = srcData[sp + 1];
+                                sp += (sfx >>> 16) * 2;
+                            }
+                        }
+                        break;
+                    case 3:
+                        for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                            for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                                destData[dp] = srcData[sp];
+                                destData[dp + 1] = srcData[sp + 1];
+                                destData[dp + 2] = srcData[sp + 2];
+                                sp += (sfx >>> 16) * 3;
+                            }
+                        }
+                        break;
+                    case 4:
+                        for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                            for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                                destData[dp] = srcData[sp];
+                                destData[dp + 1] = srcData[sp + 1];
+                                destData[dp + 2] = srcData[sp + 2];
+                                destData[dp + 3] = srcData[sp + 3];
+                                sp += (sfx >>> 16) * 4;
+                            }
+                        }
+                        break;
+               }
+               return;
+            }
+            /*** Comprehensive blit (apply transformations) ***/
+            final int srcRedShift = getChannelShift(srcRedMask);
+            final byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)];
+            final int srcGreenShift = getChannelShift(srcGreenMask);
+            final byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)];
+            final int srcBlueShift = getChannelShift(srcBlueMask);
+            final byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)];
+            final int srcAlphaShift = getChannelShift(srcAlphaMask);
+            final byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)];
+
+            final int destRedShift = getChannelShift(destRedMask);
+            final int destRedWidth = getChannelWidth(destRedMask, destRedShift);
+            final byte[] destReds = ANY_TO_EIGHT[destRedWidth];
+            final int destRedPreShift = 8 - destRedWidth;
+            final int destGreenShift = getChannelShift(destGreenMask);
+            final int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift);
+            final byte[] destGreens = ANY_TO_EIGHT[destGreenWidth];
+            final int destGreenPreShift = 8 - destGreenWidth;
+            final int destBlueShift = getChannelShift(destBlueMask);
+            final int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift);
+            final byte[] destBlues = ANY_TO_EIGHT[destBlueWidth];
+            final int destBluePreShift = 8 - destBlueWidth;
+            final int destAlphaShift = getChannelShift(destAlphaMask);
+            final int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift);
+            final byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth];
+            final int destAlphaPreShift = 8 - destAlphaWidth;
+
+            int ap = apr, alpha = alphaMode;
+            int r = 0, g = 0, b = 0, a = 0;
+            int rq = 0, gq = 0, bq = 0, aq = 0;
+            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+                     sp = spr += (sfy >>> 16) * srcStride,
+                     ap = apr += (sfy >>> 16) * alphaStride,
+                     sfy = (sfy & 0xffff) + sfyi,
+                     dp = dpr += dpryi) {
+               for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                         dp += dprxi,
+                         sfx = (sfx & 0xffff) + sfxi) {
+                    /*** READ NEXT PIXEL ***/
+                    switch (stype) {
+                        case TYPE_GENERIC_8: {
+                            final int data = srcData[sp] & 0xff;
+                            sp += (sfx >>> 16);
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_16_MSB: {
+                            final int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff);
+                            sp += (sfx >>> 16) * 2;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_16_LSB: {
+                            final int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff);
+                            sp += (sfx >>> 16) * 2;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_24: {
+                            final int data = (( ((srcData[sp] & 0xff) << 8) |
+                                               (srcData[sp + 1] & 0xff)) << 8) |
+                                (srcData[sp + 2] & 0xff);
+                            sp += (sfx >>> 16) * 3;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_32_MSB: {
+                            final int data = (( (( ((srcData[sp] & 0xff) << 8) |
+                                                   (srcData[sp + 1] & 0xff)) << 8) |
+                                               (srcData[sp + 2] & 0xff)) << 8) |
+                                (srcData[sp + 3] & 0xff);
+                            sp += (sfx >>> 16) * 4;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_32_LSB: {
+                            final int data = (( (( ((srcData[sp + 3] & 0xff) << 8) |
+                                                   (srcData[sp + 2] & 0xff)) << 8) |
+                                               (srcData[sp + 1] & 0xff)) << 8) |
+                                (srcData[sp] & 0xff);
+                            sp += (sfx >>> 16) * 4;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                    }
+
+                    /*** DO SPECIAL PROCESSING IF REQUIRED ***/
+                    switch (alphaMode) {
+                        case ALPHA_CHANNEL_SEPARATE:
+                            alpha = ((alphaData[ap] & 0xff) << 16) / 255;
+                            ap += (sfx >> 16);
+                            break;
+                        case ALPHA_CHANNEL_SOURCE:
+                            alpha = (a << 16) / 255;
+                            break;
+                        case ALPHA_MASK_UNPACKED:
+                            alpha = (alphaData[ap] != 0) ? 0x10000 : 0;
+                            ap += (sfx >> 16);
+                            break;                                             
+                        case ALPHA_MASK_PACKED:
+                            alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
+                            ap += (sfx >> 16);
+                            break;
+                        case ALPHA_MASK_RGB:
+                            alpha = 0x10000;
+                            for (int i = 0; i < alphaData.length; i += 3) {
+                                if ((r == alphaData[i]) && (g == alphaData[i + 1]) && (b == alphaData[i + 2])) {
+                                    alpha = 0x0000;
+                                    break;
+                                }
+                            }
+                            break;
+                    }
+                    if (alpha != 0x10000) {
+                        if (alpha == 0x0000) continue;
+                        switch (dtype) {
+                            case TYPE_GENERIC_8: {
+                                final int data = destData[dp] & 0xff;
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_16_MSB: {
+                                final int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_16_LSB: {
+                                final int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_24: {
+                                final int data = (( ((destData[dp] & 0xff) << 8) |
+                                                    (destData[dp + 1] & 0xff)) << 8) |
+                                    (destData[dp + 2] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_32_MSB: {
+                                final int data = (( (( ((destData[dp] & 0xff) << 8) |
+                                                       (destData[dp + 1] & 0xff)) << 8) |
+                                                    (destData[dp + 2] & 0xff)) << 8) |
+                                    (destData[dp + 3] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_32_LSB: {
+                                final int data = (( (( ((destData[dp + 3] & 0xff) << 8) |
+                                                       (destData[dp + 2] & 0xff)) << 8) |
+                                                    (destData[dp + 1] & 0xff)) << 8) |
+                                    (destData[dp] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                        }
+                        // Perform alpha blending
+                        a = aq + ((a - aq) * alpha >> 16);
+                        r = rq + ((r - rq) * alpha >> 16);
+                        g = gq + ((g - gq) * alpha >> 16);
+                        b = bq + ((b - bq) * alpha >> 16);
+                    }
+
+                    /*** WRITE NEXT PIXEL ***/
+                    final int data = 
+                        (r >>> destRedPreShift << destRedShift) |
+                        (g >>> destGreenPreShift << destGreenShift) |
+                        (b >>> destBluePreShift << destBlueShift) |
+                        (a >>> destAlphaPreShift << destAlphaShift);
+                    switch (dtype) {
+                        case TYPE_GENERIC_8: {
+                            destData[dp] = (byte) data;
+                        } break;
+                        case TYPE_GENERIC_16_MSB: {
+                            destData[dp] = (byte) (data >>> 8);
+                            destData[dp + 1] = (byte) (data & 0xff);
+                        } break;
+                        case TYPE_GENERIC_16_LSB: {
+                            destData[dp] = (byte) (data & 0xff);
+                            destData[dp + 1] = (byte) (data >>> 8);
+                        } break;
+                        case TYPE_GENERIC_24: {
+                            destData[dp] = (byte) (data >>> 16);
+                            destData[dp + 1] = (byte) (data >>> 8);
+                            destData[dp + 2] = (byte) (data & 0xff);
+                        } break;
+                        case TYPE_GENERIC_32_MSB: {
+                            destData[dp] = (byte) (data >>> 24);
+                            destData[dp + 1] = (byte) (data >>> 16);
+                            destData[dp + 2] = (byte) (data >>> 8);
+                            destData[dp + 3] = (byte) (data & 0xff);
+                        } break;
+                        case TYPE_GENERIC_32_LSB: {
+                            destData[dp] = (byte) (data & 0xff);
+                            destData[dp + 1] = (byte) (data >>> 8);
+                            destData[dp + 2] = (byte) (data >>> 16);
+                            destData[dp + 3] = (byte) (data >>> 24);
+                        } break;
+                    }
+               }
+            }                  
+        }
+
+        /**
+         * Blits an index palette image into an index palette image.
+         * <p>
+         * Note: The source and destination red, green, and blue
+         * arrays may be null if no alpha blending or dither is to be
+         * performed.
+         * </p>
+         * 
+         * @param op the blitter operation: a combination of BLIT_xxx flags
+         *        (see BLIT_xxx constants)
+         * @param srcData the source byte array containing image data
+         * @param srcDepth the source depth: one of 1, 2, 4, 8
+         * @param srcStride the source number of bytes per line
+         * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
+         *        ignored if srcDepth is not 1
+         * @param srcX the top-left x-coord of the source blit region
+         * @param srcY the top-left y-coord of the source blit region
+         * @param srcWidth the width of the source blit region
+         * @param srcHeight the height of the source blit region
+         * @param srcReds the source palette red component intensities
+         * @param srcGreens the source palette green component intensities
+         * @param srcBlues the source palette blue component intensities
+         * @param alphaMode the alpha blending or mask mode, may be
+         *        an integer 0-255 for global alpha; ignored if BLIT_ALPHA
+         *        not specified in the blitter operations
+         *        (see ALPHA_MODE_xxx constants)
+         * @param alphaData the alpha blending or mask data, varies depending
+         *        on the value of alphaMode and sometimes ignored
+         * @param alphaStride the alpha data number of bytes per line
+         * @param alphaX the top-left x-coord of the alpha blit region
+         * @param alphaY the top-left y-coord of the alpha blit region
+         * @param destData the destination byte array containing image data
+         * @param destDepth the destination depth: one of 1, 2, 4, 8
+         * @param destStride the destination number of bytes per line
+         * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
+         *        ignored if destDepth is not 1
+         * @param destX the top-left x-coord of the destination blit region
+         * @param destY the top-left y-coord of the destination blit region
+         * @param destWidth the width of the destination blit region
+         * @param destHeight the height of the destination blit region
+         * @param destReds the destination palette red component intensities
+         * @param destGreens the destination palette green component intensities
+         * @param destBlues the destination palette blue component intensities
+         * @param flipX if true the resulting image is flipped along the vertical axis
+         * @param flipY if true the resulting image is flipped along the horizontal axis
+         */
+        static void blit(int op,
+                         byte[] srcData, int srcDepth, int srcStride, int srcOrder,
+                         int srcX, int srcY, int srcWidth, int srcHeight,
+                         byte[] srcReds, byte[] srcGreens, byte[] srcBlues,
+                         int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
+                         byte[] destData, int destDepth, int destStride, int destOrder,
+                         int destX, int destY, int destWidth, int destHeight,
+                         byte[] destReds, byte[] destGreens, byte[] destBlues,
+                         boolean flipX, boolean flipY) {
+            if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode == ALPHA_TRANSPARENT)) return;
+
+            /*** Prepare scaling data ***/
+            final int dwm1 = destWidth - 1;
+            final int sfxi = (dwm1 != 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
+            final int dhm1 = destHeight - 1;
+            final int sfyi = (dhm1 != 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
+
+            /*** Prepare source-related data ***/
+            final int stype;
+            switch (srcDepth) {
+               case 8:
+                    stype = TYPE_INDEX_8;
+                    break;
+               case 4:
+                    srcStride <<= 1;
+                    stype = TYPE_INDEX_4;
+                    break;
+               case 2:
+                    srcStride <<= 2;
+                    stype = TYPE_INDEX_2;
+                    break;
+               case 1:
+                    srcStride <<= 3;
+                    stype = (srcOrder == MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
+                    break;
+               default:
+                    //throw new IllegalArgumentException("Invalid source type");
+                    return;            
+            }                  
+            int spr = srcY * srcStride + srcX;
+
+            /*** Prepare destination-related data ***/
+            final int dtype;
+            switch (destDepth) {
+               case 8:
+                    dtype = TYPE_INDEX_8;
+                    break;
+               case 4:
+                    destStride <<= 1;
+                    dtype = TYPE_INDEX_4;
+                    break;
+               case 2:
+                    destStride <<= 2;
+                    dtype = TYPE_INDEX_2;
+                    break;
+               case 1:
+                    destStride <<= 3;
+                    dtype = (destOrder == MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
+                    break;
+               default:
+                    //throw new IllegalArgumentException("Invalid source type");
+                    return;
+            }                  
+            int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX);
+            final int dprxi = (flipX) ? -1 : 1;
+            final int dpryi = (flipY) ? -destStride : destStride;
+
+            /*** Prepare special processing data ***/
+            int apr;
+            if ((op & BLIT_ALPHA) != 0) {
+               switch (alphaMode) {
+                    case ALPHA_MASK_UNPACKED:
+                    case ALPHA_CHANNEL_SEPARATE:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        apr = alphaY * alphaStride + alphaX;
+                        break;
+                    case ALPHA_MASK_PACKED:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        alphaStride <<= 3;
+                        apr = alphaY * alphaStride + alphaX;
+                        break;
+                    case ALPHA_MASK_INDEX:
+                    case ALPHA_MASK_RGB:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        apr = 0;
+                        break;
+                    default:
+                        alphaMode = (alphaMode << 16) / 255; // prescale
+                    case ALPHA_CHANNEL_SOURCE:
+                        apr = 0;
+                        break;
+               }
+            } else {
+               alphaMode = 0x10000;
+               apr = 0;
+            }
+            final boolean ditherEnabled = (op & BLIT_DITHER) != 0;
+
+            /*** Blit ***/
+            int dp = dpr;
+            int sp = spr;
+            int ap = apr;
+            int destPaletteSize = 1 << destDepth;
+            if ((destReds != null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length;
+            byte[] paletteMapping = null;
+            boolean isExactPaletteMapping = true;
+            switch (alphaMode) {
+               case 0x10000:
+                    /*** If the palettes and formats are equivalent use a one-to-one mapping ***/
+                    if ((stype == dtype) &&
+                        (srcReds == destReds) && (srcGreens == destGreens) && (srcBlues == destBlues)) {
+                        paletteMapping = ONE_TO_ONE_MAPPING;
+                        break;
+                       /*** If palettes have not been supplied, supply a suitable mapping ***/
+                    } else if ((srcReds == null) || (destReds == null)) {
+                        if (srcDepth <= destDepth) {
+                            paletteMapping = ONE_TO_ONE_MAPPING;
+                        } else {
+                            paletteMapping = new byte[1 << srcDepth];
+                            int mask = (0xff << destDepth) >>> 8;
+                            for (int i = 0; i < paletteMapping.length; ++i) paletteMapping[i] = (byte)(i & mask);
+                        }
+                        break;
+                    }
+               case ALPHA_MASK_UNPACKED:
+               case ALPHA_MASK_PACKED:
+               case ALPHA_MASK_INDEX:
+               case ALPHA_MASK_RGB:
+                    /*** Generate a palette mapping ***/
+                    int srcPaletteSize = 1 << srcDepth;
+                    paletteMapping = new byte[srcPaletteSize];
+                    if ((srcReds != null) && (srcReds.length < srcPaletteSize)) srcPaletteSize = srcReds.length;
+                    for (int i = 0, r, g, b, index; i < srcPaletteSize; ++i) {
+                        r = srcReds[i] & 0xff;
+                        g = srcGreens[i] & 0xff;
+                        b = srcBlues[i] & 0xff;
+                        index = 0;
+                        int minDistance = 0x7fffffff;
+                        for (int j = 0, dr, dg, db, distance; j < destPaletteSize; ++j) {
+                            dr = (destReds[j] & 0xff) - r;
+                            dg = (destGreens[j] & 0xff) - g;
+                            db = (destBlues[j] & 0xff) - b;
+                            distance = dr * dr + dg * dg + db * db;
+                            if (distance < minDistance) {
+                                index = j;
+                                if (distance == 0) break;
+                                minDistance = distance;
+                            }
+                        }
+                        paletteMapping[i] = (byte)index;
+                        if (minDistance != 0) isExactPaletteMapping = false;
+                    }
+                    break;
+            }
+            if ((paletteMapping != null) && (isExactPaletteMapping || ! ditherEnabled)) {
+               if ((stype == dtype) && (alphaMode == 0x10000)) {
+                    /*** Fast blit (copy w/ mapping) ***/
+                    switch (stype) {
+                        case TYPE_INDEX_8:
+                            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                                for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                                    destData[dp] = paletteMapping[srcData[sp] & 0xff];
+                                    sp += (sfx >>> 16);
+                                }
+                            }
+                            break;                                     
+                        case TYPE_INDEX_4:
+                            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                                for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                                    final int v;
+                                    if ((sp & 1) != 0) v = paletteMapping[srcData[sp >> 1] & 0x0f];
+                                    else v = (srcData[sp >> 1] >>> 4) & 0x0f;
+                                    sp += (sfx >>> 16);
+                                    if ((dp & 1) != 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | v);
+                                    else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (v << 4));
+                                }
+                            }
+                            break;
+                        case TYPE_INDEX_2:
+                            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                                for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                                    final int index = paletteMapping[(srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03];
+                                    sp += (sfx >>> 16);
+                                    final int shift = 6 - (dp & 3) * 2;
+                                    destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift));
+                                }
+                            }
+                            break;                                     
+                        case TYPE_INDEX_1_MSB:
+                            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                                for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                                    final int index = paletteMapping[(srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01];
+                                    sp += (sfx >>> 16);
+                                    final int shift = 7 - (dp & 7);
+                                    destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
+                                }
+                            }
+                            break;                                     
+                        case TYPE_INDEX_1_LSB:
+                            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                                for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                                    final int index = paletteMapping[(srcData[sp >> 3] >>> (sp & 7)) & 0x01];
+                                    sp += (sfx >>> 16);
+                                    final int shift = dp & 7;
+                                    destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
+                                }
+                            }
+                            break;
+                    }
+               } else {
+                    /*** Convert between indexed modes using mapping and mask ***/
+                    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+                             sp = spr += (sfy >>> 16) * srcStride,
+                             sfy = (sfy & 0xffff) + sfyi,
+                             dp = dpr += dpryi) {
+                        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                                 dp += dprxi,
+                                 sfx = (sfx & 0xffff) + sfxi) {
+                            int index;
+                            /*** READ NEXT PIXEL ***/
+                            switch (stype) {
+                                case TYPE_INDEX_8:
+                                    index = srcData[sp] & 0xff;
+                                    sp += (sfx >>> 16);
+                                    break;                                     
+                                case TYPE_INDEX_4:
+                                    if ((sp & 1) != 0) index = srcData[sp >> 1] & 0x0f;
+                                    else index = (srcData[sp >> 1] >>> 4) & 0x0f;
+                                    sp += (sfx >>> 16);
+                                    break;                                     
+                                case TYPE_INDEX_2:
+                                    index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
+                                    sp += (sfx >>> 16);
+                                    break;                                     
+                                case TYPE_INDEX_1_MSB:
+                                    index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
+                                    sp += (sfx >>> 16);
+                                    break;                                     
+                                case TYPE_INDEX_1_LSB:
+                                    index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
+                                    sp += (sfx >>> 16);
+                                    break;
+                                default:
+                                    return;
+                            }
+                            /*** APPLY MASK ***/
+                            switch (alphaMode) {
+                                case ALPHA_MASK_UNPACKED: {
+                                    final byte mask = alphaData[ap];
+                                    ap += (sfx >> 16);
+                                    if (mask == 0) continue;
+                                } break;
+                                case ALPHA_MASK_PACKED: {
+                                    final int mask = alphaData[ap >> 3] & (1 << (ap & 7));
+                                    ap += (sfx >> 16);
+                                    if (mask == 0) continue;
+                                } break;
+                                case ALPHA_MASK_INDEX: {
+                                    int i = 0;
+                                    while (i < alphaData.length) {
+                                        if (index == (alphaData[i] & 0xff)) break;
+                                    }
+                                    if (i < alphaData.length) continue;
+                                } break;
+                                case ALPHA_MASK_RGB: {
+                                    final byte r = srcReds[index], g = srcGreens[index], b = srcBlues[index];
+                                    int i = 0;
+                                    while (i < alphaData.length) {
+                                        if ((r == alphaData[i]) && (g == alphaData[i + 1]) && (b == alphaData[i + 2])) break;
+                                        i += 3;
+                                    }
+                                    if (i < alphaData.length) continue;
+                                } break;
+                            }
+                            index = paletteMapping[index] & 0xff;
+                       
+                            /*** WRITE NEXT PIXEL ***/
+                            switch (dtype) {
+                                case TYPE_INDEX_8:
+                                    destData[dp] = (byte) index;
+                                    break;
+                                case TYPE_INDEX_4:
+                                    if ((dp & 1) != 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | index);
+                                    else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (index << 4));
+                                    break;                                     
+                                case TYPE_INDEX_2: {
+                                    final int shift = 6 - (dp & 3) * 2;
+                                    destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift));
+                                } break;                                       
+                                case TYPE_INDEX_1_MSB: {
+                                    final int shift = 7 - (dp & 7);
+                                    destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
+                                } break;
+                                case TYPE_INDEX_1_LSB: {
+                                    final int shift = dp & 7;
+                                    destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
+                                } break;                                       
+                            }
+                        }
+                    }
+               }
+               return;
+            }
+               
+            /*** Comprehensive blit (apply transformations) ***/
+            int alpha = alphaMode;
+            int index = 0;
+            int indexq = 0;
+            int lastindex = 0, lastr = -1, lastg = -1, lastb = -1;
+            final int[] rerr, gerr, berr;
+            if (ditherEnabled) {
+               rerr = new int[destWidth + 2];
+               gerr = new int[destWidth + 2];
+               berr = new int[destWidth + 2];
+            } else {
+               rerr = null; gerr = null; berr = null;
+            }
+            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+                     sp = spr += (sfy >>> 16) * srcStride,
+                     ap = apr += (sfy >>> 16) * alphaStride,
+                     sfy = (sfy & 0xffff) + sfyi,
+                     dp = dpr += dpryi) {
+               int lrerr = 0, lgerr = 0, lberr = 0;
+               for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                         dp += dprxi,
+                         sfx = (sfx & 0xffff) + sfxi) {
+                    /*** READ NEXT PIXEL ***/
+                    switch (stype) {
+                        case TYPE_INDEX_8:
+                            index = srcData[sp] & 0xff;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_4:
+                            if ((sp & 1) != 0) index = srcData[sp >> 1] & 0x0f;
+                            else index = (srcData[sp >> 1] >>> 4) & 0x0f;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_2:
+                            index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_1_MSB:
+                            index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_1_LSB:
+                            index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
+                            sp += (sfx >>> 16);
+                            break;
+                    }
+
+                    /*** DO SPECIAL PROCESSING IF REQUIRED ***/
+                    int r = srcReds[index] & 0xff, g = srcGreens[index] & 0xff, b = srcBlues[index] & 0xff;
+                    switch (alphaMode) {
+                        case ALPHA_CHANNEL_SEPARATE:
+                            alpha = ((alphaData[ap] & 0xff) << 16) / 255;
+                            ap += (sfx >> 16);
+                            break;
+                        case ALPHA_MASK_UNPACKED:
+                            alpha = (alphaData[ap] != 0) ? 0x10000 : 0;
+                            ap += (sfx >> 16);
+                            break;                                             
+                        case ALPHA_MASK_PACKED:
+                            alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
+                            ap += (sfx >> 16);
+                            break;
+                        case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices
+                            int i = 0;
+                            while (i < alphaData.length) {
+                                if (index == (alphaData[i] & 0xff)) break;
+                            }
+                            if (i < alphaData.length) continue;
+                        } break;
+                        case ALPHA_MASK_RGB: {
+                            int i = 0;
+                            while (i < alphaData.length) {
+                                if ((r == (alphaData[i] & 0xff)) &&
+                                    (g == (alphaData[i + 1] & 0xff)) &&
+                                    (b == (alphaData[i + 2] & 0xff))) break;
+                                i += 3;
+                            }
+                            if (i < alphaData.length) continue;
+                        } break;
+                    }
+                    if (alpha != 0x10000) {
+                        if (alpha == 0x0000) continue;
+                        switch (dtype) {
+                            case TYPE_INDEX_8:
+                                indexq = destData[dp] & 0xff;
+                                break;
+                            case TYPE_INDEX_4:
+                                if ((dp & 1) != 0) indexq = destData[dp >> 1] & 0x0f;
+                                else indexq = (destData[dp >> 1] >>> 4) & 0x0f;
+                                break;
+                            case TYPE_INDEX_2:
+                                indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03;
+                                break;
+                            case TYPE_INDEX_1_MSB:
+                                indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01;
+                                break;
+                            case TYPE_INDEX_1_LSB:
+                                indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01;
+                                break;
+                        }
+                        // Perform alpha blending
+                        final int rq = destReds[indexq] & 0xff;
+                        final int gq = destGreens[indexq] & 0xff;
+                        final int bq = destBlues[indexq] & 0xff;
+                        r = rq + ((r - rq) * alpha >> 16);
+                        g = gq + ((g - gq) * alpha >> 16);
+                        b = bq + ((b - bq) * alpha >> 16);
+                    }
+
+                    /*** MAP COLOR TO THE PALETTE ***/
+                    if (ditherEnabled) {
+                        // Floyd-Steinberg error diffusion
+                        r += rerr[dx] >> 4;
+                        if (r < 0) r = 0; else if (r > 255) r = 255;
+                        g += gerr[dx] >> 4;
+                        if (g < 0) g = 0; else if (g > 255) g = 255;
+                        b += berr[dx] >> 4;
+                        if (b < 0) b = 0; else if (b > 255) b = 255;
+                        rerr[dx] = lrerr;
+                        gerr[dx] = lgerr;
+                        berr[dx] = lberr;
+                    }
+                    if (r != lastr || g != lastg || b != lastb) {
+                        // moving the variable declarations out seems to make the JDK JIT happier...
+                        for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) {
+                            dr = (destReds[j] & 0xff) - r;
+                            dg = (destGreens[j] & 0xff) - g;
+                            db = (destBlues[j] & 0xff) - b;
+                            distance = dr * dr + dg * dg + db * db;
+                            if (distance < minDistance) {
+                                lastindex = j;
+                                if (distance == 0) break;
+                                minDistance = distance;
+                            }
+                        }
+                        lastr = r; lastg = g; lastb = b;
+                    }
+                    if (ditherEnabled) {
+                        // Floyd-Steinberg error diffusion, cont'd...
+                        final int dxm1 = dx - 1, dxp1 = dx + 1;
+                        int acc;
+                        rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr;
+                        rerr[dx] += acc += lrerr + lrerr;
+                        rerr[dxm1] += acc + lrerr + lrerr;
+                        gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr;
+                        gerr[dx] += acc += lgerr + lgerr;
+                        gerr[dxm1] += acc + lgerr + lgerr;
+                        berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr;
+                        berr[dx] += acc += lberr + lberr;
+                        berr[dxm1] += acc + lberr + lberr;
+                    }
+
+                    /*** WRITE NEXT PIXEL ***/
+                    switch (dtype) {
+                        case TYPE_INDEX_8:
+                            destData[dp] = (byte) lastindex;
+                            break;
+                        case TYPE_INDEX_4:
+                            if ((dp & 1) != 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | lastindex);
+                            else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4));
+                            break;
+                        case TYPE_INDEX_2: {
+                            final int shift = 6 - (dp & 3) * 2;
+                            destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift));
+                        } break;                                       
+                        case TYPE_INDEX_1_MSB: {
+                            final int shift = 7 - (dp & 7);
+                            destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
+                        } break;
+                        case TYPE_INDEX_1_LSB: {
+                            final int shift = dp & 7;
+                            destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
+                        } break;                                       
+                    }
+               }
+            }
+        }
+
+        /**
+         * Blits an index palette image into a direct palette image.
+         * <p>
+         * Note: The source and destination masks and palettes must
+         * always be fully specified.
+         * </p>
+         * 
+         * @param op the blitter operation: a combination of BLIT_xxx flags
+         *        (see BLIT_xxx constants)
+         * @param srcData the source byte array containing image data
+         * @param srcDepth the source depth: one of 1, 2, 4, 8
+         * @param srcStride the source number of bytes per line
+         * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
+         *        ignored if srcDepth is not 1
+         * @param srcX the top-left x-coord of the source blit region
+         * @param srcY the top-left y-coord of the source blit region
+         * @param srcWidth the width of the source blit region
+         * @param srcHeight the height of the source blit region
+         * @param srcReds the source palette red component intensities
+         * @param srcGreens the source palette green component intensities
+         * @param srcBlues the source palette blue component intensities
+         * @param alphaMode the alpha blending or mask mode, may be
+         *        an integer 0-255 for global alpha; ignored if BLIT_ALPHA
+         *        not specified in the blitter operations
+         *        (see ALPHA_MODE_xxx constants)
+         * @param alphaData the alpha blending or mask data, varies depending
+         *        on the value of alphaMode and sometimes ignored
+         * @param alphaStride the alpha data number of bytes per line
+         * @param alphaX the top-left x-coord of the alpha blit region
+         * @param alphaY the top-left y-coord of the alpha blit region
+         * @param destData the destination byte array containing image data
+         * @param destDepth the destination depth: one of 8, 16, 24, 32
+         * @param destStride the destination number of bytes per line
+         * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
+         *        ignored if destDepth is not 16 or 32
+         * @param destX the top-left x-coord of the destination blit region
+         * @param destY the top-left y-coord of the destination blit region
+         * @param destWidth the width of the destination blit region
+         * @param destHeight the height of the destination blit region
+         * @param destRedMask the destination red channel mask
+         * @param destGreenMask the destination green channel mask
+         * @param destBlueMask the destination blue channel mask
+         * @param flipX if true the resulting image is flipped along the vertical axis
+         * @param flipY if true the resulting image is flipped along the horizontal axis
+         */
+        static void blit(int op,
+                         byte[] srcData, int srcDepth, int srcStride, int srcOrder,
+                         int srcX, int srcY, int srcWidth, int srcHeight,
+                         byte[] srcReds, byte[] srcGreens, byte[] srcBlues,
+                         int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
+                         byte[] destData, int destDepth, int destStride, int destOrder,
+                         int destX, int destY, int destWidth, int destHeight,
+                         int destRedMask, int destGreenMask, int destBlueMask,
+                         boolean flipX, boolean flipY) {
+            if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode == ALPHA_TRANSPARENT)) return;
+
+            // these should be supplied as params later
+            final int destAlphaMask = 0;
+
+            /*** Prepare scaling data ***/
+            final int dwm1 = destWidth - 1;
+            final int sfxi = (dwm1 != 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
+            final int dhm1 = destHeight - 1;
+            final int sfyi = (dhm1 != 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
+
+            /*** Prepare source-related data ***/
+            final int stype;
+            switch (srcDepth) {
+               case 8:
+                    stype = TYPE_INDEX_8;
+                    break;
+               case 4:
+                    srcStride <<= 1;
+                    stype = TYPE_INDEX_4;
+                    break;
+               case 2:
+                    srcStride <<= 2;
+                    stype = TYPE_INDEX_2;
+                    break;
+               case 1:
+                    srcStride <<= 3;
+                    stype = (srcOrder == MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
+                    break;
+               default:
+                    //throw new IllegalArgumentException("Invalid source type");
+                    return;
+            }                  
+            int spr = srcY * srcStride + srcX;
+
+            /*** Prepare destination-related data ***/
+            final int dbpp, dtype;
+            switch (destDepth) {
+               case 8:
+                    dbpp = 1;
+                    dtype = TYPE_GENERIC_8;
+                    break;
+               case 16:
+                    dbpp = 2;
+                    dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
+                    break;
+               case 24:
+                    dbpp = 3;
+                    dtype = TYPE_GENERIC_24;
+                    break;
+               case 32:
+                    dbpp = 4;
+                    dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
+                    break;
+               default:
+                    //throw new IllegalArgumentException("Invalid destination type");
+                    return;
+            }                  
+            int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp;
+            final int dprxi = (flipX) ? -dbpp : dbpp;
+            final int dpryi = (flipY) ? -destStride : destStride;
+
+            /*** Prepare special processing data ***/
+            int apr;
+            if ((op & BLIT_ALPHA) != 0) {
+               switch (alphaMode) {
+                    case ALPHA_MASK_UNPACKED:
+                    case ALPHA_CHANNEL_SEPARATE:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        apr = alphaY * alphaStride + alphaX;
+                        break;
+                    case ALPHA_MASK_PACKED:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        alphaStride <<= 3;
+                        apr = alphaY * alphaStride + alphaX;
+                        break;
+                    case ALPHA_MASK_INDEX:
+                    case ALPHA_MASK_RGB:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        apr = 0;
+                        break;
+                    default:
+                        alphaMode = (alphaMode << 16) / 255; // prescale
+                    case ALPHA_CHANNEL_SOURCE:
+                        apr = 0;
+                        break;
+               }
+            } else {
+               alphaMode = 0x10000;
+               apr = 0;
+            }
+
+            /*** Comprehensive blit (apply transformations) ***/
+            final int destRedShift = getChannelShift(destRedMask);
+            final int destRedWidth = getChannelWidth(destRedMask, destRedShift);
+            final byte[] destReds = ANY_TO_EIGHT[destRedWidth];
+            final int destRedPreShift = 8 - destRedWidth;
+            final int destGreenShift = getChannelShift(destGreenMask);
+            final int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift);
+            final byte[] destGreens = ANY_TO_EIGHT[destGreenWidth];
+            final int destGreenPreShift = 8 - destGreenWidth;
+            final int destBlueShift = getChannelShift(destBlueMask);
+            final int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift);
+            final byte[] destBlues = ANY_TO_EIGHT[destBlueWidth];
+            final int destBluePreShift = 8 - destBlueWidth;
+            final int destAlphaShift = getChannelShift(destAlphaMask);
+            final int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift);
+            final byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth];
+            final int destAlphaPreShift = 8 - destAlphaWidth;
+
+            int dp = dpr;
+            int sp = spr;
+            int ap = apr, alpha = alphaMode;
+            int r = 0, g = 0, b = 0, a = 0, index = 0;
+            int rq = 0, gq = 0, bq = 0, aq = 0;
+            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+                     sp = spr += (sfy >>> 16) * srcStride,
+                     ap = apr += (sfy >>> 16) * alphaStride,
+                     sfy = (sfy & 0xffff) + sfyi,
+                     dp = dpr += dpryi) {
+               for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                         dp += dprxi,
+                         sfx = (sfx & 0xffff) + sfxi) {
+                    /*** READ NEXT PIXEL ***/
+                    switch (stype) {
+                        case TYPE_INDEX_8:
+                            index = srcData[sp] & 0xff;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_4:
+                            if ((sp & 1) != 0) index = srcData[sp >> 1] & 0x0f;
+                            else index = (srcData[sp >> 1] >>> 4) & 0x0f;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_2:
+                            index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_1_MSB:
+                            index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_1_LSB:
+                            index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
+                            sp += (sfx >>> 16);
+                            break;
+                    }
+
+                    /*** DO SPECIAL PROCESSING IF REQUIRED ***/
+                    r = srcReds[index] & 0xff;
+                    g = srcGreens[index] & 0xff;
+                    b = srcBlues[index] & 0xff;
+                    switch (alphaMode) {
+                        case ALPHA_CHANNEL_SEPARATE:
+                            alpha = ((alphaData[ap] & 0xff) << 16) / 255;
+                            ap += (sfx >> 16);
+                            break;
+                        case ALPHA_MASK_UNPACKED:
+                            alpha = (alphaData[ap] != 0) ? 0x10000 : 0;
+                            ap += (sfx >> 16);
+                            break;                                             
+                        case ALPHA_MASK_PACKED:
+                            alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
+                            ap += (sfx >> 16);
+                            break;
+                        case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices
+                            int i = 0;
+                            while (i < alphaData.length) {
+                                if (index == (alphaData[i] & 0xff)) break;
+                            }
+                            if (i < alphaData.length) continue;
+                        } break;
+                        case ALPHA_MASK_RGB: {
+                            int i = 0;
+                            while (i < alphaData.length) {
+                                if ((r == (alphaData[i] & 0xff)) &&
+                                    (g == (alphaData[i + 1] & 0xff)) &&
+                                    (b == (alphaData[i + 2] & 0xff))) break;
+                                i += 3;
+                            }
+                            if (i < alphaData.length) continue;
+                        } break;
+                    }
+                    if (alpha != 0x10000) {
+                        if (alpha == 0x0000) continue;
+                        switch (dtype) {
+                            case TYPE_GENERIC_8: {
+                                final int data = destData[dp] & 0xff;
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_16_MSB: {
+                                final int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_16_LSB: {
+                                final int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_24: {
+                                final int data = (( ((destData[dp] & 0xff) << 8) |
+                                                    (destData[dp + 1] & 0xff)) << 8) |
+                                    (destData[dp + 2] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_32_MSB: {
+                                final int data = (( (( ((destData[dp] & 0xff) << 8) |
+                                                       (destData[dp + 1] & 0xff)) << 8) |
+                                                    (destData[dp + 2] & 0xff)) << 8) |
+                                    (destData[dp + 3] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                            case TYPE_GENERIC_32_LSB: {
+                                final int data = (( (( ((destData[dp + 3] & 0xff) << 8) |
+                                                       (destData[dp + 2] & 0xff)) << 8) |
+                                                    (destData[dp + 1] & 0xff)) << 8) |
+                                    (destData[dp] & 0xff);
+                                rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                                gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                                bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                                aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                            } break;
+                        }
+                        // Perform alpha blending
+                        a = aq + ((a - aq) * alpha >> 16);
+                        r = rq + ((r - rq) * alpha >> 16);
+                        g = gq + ((g - gq) * alpha >> 16);
+                        b = bq + ((b - bq) * alpha >> 16);
+                    }
+
+                    /*** WRITE NEXT PIXEL ***/
+                    final int data = 
+                        (r >>> destRedPreShift << destRedShift) |
+                        (g >>> destGreenPreShift << destGreenShift) |
+                        (b >>> destBluePreShift << destBlueShift) |
+                        (a >>> destAlphaPreShift << destAlphaShift);
+                    switch (dtype) {
+                        case TYPE_GENERIC_8: {
+                            destData[dp] = (byte) data;
+                        } break;
+                        case TYPE_GENERIC_16_MSB: {
+                            destData[dp] = (byte) (data >>> 8);
+                            destData[dp + 1] = (byte) (data & 0xff);
+                        } break;
+                        case TYPE_GENERIC_16_LSB: {
+                            destData[dp] = (byte) (data & 0xff);
+                            destData[dp + 1] = (byte) (data >>> 8);
+                        } break;
+                        case TYPE_GENERIC_24: {
+                            destData[dp] = (byte) (data >>> 16);
+                            destData[dp + 1] = (byte) (data >>> 8);
+                            destData[dp + 2] = (byte) (data & 0xff);
+                        } break;
+                        case TYPE_GENERIC_32_MSB: {
+                            destData[dp] = (byte) (data >>> 24);
+                            destData[dp + 1] = (byte) (data >>> 16);
+                            destData[dp + 2] = (byte) (data >>> 8);
+                            destData[dp + 3] = (byte) (data & 0xff);
+                        } break;
+                        case TYPE_GENERIC_32_LSB: {
+                            destData[dp] = (byte) (data & 0xff);
+                            destData[dp + 1] = (byte) (data >>> 8);
+                            destData[dp + 2] = (byte) (data >>> 16);
+                            destData[dp + 3] = (byte) (data >>> 24);
+                        } break;
+                    }
+               }
+            }                  
+        }
+
+        /**
+         * Blits a direct palette image into an index palette image.
+         * <p>
+         * Note: The source and destination masks and palettes must
+         * always be fully specified.
+         * </p>
+         * 
+         * @param op the blitter operation: a combination of BLIT_xxx flags
+         *        (see BLIT_xxx constants)
+         * @param srcData the source byte array containing image data
+         * @param srcDepth the source depth: one of 8, 16, 24, 32
+         * @param srcStride the source number of bytes per line
+         * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
+         *        ignored if srcDepth is not 16 or 32
+         * @param srcX the top-left x-coord of the source blit region
+         * @param srcY the top-left y-coord of the source blit region
+         * @param srcWidth the width of the source blit region
+         * @param srcHeight the height of the source blit region
+         * @param srcRedMask the source red channel mask
+         * @param srcGreenMask the source green channel mask
+         * @param srcBlueMask the source blue channel mask
+         * @param alphaMode the alpha blending or mask mode, may be
+         *        an integer 0-255 for global alpha; ignored if BLIT_ALPHA
+         *        not specified in the blitter operations
+         *        (see ALPHA_MODE_xxx constants)
+         * @param alphaData the alpha blending or mask data, varies depending
+         *        on the value of alphaMode and sometimes ignored
+         * @param alphaStride the alpha data number of bytes per line
+         * @param alphaX the top-left x-coord of the alpha blit region
+         * @param alphaY the top-left y-coord of the alpha blit region
+         * @param destData the destination byte array containing image data
+         * @param destDepth the destination depth: one of 1, 2, 4, 8
+         * @param destStride the destination number of bytes per line
+         * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
+         *        ignored if destDepth is not 1
+         * @param destX the top-left x-coord of the destination blit region
+         * @param destY the top-left y-coord of the destination blit region
+         * @param destWidth the width of the destination blit region
+         * @param destHeight the height of the destination blit region
+         * @param destReds the destination palette red component intensities
+         * @param destGreens the destination palette green component intensities
+         * @param destBlues the destination palette blue component intensities
+         * @param flipX if true the resulting image is flipped along the vertical axis
+         * @param flipY if true the resulting image is flipped along the horizontal axis
+         */
+        static void blit(int op,
+                         byte[] srcData, int srcDepth, int srcStride, int srcOrder,
+                         int srcX, int srcY, int srcWidth, int srcHeight,
+                         int srcRedMask, int srcGreenMask, int srcBlueMask,
+                         int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
+                         byte[] destData, int destDepth, int destStride, int destOrder,
+                         int destX, int destY, int destWidth, int destHeight,
+                         byte[] destReds, byte[] destGreens, byte[] destBlues,
+                         boolean flipX, boolean flipY) {
+            if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode == ALPHA_TRANSPARENT)) return;
+
+            // these should be supplied as params later
+            final int srcAlphaMask = 0;
+
+            /*** Prepare scaling data ***/
+            final int dwm1 = destWidth - 1;
+            final int sfxi = (dwm1 != 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
+            final int dhm1 = destHeight - 1;
+            final int sfyi = (dhm1 != 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
+
+            /*** Prepare source-related data ***/
+            final int sbpp, stype;
+            switch (srcDepth) {
+               case 8:
+                    sbpp = 1;
+                    stype = TYPE_GENERIC_8;
+                    break;
+               case 16:
+                    sbpp = 2;
+                    stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
+                    break;
+               case 24:
+                    sbpp = 3;
+                    stype = TYPE_GENERIC_24;
+                    break;
+               case 32:
+                    sbpp = 4;
+                    stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
+                    break;
+               default:
+                    //throw new IllegalArgumentException("Invalid source type");
+                    return;
+            }                  
+            int spr = srcY * srcStride + srcX * sbpp;
+
+            /*** Prepare destination-related data ***/
+            final int dtype;
+            switch (destDepth) {
+               case 8:
+                    dtype = TYPE_INDEX_8;
+                    break;
+               case 4:
+                    destStride <<= 1;
+                    dtype = TYPE_INDEX_4;
+                    break;
+               case 2:
+                    destStride <<= 2;
+                    dtype = TYPE_INDEX_2;
+                    break;
+               case 1:
+                    destStride <<= 3;
+                    dtype = (destOrder == MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
+                    break;
+               default:
+                    //throw new IllegalArgumentException("Invalid source type");
+                    return;
+            }                  
+            int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX);
+            final int dprxi = (flipX) ? -1 : 1;
+            final int dpryi = (flipY) ? -destStride : destStride;
+
+            /*** Prepare special processing data ***/
+            int apr;
+            if ((op & BLIT_ALPHA) != 0) {
+               switch (alphaMode) {
+                    case ALPHA_MASK_UNPACKED:
+                    case ALPHA_CHANNEL_SEPARATE:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        apr = alphaY * alphaStride + alphaX;
+                        break;
+                    case ALPHA_MASK_PACKED:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        alphaStride <<= 3;
+                        apr = alphaY * alphaStride + alphaX;
+                        break;
+                    case ALPHA_MASK_INDEX:
+                        //throw new IllegalArgumentException("Invalid alpha type");
+                        return;
+                    case ALPHA_MASK_RGB:
+                        if (alphaData == null) alphaMode = 0x10000;
+                        apr = 0;
+                        break;
+                    default:
+                        alphaMode = (alphaMode << 16) / 255; // prescale
+                    case ALPHA_CHANNEL_SOURCE:
+                        apr = 0;
+                        break;
+               }
+            } else {
+               alphaMode = 0x10000;
+               apr = 0;
+            }
+            final boolean ditherEnabled = (op & BLIT_DITHER) != 0;
+
+            /*** Comprehensive blit (apply transformations) ***/
+            final int srcRedShift = getChannelShift(srcRedMask);
+            final byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)];
+            final int srcGreenShift = getChannelShift(srcGreenMask);
+            final byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)];
+            final int srcBlueShift = getChannelShift(srcBlueMask);
+            final byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)];
+            final int srcAlphaShift = getChannelShift(srcAlphaMask);
+            final byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)];
+
+            int dp = dpr;
+            int sp = spr;
+            int ap = apr, alpha = alphaMode;
+            int r = 0, g = 0, b = 0, a = 0;
+            int indexq = 0;
+            int lastindex = 0, lastr = -1, lastg = -1, lastb = -1;
+            final int[] rerr, gerr, berr;
+            int destPaletteSize = 1 << destDepth;
+            if ((destReds != null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length;
+            if (ditherEnabled) {
+               rerr = new int[destWidth + 2];
+               gerr = new int[destWidth + 2];
+               berr = new int[destWidth + 2];
+            } else {
+               rerr = null; gerr = null; berr = null;
+            }
+            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+                     sp = spr += (sfy >>> 16) * srcStride,
+                     ap = apr += (sfy >>> 16) * alphaStride,
+                     sfy = (sfy & 0xffff) + sfyi,
+                     dp = dpr += dpryi) {
+               int lrerr = 0, lgerr = 0, lberr = 0;
+               for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                         dp += dprxi,
+                         sfx = (sfx & 0xffff) + sfxi) {
+                    /*** READ NEXT PIXEL ***/
+                    switch (stype) {
+                        case TYPE_GENERIC_8: {
+                            final int data = srcData[sp] & 0xff;
+                            sp += (sfx >>> 16);
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_16_MSB: {
+                            final int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff);
+                            sp += (sfx >>> 16) * 2;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_16_LSB: {
+                            final int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff);
+                            sp += (sfx >>> 16) * 2;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_24: {
+                            final int data = (( ((srcData[sp] & 0xff) << 8) |
+                                               (srcData[sp + 1] & 0xff)) << 8) |
+                                (srcData[sp + 2] & 0xff);
+                            sp += (sfx >>> 16) * 3;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_32_MSB: {
+                            final int data = (( (( ((srcData[sp] & 0xff) << 8) |
+                                                   (srcData[sp + 1] & 0xff)) << 8) |
+                                               (srcData[sp + 2] & 0xff)) << 8) |
+                                (srcData[sp + 3] & 0xff);
+                            sp += (sfx >>> 16) * 4;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                        case TYPE_GENERIC_32_LSB: {
+                            final int data = (( (( ((srcData[sp + 3] & 0xff) << 8) |
+                                                   (srcData[sp + 2] & 0xff)) << 8) |
+                                               (srcData[sp + 1] & 0xff)) << 8) |
+                                (srcData[sp] & 0xff);
+                            sp += (sfx >>> 16) * 4;
+                            r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                            g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                            b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                            a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                        } break;
+                    }
+
+                    /*** DO SPECIAL PROCESSING IF REQUIRED ***/
+                    switch (alphaMode) {
+                        case ALPHA_CHANNEL_SEPARATE:
+                            alpha = ((alphaData[ap] & 0xff) << 16) / 255;
+                            ap += (sfx >> 16);
+                            break;
+                        case ALPHA_CHANNEL_SOURCE:
+                            alpha = (a << 16) / 255;
+                            break;
+                        case ALPHA_MASK_UNPACKED:
+                            alpha = (alphaData[ap] != 0) ? 0x10000 : 0;
+                            ap += (sfx >> 16);
+                            break;                                             
+                        case ALPHA_MASK_PACKED:
+                            alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
+                            ap += (sfx >> 16);
+                            break;
+                        case ALPHA_MASK_RGB:
+                            alpha = 0x10000;
+                            for (int i = 0; i < alphaData.length; i += 3) {
+                                if ((r == alphaData[i]) && (g == alphaData[i + 1]) && (b == alphaData[i + 2])) {
+                                    alpha = 0x0000;
+                                    break;
+                                }
+                            }
+                            break;
+                    }
+                    if (alpha != 0x10000) {
+                        if (alpha == 0x0000) continue;
+                        switch (dtype) {
+                            case TYPE_INDEX_8:
+                                indexq = destData[dp] & 0xff;
+                                break;
+                            case TYPE_INDEX_4:
+                                if ((dp & 1) != 0) indexq = destData[dp >> 1] & 0x0f;
+                                else indexq = (destData[dp >> 1] >>> 4) & 0x0f;
+                                break;
+                            case TYPE_INDEX_2:
+                                indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03;
+                                break;
+                            case TYPE_INDEX_1_MSB:
+                                indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01;
+                                break;
+                            case TYPE_INDEX_1_LSB:
+                                indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01;
+                                break;
+                        }
+                        // Perform alpha blending
+                        final int rq = destReds[indexq] & 0xff;
+                        final int gq = destGreens[indexq] & 0xff;
+                        final int bq = destBlues[indexq] & 0xff;
+                        r = rq + ((r - rq) * alpha >> 16);
+                        g = gq + ((g - gq) * alpha >> 16);
+                        b = bq + ((b - bq) * alpha >> 16);
+                    }
+
+                    /*** MAP COLOR TO THE PALETTE ***/
+                    if (ditherEnabled) {
+                        // Floyd-Steinberg error diffusion
+                        r += rerr[dx] >> 4;
+                        if (r < 0) r = 0; else if (r > 255) r = 255;
+                        g += gerr[dx] >> 4;
+                        if (g < 0) g = 0; else if (g > 255) g = 255;
+                        b += berr[dx] >> 4;
+                        if (b < 0) b = 0; else if (b > 255) b = 255;
+                        rerr[dx] = lrerr;
+                        gerr[dx] = lgerr;
+                        berr[dx] = lberr;
+                    }
+                    if (r != lastr || g != lastg || b != lastb) {
+                        // moving the variable declarations out seems to make the JDK JIT happier...
+                        for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) {
+                            dr = (destReds[j] & 0xff) - r;
+                            dg = (destGreens[j] & 0xff) - g;
+                            db = (destBlues[j] & 0xff) - b;
+                            distance = dr * dr + dg * dg + db * db;
+                            if (distance < minDistance) {
+                                lastindex = j;
+                                if (distance == 0) break;
+                                minDistance = distance;
+                            }
+                        }
+                        lastr = r; lastg = g; lastb = b;
+                    }
+                    if (ditherEnabled) {
+                        // Floyd-Steinberg error diffusion, cont'd...
+                        final int dxm1 = dx - 1, dxp1 = dx + 1;
+                        int acc;
+                        rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr;
+                        rerr[dx] += acc += lrerr + lrerr;
+                        rerr[dxm1] += acc + lrerr + lrerr;
+                        gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr;
+                        gerr[dx] += acc += lgerr + lgerr;
+                        gerr[dxm1] += acc + lgerr + lgerr;
+                        berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr;
+                        berr[dx] += acc += lberr + lberr;
+                        berr[dxm1] += acc + lberr + lberr;
+                    }
+
+                    /*** WRITE NEXT PIXEL ***/
+                    switch (dtype) {
+                        case TYPE_INDEX_8:
+                            destData[dp] = (byte) lastindex;
+                            break;
+                        case TYPE_INDEX_4:
+                            if ((dp & 1) != 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | lastindex);
+                            else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4));
+                            break;
+                        case TYPE_INDEX_2: {
+                            final int shift = 6 - (dp & 3) * 2;
+                            destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift));
+                        } break;                                       
+                        case TYPE_INDEX_1_MSB: {
+                            final int shift = 7 - (dp & 7);
+                            destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
+                        } break;
+                        case TYPE_INDEX_1_LSB: {
+                            final int shift = dp & 7;
+                            destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
+                        } break;                                       
+                    }
+               }
+            }
+        }
+
+        /**
+         * 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];
+        }
+
+        /**
+         * Creates an ImageData containing one band's worth of a gradient filled
+         * block.  If <code>vertical</code> is true, the band must be tiled
+         * horizontally to fill a region, otherwise it must be tiled vertically.
+         *
+         * @param width the width of the region to be filled
+         * @param height the height of the region to be filled
+         * @param vertical if true sweeps from top to bottom, else
+         *        sweeps from left to right
+         * @param fromRGB the color to start with
+         * @param toRGB the color to end with
+         * @param redBits the number of significant red bits, 0 for palette modes
+         * @param greenBits the number of significant green bits, 0 for palette modes
+         * @param blueBits the number of significant blue bits, 0 for palette modes
+         * @return the new ImageData
+         */
+        static ImageData createGradientBand(
+                                            int width, int height, boolean vertical,
+                                            RGB fromRGB, RGB toRGB,
+                                            int redBits, int greenBits, int blueBits) {
+            /* Gradients are drawn as tiled bands */
+            final int bandWidth, bandHeight, bitmapDepth;
+            final byte[] bitmapData;
+            final PaletteData paletteData;
+            /* Select an algorithm depending on the depth of the screen */
+            if (redBits != 0 && greenBits != 0 && blueBits != 0) {
+               paletteData = new PaletteData(0x0000ff00, 0x00ff0000, 0xff000000);
+               bitmapDepth = 32;
+               if (redBits >= 8 && greenBits >= 8 && blueBits >= 8) {
+                    /* Precise color */
+                    final int steps;
+                    if (vertical) {
+                        bandWidth = 1;
+                        bandHeight = height;
+                        steps = bandHeight > 1 ? bandHeight - 1 : 1;
+                    } else {
+                        bandWidth = width;
+                        bandHeight = 1;
+                        steps = bandWidth > 1 ? bandWidth - 1 : 1;
+                    }
+                    final int bytesPerLine = bandWidth * 4;
+                    bitmapData = new byte[bandHeight * bytesPerLine];
+                    buildPreciseGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine);
+                    buildPreciseGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine);
+                    buildPreciseGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine);
+               } else {
+                    /* Dithered color */
+                    final int steps;
+                    if (vertical) {
+                        bandWidth = (width < 8) ? width : 8;
+                        bandHeight = height;
+                        steps = bandHeight > 1 ? bandHeight - 1 : 1;
+                    } else {
+                        bandWidth = width;
+                        bandHeight = (height < 8) ? height : 8;
+                        steps = bandWidth > 1 ? bandWidth - 1 : 1;
+                    }
+                    final int bytesPerLine = bandWidth * 4;
+                    bitmapData = new byte[bandHeight * bytesPerLine];
+                    buildDitheredGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine, blueBits);
+                    buildDitheredGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine, greenBits);
+                    buildDitheredGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine, redBits);                        
+               }
+            } else {
+               /* Dithered two tone */
+               paletteData = new PaletteData(new RGB[] { fromRGB, toRGB });
+               bitmapDepth = 8;
+               final int blendi;
+               if (vertical) {
+                    bandWidth = (width < 8) ? width : 8;
+                    bandHeight = height;
+                    blendi = (bandHeight > 1) ? 0x1040000 / (bandHeight - 1) + 1 : 1;
+               } else {
+                    bandWidth = width;
+                    bandHeight = (height < 8) ? height : 8;
+                    blendi = (bandWidth > 1) ? 0x1040000 / (bandWidth - 1) + 1 : 1;
+               }
+               final int bytesPerLine = (bandWidth + 3) & -4;
+               bitmapData = new byte[bandHeight * bytesPerLine];
+               if (vertical) {
+                    for (int dy = 0, blend = 0, dp = 0; dy < bandHeight;
+                         ++dy, blend += blendi, dp += bytesPerLine) {
+                        for (int dx = 0; dx < bandWidth; ++dx) {
+                            bitmapData[dp + dx] = (blend + DITHER_MATRIX[dy & 7][dx]) <
+                                0x1000000 ? (byte)0 : (byte)1;
+                        }
+                    }          
+               } else {
+                    for (int dx = 0, blend = 0; dx < bandWidth; ++dx, blend += blendi) {
+                        for (int dy = 0, dptr = dx; dy < bandHeight; ++dy, dptr += bytesPerLine) {
+                            bitmapData[dptr] = (blend + DITHER_MATRIX[dy][dx & 7]) <
+                                0x1000000 ? (byte)0 : (byte)1;
+                        }
+                    }
+               }
+            }
+            return new ImageData(bandWidth, bandHeight, bitmapDepth, paletteData, 4, bitmapData);
+        }
+
+        /* 
+         * Fill in gradated values for a color channel
+         */
+        static final void buildPreciseGradientChannel(int from, int to, int steps,
+                                                      int bandWidth, int bandHeight, boolean vertical,
+                                                      byte[] bitmapData, int dp, int bytesPerLine) {
+            int val = from << 16;
+            final int inc = ((to << 16) - val) / steps + 1;
+            if (vertical) {
+               for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
+                    bitmapData[dp] = (byte)(val >>> 16);
+                    val += inc;
+               }
+            } else {
+               for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
+                    bitmapData[dp] = (byte)(val >>> 16);
+                    val += inc;
+               }
+            }          
+        }
+
+        /* 
+         * 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;
+               }
+            }
+        }
+
+        /**
+         * Renders a gradient onto a GC.
+         * <p>
+         * This is a GC helper.
+         * </p>
+         *
+         * @param gc the GC to render the gradient onto
+         * @param device the device the GC belongs to
+         * @param x the top-left x coordinate of the region to be filled
+         * @param y the top-left y coordinate of the region to be filled
+         * @param width the width of the region to be filled
+         * @param height the height of the region to be filled
+         * @param vertical if true sweeps from top to bottom, else
+         *        sweeps from left to right
+         * @param fromRGB the color to start with
+         * @param toRGB the color to end with
+         * @param redBits the number of significant red bits, 0 for palette modes
+         * @param greenBits the number of significant green bits, 0 for palette modes
+         * @param blueBits the number of significant blue bits, 0 for palette modes
+         */
+        /*
+          static void fillGradientRectangle(GC gc, Device device,
+          int x, int y, int width, int height, boolean vertical,
+          RGB fromRGB, RGB toRGB,
+          int redBits, int greenBits, int blueBits) {
+          // Create the bitmap and tile it
+          ImageData band = createGradientBand(width, height, vertical,
+          fromRGB, toRGB, redBits, greenBits, blueBits);
+          Image image = new Image(device, band);
+          if ((band.width == 1) || (band.height == 1)) {
+          gc.drawImage(image, 0, 0, band.width, band.height, x, y, width, height);
+          } else {
+          if (vertical) {
+          for (int dx = 0; dx < width; dx += band.width) {
+          int blitWidth = width - dx;
+          if (blitWidth > band.width) blitWidth = band.width;
+          gc.drawImage(image, 0, 0, blitWidth, band.height, dx + x, y, blitWidth, band.height);
+          }
+          } else {
+          for (int dy = 0; dy < height; dy += band.height) {
+          int blitHeight = height - dy;
+          if (blitHeight > band.height) blitHeight = band.height;
+          gc.drawImage(image, 0, 0, band.width, blitHeight, x, dy + y, band.width, blitHeight);
+          }
+          }
+          }
+          image.dispose();
+          }
+        */
+    }
+
+
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+    final 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);
+       }
+    }
+
+
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    final static class JPEGAppn extends JPEGVariableSizeSegment {
+
+       public JPEGAppn(byte[] reference) {
+            super(reference);
+       }
+       
+       public JPEGAppn(LEDataInputStream byteStream) {
+            super(byteStream);
+       }
+       
+       public boolean verify() {
+            int marker = getSegmentMarker();
+            return marker >= JPEGFileFormat.APP0 && marker <= JPEGFileFormat.APP15;
+       }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    final static class JPEGArithmeticConditioningTable extends JPEGVariableSizeSegment {
+
+       public JPEGArithmeticConditioningTable(LEDataInputStream byteStream) {
+            super(byteStream);
+       }
+       
+       public int signature() {
+            return JPEGFileFormat.DAC;
+       }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    final static class JPEGComment extends JPEGVariableSizeSegment {
+
+       public JPEGComment(byte[] reference) {
+            super(reference);
+       }
+       
+       public JPEGComment(LEDataInputStream byteStream) {
+            super(byteStream);
+       }
+       
+       public int signature() {
+            return JPEGFileFormat.COM;
+       }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    final static class JPEGEndOfImage extends JPEGFixedSizeSegment {
+
+       public JPEGEndOfImage() {
+            super();
+       }
+       
+       public JPEGEndOfImage(byte[] reference) {
+            super(reference);
+       }
+       
+       public int signature() {
+            return JPEGFileFormat.EOI;
+       }
+       
+       public int fixedSize() {
+            return 2;
+       }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2004 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+
+    final static class JPEGFileFormat extends FileFormat {
+       int restartInterval;
+       JPEGFrameHeader frameHeader;
+       int imageWidth, imageHeight;
+       int interleavedMcuCols, interleavedMcuRows;
+       int maxV, maxH;
+       boolean progressive;
+       int samplePrecision;
+       int nComponents;
+       int[][] frameComponents;
+       int[] componentIds;
+       byte[][] imageComponents;
+       int[] dataUnit;
+       int[][][] dataUnits;
+       int[] precedingDCs;
+       JPEGScanHeader scanHeader;
+       byte[] dataBuffer;
+       int currentBitCount;
+       int bufferCurrentPosition;
+       int restartsToGo;
+       int nextRestartNumber;
+       JPEGArithmeticConditioningTable arithmeticTables;
+       JPEGHuffmanTable[] acHuffmanTables;
+       JPEGHuffmanTable[] dcHuffmanTables;
+       int[][] quantizationTables;
+       int currentByte;
+       int decoderQFactor;
+       int encoderQFactor = 75;
+       int eobrun = 0;
+       /* JPEGConstants */
+       public static final int DCTSIZE = 8;
+       public static final int DCTSIZESQR = 64;
+       /* JPEGFixedPointConstants */
+       public static final int FIX_0_899976223 = 7373;
+       public static final int FIX_1_961570560 = 16069;
+       public static final int FIX_2_053119869 = 16819;
+       public static final int FIX_0_298631336 = 2446;
+       public static final int FIX_1_847759065 = 15137;
+       public static final int FIX_1_175875602 = 9633;
+       public static final int FIX_3_072711026 = 25172;
+       public static final int FIX_0_765366865 = 6270;
+       public static final int FIX_2_562915447 = 20995;
+       public static final int FIX_0_541196100 = 4433;
+       public static final int FIX_0_390180644 = 3196;
+       public static final int FIX_1_501321110 = 12299;
+       /* JPEGMarkerCodes */
+       public static final int APP0  = 0xFFE0;
+       public static final int APP15 = 0xFFEF;
+       public static final int COM   = 0xFFFE;
+       public static final int DAC   = 0xFFCC;
+       public static final int DHP   = 0xFFDE;
+       public static final int DHT   = 0xFFC4;
+       public static final int DNL   = 0xFFDC;
+       public static final int DRI   = 0xFFDD;
+       public static final int DQT   = 0xFFDB;
+       public static final int EOI   = 0xFFD9;
+       public static final int EXP   = 0xFFDF;
+       public static final int JPG   = 0xFFC8;
+       public static final int JPG0  = 0xFFF0;
+       public static final int JPG13 = 0xFFFD;
+       public static final int RST0  = 0xFFD0;
+       public static final int RST1  = 0xFFD1;
+       public static final int RST2  = 0xFFD2;
+       public static final int RST3  = 0xFFD3;
+       public static final int RST4  = 0xFFD4;
+       public static final int RST5  = 0xFFD5;
+       public static final int RST6  = 0xFFD6;
+       public static final int RST7  = 0xFFD7;
+       public static final int SOF0  = 0xFFC0;
+       public static final int SOF1  = 0xFFC1;
+       public static final int SOF2  = 0xFFC2;
+       public static final int SOF3  = 0xFFC3;
+       public static final int SOF5  = 0xFFC5;
+       public static final int SOF6  = 0xFFC6;
+       public static final int SOF7  = 0xFFC7;
+       public static final int SOF9  = 0xFFC9;
+       public static final int SOF10 = 0xFFCA;
+       public static final int SOF11 = 0xFFCB;
+       public static final int SOF13 = 0xFFCD;
+       public static final int SOF14 = 0xFFCE;
+       public static final int SOF15 = 0xFFCF;
+       public static final int SOI   = 0xFFD8;
+       public static final int SOS   = 0xFFDA;
+       public static final int TEM   = 0xFF01;
+       /* JPEGFrameComponentParameterConstants */
+       public static final int TQI     = 0;
+       public static final int HI      = 1;
+       public static final int VI      = 2;
+       public static final int CW      = 3;
+       public static final int CH      = 4;
+       /* JPEGScanComponentParameterConstants */
+       public static final int DC      = 0;
+       public static final int AC      = 1;
+       /* JFIF Component Constants */
+       public static final int ID_Y            = 1 - 1;
+       public static final int ID_CB   = 2 - 1;
+       public static final int ID_CR   = 3 - 1;
+
+       public static final int[] ExtendTest = {
+            0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 
+            4096, 8192, 16384, 32768, 65536, 131072, 262144
+       };
+       public static final int[] ExtendOffset = new int[] {
+            0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, 
+            -4095, -8191, -16383, -32767, -65535, -131071, -262143
+       };
+       public static final int[] ZigZag8x8 = {
+            0, 1, 8, 16, 9, 2, 3, 10,
+            17, 24, 32, 25, 18, 11, 4, 5,
+            12, 19, 26, 33, 40, 48, 41, 34,
+            27, 20, 13, 6, 7, 14, 21, 28,
+            35, 42, 49, 56, 57, 50, 43, 36,
+            29, 22, 15, 23, 30, 37, 44, 51,
+            58, 59, 52, 45, 38, 31, 39, 46,
+            53, 60, 61, 54, 47, 55, 62, 63
+       };
+       public static int[] CrRTable, CbBTable, CrGTable, CbGTable;
+       public static int[] RYTable, GYTable, BYTable,
+            RCbTable, GCbTable, BCbTable, RCrTable, GCrTable, BCrTable, NBitsTable;
+       static {
+            initialize();
+       }
+        void compress(ImageData image, byte[] dataYComp, byte[] dataCbComp, byte[] dataCrComp) {
+            int srcWidth = image.width;
+            int srcHeight = image.height;
+            int vhFactor = maxV * maxH;
+            int[] frameComponent;
+            imageComponents = new byte[nComponents][];
+            for (int i = 0; i < nComponents; i++) {
+               frameComponent = frameComponents[componentIds[i]];
+               imageComponents[i] = new byte[frameComponent[CW] * frameComponent[CH]];
+            }
+            frameComponent = frameComponents[componentIds[ID_Y]];
+            for (int yPos = 0; yPos < srcHeight; yPos++) {
+               int srcOfs = yPos * srcWidth;
+               int dstOfs = yPos * frameComponent[CW];
+               System.arraycopy(dataYComp, srcOfs, imageComponents[ID_Y], dstOfs, srcWidth);
+            }
+            frameComponent = frameComponents[componentIds[ID_CB]];
+            for (int yPos = 0; yPos < srcHeight / maxV; yPos++) {
+               int destRowIndex = yPos * frameComponent[CW];
+               for (int xPos = 0; xPos < srcWidth / maxH; xPos++) {
+                    int sum = 0;
+                    for (int iv = 0; iv < maxV; iv++) {
+                        int srcIndex = (yPos * maxV + iv) * srcWidth + (xPos * maxH);
+                        for (int ih = 0; ih < maxH; ih++) {
+                            sum += dataCbComp[srcIndex + ih] & 0xFF;
+                        }
+                    }
+                    imageComponents[ID_CB][destRowIndex + xPos] = (byte)(sum / vhFactor);
+               }
+            }
+            frameComponent = frameComponents[componentIds[ID_CR]];
+            for (int yPos = 0; yPos < srcHeight / maxV; yPos++) {
+               int destRowIndex = yPos * frameComponent[CW];
+               for (int xPos = 0; xPos < srcWidth / maxH; xPos++) {
+                    int sum = 0;
+                    for (int iv = 0; iv < maxV; iv++) {
+                        int srcIndex = (yPos * maxV + iv) * srcWidth + (xPos * maxH);
+                        for (int ih = 0; ih < maxH; ih++) {
+                            sum += dataCrComp[srcIndex + ih] & 0xFF;
+                        }
+                    }
+                    imageComponents[ID_CR][destRowIndex + xPos] = (byte)(sum / vhFactor);
+               }
+            }
+            for (int iComp = 0; iComp < nComponents; iComp++) {
+               byte[] imageComponent = imageComponents[iComp];
+               frameComponent = frameComponents[componentIds[iComp]];
+               int hFactor = frameComponent[HI];
+               int vFactor = frameComponent[VI];
+               int componentWidth = frameComponent[CW];
+               int componentHeight = frameComponent[CH];
+               int compressedWidth = srcWidth / (maxH / hFactor);
+               int compressedHeight = srcHeight / (maxV / vFactor);
+               if (compressedWidth < componentWidth) {
+                    int delta = componentWidth - compressedWidth;
+                    for (int yPos = 0; yPos < compressedHeight; yPos++) {
+                        int dstOfs = ((yPos + 1) * componentWidth - delta);
+                        int dataValue = imageComponent[dstOfs - 1] & 0xFF;
+                        for (int i = 0; i < delta; i++) {
+                            imageComponent[dstOfs + i] = (byte)dataValue;
+                        }
+                    }
+               }
+               if (compressedHeight < componentHeight) {
+                    int srcOfs = (compressedHeight - 1) * componentWidth;
+                    for (int yPos = compressedHeight; yPos <= componentHeight; yPos++) {
+                        int dstOfs = (yPos - 1) * componentWidth;
+                        System.arraycopy(imageComponent, srcOfs, imageComponent, dstOfs, componentWidth);
+                    }
+               }
+            }
+        }
+        void convert4BitRGBToYCbCr(ImageData image) {
+            RGB[] rgbs = image.getRGBs();
+            int paletteSize = rgbs.length;
+            byte[] yComp = new byte[paletteSize];
+            byte[] cbComp = new byte[paletteSize];
+            byte[] crComp = new byte[paletteSize];
+            int srcWidth = image.width;
+            int srcHeight = image.height;
+            for (int i = 0; i < paletteSize; i++) {
+               RGB color = rgbs[i];
+               int r = color.red;
+               int g = color.green;
+               int b = color.blue;
+               int n = RYTable[r] + GYTable[g] + BYTable[b];
+               yComp[i] = (byte)(n / 65536);
+               if ((n < 0) && (n % 65536 != 0)) yComp[i]--;
+               n = RCbTable[r] + GCbTable[g] + BCbTable[b];
+               cbComp[i] = (byte)(n / 65536);
+               if ((n < 0) && (n % 65536 != 0)) cbComp[i]--;
+               n = RCrTable[r] + GCrTable[g] + BCrTable[b];
+               crComp[i] = (byte)(n / 65536);
+               if ((n < 0) && (n % 65536 != 0)) crComp[i]--;
+            }
+            int bSize = srcWidth * srcHeight;
+            byte[] dataYComp = new byte[bSize];
+            byte[] dataCbComp = new byte[bSize];
+            byte[] dataCrComp = new byte[bSize];
+            byte[] origData = image.data;
+            for (int yPos = 0; yPos < srcHeight; yPos++) {
+               for (int xPos = 0; xPos < srcWidth / 2; xPos++) {
+                    int srcIndex = yPos * (srcWidth / 2) + xPos;
+                    int dstIndex = yPos * srcWidth + (xPos * 2);
+                    int value2 = origData[srcIndex] & 0xFF;
+                    int value1 = value2 / 16;
+                    value2 = value2 % 16;
+                    dataYComp[dstIndex] = yComp[value1];
+                    dataCbComp[dstIndex] = cbComp[value1];
+                    dataCrComp[dstIndex] = crComp[value1];
+                    dataYComp[dstIndex + 1] = yComp[value2];
+                    dataCbComp[dstIndex + 1] = cbComp[value2];
+                    dataCrComp[dstIndex + 1] = crComp[value2];
+               }
+            }
+            compress(image, dataYComp, dataCbComp, dataCrComp);
+        }
+        void convert8BitRGBToYCbCr(ImageData image) {
+            RGB[] rgbs = image.getRGBs();
+            int paletteSize = rgbs.length;
+            byte[] yComp = new byte[paletteSize];
+            byte[] cbComp = new byte[paletteSize];
+            byte[] crComp = new byte[paletteSize];
+            int srcWidth = image.width;
+            int srcHeight = image.height;
+            for (int i = 0; i < paletteSize; i++) {
+               RGB color = rgbs[i];
+               int r = color.red;
+               int g = color.green;
+               int b = color.blue;
+               int n = RYTable[r] + GYTable[g] + BYTable[b];
+               yComp[i] = (byte)(n / 65536);
+               if ((n < 0) && (n % 65536 != 0)) yComp[i]--;
+               n = RCbTable[r] + GCbTable[g] + BCbTable[b];
+               cbComp[i] = (byte)(n / 65536);
+               if ((n < 0) && (n % 65536 != 0)) cbComp[i]--;
+               n = RCrTable[r] + GCrTable[g] + BCrTable[b];
+               crComp[i] = (byte)(n / 65536);
+               if ((n < 0) && (n % 65536 != 0)) crComp[i]--;
+            }
+            int dstWidth = image.width;
+            int dstHeight = srcHeight;
+            int stride = (srcWidth + 3) / 4 * 4;
+            int bSize = dstWidth * dstHeight;
+            byte[] dataYComp = new byte[bSize];
+            byte[] dataCbComp = new byte[bSize];
+            byte[] dataCrComp = new byte[bSize];
+            byte[] origData = image.data;
+            for (int yPos = 0; yPos < srcHeight; yPos++) {
+               int srcRowIndex = yPos * stride;
+               int dstRowIndex = yPos * dstWidth;
+               for (int xPos = 0; xPos < srcWidth; xPos++) {
+                    int value = origData[srcRowIndex + xPos] & 0xFF;
+                    int dstIndex = dstRowIndex + xPos;
+                    dataYComp[dstIndex] = yComp[value];
+                    dataCbComp[dstIndex] = cbComp[value];
+                    dataCrComp[dstIndex] = crComp[value];
+               }
+            }
+            compress(image, dataYComp, dataCbComp, dataCrComp);
+        }
+        byte[] convertCMYKToRGB() {
+            /* Unsupported CMYK format. Answer an empty byte array. */
+            return new byte[0];
+        }
+        void convertImageToYCbCr(ImageData image) {
+            switch (image.depth) {
+               case 4:
+                    convert4BitRGBToYCbCr(image);
+                    return;
+               case 8:
+                    convert8BitRGBToYCbCr(image);
+                    return;
+               case 16:
+               case 24:
+               case 32:
+                    convertMultiRGBToYCbCr(image);
+                    return;
+               default:
+                    SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+            }
+            return;
+        }
+        void convertMultiRGBToYCbCr(ImageData image) {
+            int srcWidth = image.width;
+            int srcHeight = image.height;
+            int bSize = srcWidth * srcHeight;
+            byte[] dataYComp = new byte[bSize];
+            byte[] dataCbComp = new byte[bSize];
+            byte[] dataCrComp = new byte[bSize];
+            PaletteData palette = image.palette;
+            int[] buffer = new int[srcWidth];
+            if (palette.isDirect) {
+               int redMask = palette.redMask;
+               int greenMask = palette.greenMask;
+               int blueMask = palette.blueMask;
+               int redShift = palette.redShift;
+               int greenShift = palette.greenShift;
+               int blueShift = palette.blueShift;
+               for (int yPos = 0; yPos < srcHeight; yPos++) {
+                    image.getPixels(0, yPos, srcWidth, buffer, 0);
+                    int dstRowIndex = yPos * srcWidth;
+                    for (int xPos = 0; xPos < srcWidth; xPos++) {
+                        int pixel = buffer[xPos];
+                        int dstDataIndex = dstRowIndex + xPos;
+                        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;                               
+                        dataYComp[dstDataIndex] = (byte)((RYTable[r] + GYTable[g] + BYTable[b]) / 65536);
+                        dataCbComp[dstDataIndex] = (byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) / 65536);
+                        dataCrComp[dstDataIndex] = (byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) / 65536);
+                    }
+               }
+            } else {
+               for (int yPos = 0; yPos < srcHeight; yPos++) {
+                    image.getPixels(0, yPos, srcWidth, buffer, 0);
+                    int dstRowIndex = yPos * srcWidth;
+                    for (int xPos = 0; xPos < srcWidth; xPos++) {
+                        int pixel = buffer[xPos];
+                        int dstDataIndex = dstRowIndex + xPos;
+                        RGB rgb = palette.getRGB(pixel);
+                        int r = rgb.red;
+                        int g = rgb.green;
+                        int b = rgb.blue;
+                        dataYComp[dstDataIndex] = (byte)((RYTable[r] + GYTable[g] + BYTable[b]) / 65536);
+                        dataCbComp[dstDataIndex] = (byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) / 65536);
+                        dataCrComp[dstDataIndex] = (byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) / 65536);
+                    }
+               }
+            }
+            compress(image, dataYComp, dataCbComp, dataCrComp);
+        }
+        byte[] convertYToRGB() {
+            int compWidth = frameComponents[componentIds[ID_Y]][CW];
+            int bytesPerLine = (((imageWidth * 8 + 7) / 8) + 3) / 4 * 4;
+            byte[] data = new byte[bytesPerLine * imageHeight];
+            byte[] yComp = imageComponents[ID_Y];
+            int destIndex = 0;
+            for (int i = 0; i < imageHeight; i++) {
+               int srcIndex = i * compWidth;
+               for (int j = 0; j < bytesPerLine; j++) {
+                    int y = yComp[srcIndex] & 0xFF;
+                    if (y < 0) {
+                        y = 0;
+                    } else {
+                        if (y > 255) y = 255;
+                    }
+                    if (j >= imageWidth) {
+                        y = 0;
+                    }
+                    data[destIndex] = (byte)y;
+                    srcIndex++;
+                    destIndex++;
+               }
+            }
+            return data;
+        }
+        byte[] convertYCbCrToRGB() {
+            /**
+             * Convert existing image components into an RGB format.
+             * YCbCr is defined per CCIR 601-1, except that Cb and Cr are
+             * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
+             * The conversion equations to be implemented are therefore
+             *         R = Y                + 1.40200 * Cr
+             *         G = Y - 0.34414 * Cb - 0.71414 * Cr
+             *         B = Y + 1.77200 * Cb
+             * where Cb and Cr represent the incoming values less MAXJSAMPLE/2.
+             * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
+             * 
+             * To avoid floating-point arithmetic, we represent the fractional constants
+             * as integers scaled up by 2^16 (about 4 digits precision); we have to divide
+             * the products by 2^16, with appropriate rounding, to get the correct answer.
+             * Notice that Y, being an integral input, does not contribute any fraction
+             * so it need not participate in the rounding.
+             * 
+             * For even more speed, we avoid doing any multiplications in the inner loop
+             * by precalculating the constants times Cb and Cr for all possible values.
+             * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table);
+             * for 12-bit samples it is still acceptable.  It's not very reasonable for
+             * 16-bit samples, but if you want lossless storage you shouldn't be changing
+             * colorspace anyway.
+             * The Cr=>R and Cb=>B values can be rounded to integers in advance; the
+             * values for the G calculation are left scaled up, since we must add them
+             * together before rounding.
+             */
+            int bSize = imageWidth * imageHeight * nComponents;
+            byte[] rgbData = new byte[bSize];
+            int destIndex = 0;
+            expandImageComponents();
+            byte[] yComp = imageComponents[ID_Y];
+            byte[] cbComp = imageComponents[ID_CB];
+            byte[] crComp = imageComponents[ID_CR];
+            int compWidth = frameComponents[componentIds[ID_Y]][CW];
+            for (int v = 0; v < imageHeight; v++) {
+               int srcIndex = v * compWidth;
+               for (int i = 0; i < imageWidth; i++) {
+                    int y = yComp[srcIndex] & 0xFF;
+                    int cb = cbComp[srcIndex] & 0xFF;
+                    int cr = crComp[srcIndex] & 0xFF;
+                    int r = y + CrRTable[cr];
+                    int g = y + ((CbGTable[cb] + CrGTable[cr]) / 65536);
+                    int b = y + CbBTable[cb];
+                    if (r < 0) {
+                        r = 0;
+                    } else {
+                        if (r > 255) r = 255;
+                    }
+                    if (g < 0) {
+                        g = 0;
+                    } else {
+                        if (g > 255) g = 255;
+                    }
+                    if (b < 0) {
+                        b = 0;
+                    } else {
+                        if (b > 255) b = 255;
+                    }
+                    rgbData[destIndex] = (byte)b;
+                    rgbData[destIndex + 1] = (byte)g;
+                    rgbData[destIndex + 2] = (byte)r;
+                    destIndex += 3;
+                    srcIndex++;
+               }
+            }
+            return rgbData;
+        }
+        byte[] convertYIQToRGB() {
+            /* Unsupported CMYK format. Answer an empty byte array. */
+            return new byte[0];
+        }
+        void decodeACCoefficients(int[] dataUnit, int iComp) {
+            int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+            JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+            int k = 1;
+            while (k < 64) {
+               int rs = decodeUsingTable(acTable);
+               int r = rs >> 4;
+               int s = rs & 0xF;
+               if (s == 0) {
+                    if (r == 15) {
+                        k += 16;
+                    } else {
+                        break;
+                    }
+               } else {
+                    k += r;
+                    int bits = receive(s);
+                    dataUnit[ZigZag8x8[k]] = extendBy(bits, s);
+                    k++;
+               }
+            }
+        }
+        void decodeACFirstCoefficients(int[] dataUnit, int iComp, int start, int end, int approxBit) {
+            if (eobrun > 0) {
+               eobrun--;
+               return;
+            }
+            int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+            JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+            int k = start;
+            while (k <= end) {
+               int rs = decodeUsingTable(acTable);
+               int r = rs >> 4;
+               int s = rs & 0xF;
+               if (s == 0) {
+                    if (r == 15) {
+                        k += 16;
+                    } else {
+                        eobrun = (1 << r) + receive(r) - 1;
+                        break;
+                    }
+               } else {
+                    k += r;
+                    int bits = receive(s);
+                    dataUnit[ZigZag8x8[k]] = extendBy(bits, s) << approxBit;
+                    k++;
+               }
+            }
+        }
+        void decodeACRefineCoefficients(int[] dataUnit, int iComp, int start, int end, int approxBit) {
+            int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+            JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+            int k = start;
+            while (k <= end) {
+               if (eobrun > 0) {
+                    while (k <= end) {
+                        int zzIndex = ZigZag8x8[k];
+                        if (dataUnit[zzIndex] != 0) {
+                            dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
+                        }
+                        k++;
+                    }
+                    eobrun--;
+               } else {
+                    int rs = decodeUsingTable(acTable);
+                    int r = rs >> 4;
+                    int s = rs & 0xF;
+                    if (s == 0) {
+                        if (r == 15) {
+                            int zeros = 0;
+                            while (zeros < 16 && k <= end) {
+                                int zzIndex = ZigZag8x8[k];
+                                if (dataUnit[zzIndex] != 0) {
+                                    dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
+                                } else {
+                                    zeros++;
+                                }
+                                k++;
+                            }
+                        } else {
+                            eobrun = (1 << r) + receive(r);
+                        }
+                    } else {
+                        int bit = receive(s);
+                        int zeros = 0;
+                        int zzIndex = ZigZag8x8[k];
+                        while ((zeros < r || dataUnit[zzIndex] != 0) && k <= end) {
+                            if (dataUnit[zzIndex] != 0) {
+                                dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
+                            } else {
+                                zeros++;
+                            }
+                            k++;
+                            zzIndex = ZigZag8x8[k];
+                        }
+                        if (bit != 0) {
+                            dataUnit[zzIndex] = 1 << approxBit;
+                        } else {
+                            dataUnit[zzIndex] = -1 << approxBit;
+                        }
+                        k++;
+                    }
+               }
+            }
+        }
+        int refineAC(int ac, int approxBit) {
+            if (ac > 0) {
+               int bit = nextBit();
+               if (bit != 0) {
+                    ac = ac + (1 << approxBit);
+               }
+            } else if (ac < 0) {
+               int bit = nextBit();
+               if (bit != 0) {
+                    ac = ac + (-1 << approxBit);
+               }
+            }
+            return ac;
+        }
+        void decodeDCCoefficient(int[] dataUnit, int iComp, boolean first, int approxBit) {
+            int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+            JPEGHuffmanTable dcTable = dcHuffmanTables[sParams[DC]];
+            int lastDC = 0;
+            if (progressive && !first) {
+               int bit = nextBit();
+               lastDC = dataUnit[0] + (bit << approxBit);
+            } else {
+               lastDC = precedingDCs[iComp];
+               int nBits = decodeUsingTable(dcTable);
+               if (nBits != 0) {
+                    int bits = receive(nBits);
+                    int diff = extendBy(bits, nBits);
+                    lastDC = lastDC + diff;
+                    precedingDCs[iComp] = lastDC;
+               }
+               if (progressive) {
+                    lastDC = lastDC << approxBit;
+               }
+            }
+            dataUnit[0] = lastDC;
+        }
+        void dequantize(int[] dataUnit, int iComp) {
+            int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
+            for (int i = 0; i < dataUnit.length; i++) {
+               int zzIndex = ZigZag8x8[i];
+               dataUnit[zzIndex] = dataUnit[zzIndex] * qTable[i];
+            }
+        }
+        byte[] decodeImageComponents() {
+            int[] compIds = new int[nComponents];
+            int compIdsIndex = 0;
+            for (int i = 0; i < nComponents; i++) {
+               compIds[compIdsIndex] = i + 1;
+               compIdsIndex++;
+            }
+            if ((compIds.length == 3) &&
+               (compIds[0] == 1) &&
+               (compIds[1] == 2) &&
+               (compIds[2] == 3)) {
+                return convertYCbCrToRGB();
+            }
+            if ((compIds.length == 3) &&
+               (compIds[0] == 1) &&
+               (compIds[1] == 4) &&
+               (compIds[2] == 5)) {
+                return convertYIQToRGB();
+            }
+            if (compIds.length == 4) {
+               return convertCMYKToRGB();
+            }
+            return convertYToRGB();
+        }
+        void decodeMCUAtXAndY(int xmcu, int ymcu, int nComponentsInScan, boolean first, int start, int end, int approxBit) {
+            for (int iComp = 0; iComp < nComponentsInScan; iComp++) {
+               int scanComponent = iComp;
+               while (scanHeader.componentParameters[componentIds[scanComponent]] == null) {
+                    scanComponent++;
+               }
+               int[] frameComponent = frameComponents[componentIds[scanComponent]];
+               int hi = frameComponent[HI];
+               int vi = frameComponent[VI];
+               if (nComponentsInScan == 1) {
+                    hi = 1;
+                    vi = 1;
+               }
+               int compWidth = frameComponent[CW];
+               for (int ivi = 0; ivi < vi; ivi++) {
+                    for (int ihi = 0; ihi < hi; ihi++) {
+                        if (progressive) {
+                            // Progressive: First scan - create a new data unit.
+                            // Subsequent scans - refine the existing data unit.
+                            int index = (ymcu * vi + ivi) * compWidth + xmcu * hi + ihi;
+                            dataUnit = dataUnits[scanComponent][index];
+                            if (dataUnit == null) {
+                                dataUnit = new int[64];
+                                dataUnits[scanComponent][index] = dataUnit;
+                            }
+                        } else {
+                            // Sequential: Clear and reuse the data unit buffer.
+                            for (int i = 0; i < dataUnit.length; i++) {
+                                dataUnit[i] = 0;
+                            }
+                        }
+                        if (!progressive || scanHeader.isDCProgressiveScan()) {
+                            decodeDCCoefficient(dataUnit, scanComponent, first, approxBit);
+                        }
+                        if (!progressive) {
+                            decodeACCoefficients(dataUnit, scanComponent);
+                        } else {
+                            if (scanHeader.isACProgressiveScan()) {
+                                if (first) {
+                                    decodeACFirstCoefficients(dataUnit, scanComponent, start, end, approxBit);
+                                } else {
+                                    decodeACRefineCoefficients(dataUnit, scanComponent, start, end, approxBit);
+                                }
+                            }
+                            if (loader.hasListeners()) {
+                                // Dequantization, IDCT, up-sampling and color conversion
+                                // are done on a copy of the coefficient data in order to
+                                // display the image incrementally.
+                                int[] temp = dataUnit;
+                                dataUnit = new int[64];
+                                System.arraycopy(temp, 0, dataUnit, 0, 64);
+                            }
+                        }
+                        if (!progressive || (progressive && loader.hasListeners())) {
+                            dequantize(dataUnit, scanComponent);
+                            inverseDCT(dataUnit);
+                            storeData(dataUnit, scanComponent, xmcu, ymcu, hi, ihi, vi, ivi);
+                        }
+                    }
+               }
+            }
+        }
+        void decodeScan() {
+            if (progressive && !scanHeader.verifyProgressiveScan()) {
+               SWT.error(SWT.ERROR_INVALID_IMAGE);
+            }
+            int nComponentsInScan = scanHeader.getNumberOfImageComponents();
+            int mcuRowsInScan = interleavedMcuRows;
+            int mcusPerRow = interleavedMcuCols;
+            if (nComponentsInScan == 1) {
+               // Non-interleaved.
+               int scanComponent = 0;
+               while (scanHeader.componentParameters[componentIds[scanComponent]] == null) {
+                    scanComponent++;
+               }
+               int[] frameComponent = frameComponents[componentIds[scanComponent]];
+               int hi = frameComponent[HI];
+               int vi = frameComponent[VI];
+               int mcuWidth = DCTSIZE * maxH / hi;
+               int mcuHeight = DCTSIZE * maxV / vi;
+               mcusPerRow = (imageWidth + mcuWidth - 1) / mcuWidth;
+               mcuRowsInScan = (imageHeight + mcuHeight - 1) / mcuHeight;
+            }
+            boolean first = scanHeader.isFirstScan();
+            int start = scanHeader.getStartOfSpectralSelection();
+            int end = scanHeader.getEndOfSpectralSelection();
+            int approxBit = scanHeader.getApproxBitPositionLow();
+            restartsToGo = restartInterval;
+            nextRestartNumber = 0;
+            for (int ymcu = 0; ymcu < mcuRowsInScan; ymcu++) {
+               for (int xmcu = 0; xmcu < mcusPerRow; xmcu++) {
+                    if (restartInterval != 0) {
+                        if (restartsToGo == 0) processRestartInterval();
+                        restartsToGo--;
+                    }
+                    decodeMCUAtXAndY(xmcu, ymcu, nComponentsInScan, first, start, end, approxBit);
+               }
+            }
+        }
+        int decodeUsingTable(JPEGHuffmanTable huffmanTable) {
+            int i = 0;
+            int[] maxCodes = huffmanTable.getDhMaxCodes();
+            int[] minCodes = huffmanTable.getDhMinCodes();
+            int[] valPtrs = huffmanTable.getDhValPtrs();
+            int[] huffVals = huffmanTable.getDhValues();
+            int code = nextBit();
+            while (code > maxCodes[i]) {
+               code = code * 2 + nextBit();
+               i++;
+            }
+            int j = valPtrs[i];
+            j = j + code - minCodes[i];
+            return huffVals[j];
+        }
+        void emit(int huffCode, int nBits) {
+            if (nBits == 0) {
+               SWT.error(SWT.ERROR_INVALID_IMAGE);
+            }
+            int[] power2m1 = new int[] {
+               1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 
+               16383, 32767, 65535, 131125
+            };
+            int code = (huffCode & power2m1[nBits - 1]) << (24 - nBits - currentBitCount);
+            byte[] codeBuffer = new byte[4];
+            codeBuffer[0] = (byte)(code % 256);
+            codeBuffer[1] = (byte)((code / 256) % 256);
+            codeBuffer[2] = (byte)((code / 65536) % 256);
+            codeBuffer[3] = (byte)((code / 16777216) % 256);
+            int abs = nBits - (8 - currentBitCount);
+            if (abs < 0) abs = -abs;
+            if ((abs / 8) > 0) {
+               currentByte += codeBuffer[2];
+               emitByte((byte)currentByte);
+               emitByte((byte)codeBuffer[1]);
+               currentByte = codeBuffer[0];
+               currentBitCount += nBits - 16;
+            } else {
+               currentBitCount += nBits;
+               if (currentBitCount >= 8) {
+                    currentByte += codeBuffer[2];
+                    emitByte((byte)currentByte);
+                    currentByte = codeBuffer[1];
+                    currentBitCount -= 8;
+               } else {
+                    currentByte += codeBuffer[2];
+               }
+            }
+        }
+        void emitByte(byte byteValue) {
+            if (bufferCurrentPosition >= 512) {
+               resetOutputBuffer();
+            }
+            dataBuffer[bufferCurrentPosition] = byteValue;
+            bufferCurrentPosition++;
+            if (byteValue == -1) {
+               emitByte((byte)0);
+            }
+        }
+        void encodeACCoefficients(int[] dataUnit, int iComp) {
+            int[] sParams = scanHeader.componentParameters[iComp];
+            JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+            int[] ehCodes = acTable.ehCodes;
+            byte[] ehSizes = acTable.ehCodeLengths;
+            int r = 0;
+            int k = 1;
+            while (k < 64) {
+               k++;
+               int acValue = dataUnit[ZigZag8x8[k - 1]];
+               if (acValue == 0) {
+                    if (k == 64) {
+                        emit(ehCodes[0], ehSizes[0] & 0xFF);
+                    } else {
+                        r++;
+                    }
+               } else {
+                    while (r > 15) {
+                        emit(ehCodes[0xF0], ehSizes[0xF0] & 0xFF);
+                        r -= 16;
+                    }
+                    if (acValue < 0) {
+                        int absACValue = acValue;
+                        if (absACValue < 0) absACValue = -absACValue;
+                        int nBits = NBitsTable[absACValue];
+                        int rs = r * 16 + nBits;
+                        emit(ehCodes[rs], ehSizes[rs] & 0xFF);
+                        emit(0xFFFFFF - absACValue, nBits);
+                    } else {
+                        int nBits = NBitsTable[acValue];
+                        int rs = r * 16 + nBits;
+                        emit(ehCodes[rs], ehSizes[rs] & 0xFF);
+                        emit(acValue, nBits);
+                    }
+                    r = 0;
+               }
+            }
+        }
+        void encodeDCCoefficients(int[] dataUnit, int iComp) {
+            int[] sParams = scanHeader.componentParameters[iComp];
+            JPEGHuffmanTable dcTable = dcHuffmanTables[sParams[DC]];
+            int lastDC = precedingDCs[iComp];
+            int dcValue = dataUnit[0];
+            int diff = dcValue - lastDC;
+            precedingDCs[iComp] = dcValue;
+            if (diff < 0) {
+               int absDiff = 0 - diff;
+               int nBits = NBitsTable[absDiff];
+               emit(dcTable.ehCodes[nBits], dcTable.ehCodeLengths[nBits]);
+               emit(0xFFFFFF - absDiff, nBits);
+            } else {
+               int nBits = NBitsTable[diff];
+               emit(dcTable.ehCodes[nBits], dcTable.ehCodeLengths[nBits]);
+               if (nBits != 0) {
+                    emit(diff, nBits);
+               }
+            }
+        }
+        void encodeMCUAtXAndY(int xmcu, int ymcu) {
+            int nComponentsInScan = scanHeader.getNumberOfImageComponents();
+            dataUnit = new int[64];
+            for (int iComp = 0; iComp < nComponentsInScan; iComp++) {
+               int[] frameComponent = frameComponents[componentIds[iComp]];
+               int hi = frameComponent[HI];
+               int vi = frameComponent[VI];
+               for (int ivi = 0; ivi < vi; ivi++) {
+                    for (int ihi = 0; ihi < hi; ihi++) {
+                        extractData(dataUnit, iComp, xmcu, ymcu, ihi, ivi);
+                        forwardDCT(dataUnit);
+                        quantizeData(dataUnit, iComp);
+                        encodeDCCoefficients(dataUnit, iComp);
+                        encodeACCoefficients(dataUnit, iComp);
+                    }
+               }
+            }
+        }
+        void encodeScan() {
+            for (int ymcu = 0; ymcu < interleavedMcuRows; ymcu++) {
+               for (int xmcu = 0; xmcu < interleavedMcuCols; xmcu++) {
+                    encodeMCUAtXAndY(xmcu, ymcu);
+               }
+            }
+            if (currentBitCount != 0) {
+               emitByte((byte)currentByte);
+            }
+            resetOutputBuffer();
+        }
+        void expandImageComponents() {
+            for (int iComp = 0; iComp < nComponents; iComp++) {
+               int[] frameComponent = frameComponents[componentIds[iComp]];
+               int hi = frameComponent[HI];
+               int vi = frameComponent[VI];
+               int upH = maxH / hi;
+               int upV = maxV / vi;
+               if ((upH * upV) > 1) {
+                    byte[] component = imageComponents[iComp];
+                    int compWidth = frameComponent[CW];
+                    int compHeight = frameComponent[CH];
+                    int upCompWidth = compWidth * upH;
+                    int upCompHeight = compHeight * upV;
+                    RGB[] rgbs = new RGB[] {
+                        new RGB(0,0,0),
+                        new RGB(0x80,0,0),
+                        new RGB(0,0x80,0),
+                        new RGB(0x80,0x80,0),
+                        new RGB(0,0,0x80),
+                        new RGB(0x80,0,0x80),
+                        new RGB(0,0x80,0x80),
+                        new RGB(0xC0,0xC0,0xC0),
+                        new RGB(0x80,0x80,0x80),
+                        new RGB(0xFF,0,0),
+                        new RGB(0,0xFF,0),
+                        new RGB(0xFF,0xFF,0),
+                        new RGB(0,0,0xFF),
+                        new RGB(0xFF,0,0xFF),
+                        new RGB(0,0xFF,0xFF),
+                        new RGB(0xFF,0xFF,0xFF),
+                    };
+                    ImageData src = new ImageData(compWidth, compHeight, 8, new PaletteData(rgbs), 4, component);
+                    ImageData dest = src.scaledTo(upCompWidth, upCompHeight);
+                    imageComponents[iComp] = dest.data;
+               }
+            }
+        }
+        int extendBy(int diff, int t) {
+            if (diff < ExtendTest[t]) {
+               return diff + ExtendOffset[t];
+            } else {
+               return diff;
+            }
+        }
+        void extractData(int[] dataUnit, int iComp, int xmcu, int ymcu, int ihi, int ivi) {
+            byte[] compImage = imageComponents[iComp];
+            int[] frameComponent = frameComponents[componentIds[iComp]];
+            int hi = frameComponent[HI];
+            int vi = frameComponent[VI];
+            int compWidth = frameComponent[CW];
+            int srcIndex = ((ymcu * vi + ivi) * compWidth * DCTSIZE) + ((xmcu * hi + ihi) * DCTSIZE);
+            int destIndex = 0;
+            for (int i = 0; i < DCTSIZE; i++) {
+               for (int col = 0; col < DCTSIZE; col++) {
+                    dataUnit[destIndex] = (compImage[srcIndex + col] & 0xFF) - 128;
+                    destIndex++;
+               }
+               srcIndex += compWidth;
+            }
+        }
+        void forwardDCT(int[] dataUnit) {
+            for (int row = 0; row < 8; row++) {
+               int rIndex = row * DCTSIZE;
+               int tmp0 = dataUnit[rIndex] + dataUnit[rIndex + 7];
+               int tmp7 = dataUnit[rIndex] - dataUnit[rIndex + 7];
+               int tmp1 = dataUnit[rIndex + 1] + dataUnit[rIndex + 6];
+               int tmp6 = dataUnit[rIndex + 1] - dataUnit[rIndex + 6];
+               int tmp2 = dataUnit[rIndex + 2] + dataUnit[rIndex + 5];
+               int tmp5 = dataUnit[rIndex + 2] - dataUnit[rIndex + 5];
+               int tmp3 = dataUnit[rIndex + 3] + dataUnit[rIndex + 4];
+               int tmp4 = dataUnit[rIndex + 3] - dataUnit[rIndex + 4];
+
+               /**
+                * Even part per LL&M figure 1 --- note that published figure 
+                * is faulty; rotator 'sqrt(2)*c1' should be 'sqrt(2)*c6'.
+                */
+               int tmp10 = tmp0 + tmp3;
+               int tmp13 = tmp0 - tmp3;
+               int tmp11 = tmp1 + tmp2;
+               int tmp12 = tmp1 - tmp2;
+
+               dataUnit[rIndex] = (tmp10 + tmp11) * 4;
+               dataUnit[rIndex + 4]  = (tmp10 - tmp11) * 4;
+
+               int z1 = (tmp12 + tmp13) * FIX_0_541196100;
+               int scaleFactor1 = ExtendTest[11];
+               int scaleFactor2 = ExtendTest[12];
+               int n = z1 + (tmp13 * FIX_0_765366865) + scaleFactor1;
+               dataUnit[rIndex + 2] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 2]--;
+               n = z1 + (tmp12 * (0 - FIX_1_847759065)) + scaleFactor1;
+               dataUnit[rIndex + 6] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 6]--;
+
+               /**
+                * Odd part per figure 8 --- note paper omits factor of sqrt(2).
+                * cK represents cos(K*pi/16).
+                * i0..i3 in the paper are tmp4..tmp7 here.
+                */
+               z1 = tmp4 + tmp7;
+               int z2 = tmp5 + tmp6;
+               int z3 = tmp4 + tmp6;
+               int z4 = tmp5 + tmp7;
+               int z5 = (z3 + z4) * FIX_1_175875602;   // sqrt(2) * c3
+
+               tmp4 = tmp4 * FIX_0_298631336;  // sqrt(2) * (-c1+c3+c5-c7)
+               tmp5 = tmp5 * FIX_2_053119869;  // sqrt(2) * ( c1+c3-c5+c7)
+               tmp6 = tmp6 * FIX_3_072711026;  // sqrt(2) * ( c1+c3+c5-c7)
+               tmp7 = tmp7 * FIX_1_501321110;  // sqrt(2) * ( c1+c3-c5-c7)
+               z1 = z1 * (0 - FIX_0_899976223);        // sqrt(2) * (c7-c3)
+               z2 = z2 * (0 - FIX_2_562915447);        // sqrt(2) * (-c1-c3)
+               z3 = z3 * (0 - FIX_1_961570560);        // sqrt(2) * (-c3-c5)
+               z4 = z4 * (0 - FIX_0_390180644);        // sqrt(2) * (c5-c3)
+
+               z3 = z3 + z5;
+               z4 = z4 + z5;
+
+               n = tmp4 + z1 + z3 + scaleFactor1;
+               dataUnit[rIndex + 7] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 7]--;
+               n = tmp5 + z2 + z4 + scaleFactor1;
+               dataUnit[rIndex + 5] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 5]--;
+               n = tmp6 + z2 + z3 + scaleFactor1;
+               dataUnit[rIndex + 3] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 3]--;
+               n = tmp7 + z1 + z4 + scaleFactor1;
+               dataUnit[rIndex + 1] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 1]--;
+            }
+
+            /**
+             * Pass 2: process columns.
+             * Note that we must descale the results by a factor of 8 == 2**3,
+             * and also undo the PASS1_BITS scaling.
+             */
+            for (int col = 0; col < 8; col++) {
+               int c0 = col;
+               int c1 = col + 8;
+               int c2 = col + 16;
+               int c3 = col + 24;
+               int c4 = col + 32;
+               int c5 = col + 40;
+               int c6 = col + 48;
+               int c7 = col + 56;
+               int tmp0 = dataUnit[c0] + dataUnit[c7];
+               int tmp7 = dataUnit[c0] - dataUnit[c7];
+               int tmp1 = dataUnit[c1] + dataUnit[c6];
+               int tmp6 = dataUnit[c1] - dataUnit[c6];
+               int tmp2 = dataUnit[c2] + dataUnit[c5];
+               int tmp5 = dataUnit[c2] - dataUnit[c5];
+               int tmp3 = dataUnit[c3] + dataUnit[c4];
+               int tmp4 = dataUnit[c3] - dataUnit[c4];
+
+               /**
+                * Even part per LL&M figure 1 --- note that published figure 
+                * is faulty; rotator 'sqrt(2)*c1' should be 'sqrt(2)*c6'.
+                */
+               int tmp10 = tmp0 + tmp3;
+               int tmp13 = tmp0 - tmp3;
+               int tmp11 = tmp1 + tmp2;
+               int tmp12 = tmp1 - tmp2;
+
+               int scaleFactor1 = ExtendTest[5];
+               int scaleFactor2 = ExtendTest[6];
+               int n = tmp10 + tmp11 + scaleFactor1;
+               dataUnit[c0] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c0]--;
+               n = tmp10 - tmp11 + scaleFactor1;
+               dataUnit[c4] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c4]--;
+
+               int z1 = (tmp12 + tmp13) * FIX_0_541196100;
+               scaleFactor1 = ExtendTest[18];
+               scaleFactor2 = ExtendTest[19];
+               n = z1 + (tmp13 * FIX_0_765366865) + scaleFactor1;
+               dataUnit[c2] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c2]--;
+               n = z1 + (tmp12 * (0 - FIX_1_847759065)) + scaleFactor1;
+               dataUnit[c6] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c6]--;
+
+               /**
+                * Odd part per figure 8 --- note paper omits factor of sqrt(2).
+                * cK represents cos(K*pi/16).
+                * i0..i3 in the paper are tmp4..tmp7 here.
+                */
+               z1 = tmp4 + tmp7;
+               int z2 = tmp5 + tmp6;
+               int z3 = tmp4 + tmp6;
+               int z4 = tmp5 + tmp7;
+               int z5 = (z3 + z4) * FIX_1_175875602;   // sqrt(2) * c3
+
+               tmp4 = tmp4 * FIX_0_298631336;  // sqrt(2) * (-c1+c3+c5-c7)
+               tmp5 = tmp5 * FIX_2_053119869;  // sqrt(2) * ( c1+c3-c5+c7)
+               tmp6 = tmp6 * FIX_3_072711026;  // sqrt(2) * ( c1+c3+c5-c7)
+               tmp7 = tmp7 * FIX_1_501321110;  // sqrt(2) * ( c1+c3-c5-c7)
+               z1 = z1 * (0 - FIX_0_899976223);        // sqrt(2) * (c7-c3)
+               z2 = z2 * (0 - FIX_2_562915447);        // sqrt(2) * (-c1-c3)
+               z3 = z3 * (0 - FIX_1_961570560);        // sqrt(2) * (-c3-c5)
+               z4 = z4 * (0 - FIX_0_390180644);        // sqrt(2) * (c5-c3)
+
+               z3 = z3 + z5;
+               z4 = z4 + z5;
+
+               n = tmp4 + z1 + z3 + scaleFactor1;
+               dataUnit[c7] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c7]--;
+               n = tmp5 + z2 + z4 + scaleFactor1;
+               dataUnit[c5] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c5]--;
+               n = tmp6 + z2 + z3 + scaleFactor1;
+               dataUnit[c3] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c3]--;
+               n = tmp7 + z1 + z4 + scaleFactor1;
+               dataUnit[c1] = n / scaleFactor2;
+               if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c1]--;
+            }
+        }
+        void getAPP0() {
+            JPEGAppn appn = new JPEGAppn(inputStream);
+            if (!appn.verify()) {
+               SWT.error(SWT.ERROR_INVALID_IMAGE);
+            }
+        }
+        void getCOM() {
+            new JPEGComment(inputStream);
+        }
+        void getDAC() {
+            JPEGArithmeticConditioningTable dac = new JPEGArithmeticConditioningTable(inputStream);
+            arithmeticTables = dac;
+        }
+        void getDHT() {
+            JPEGHuffmanTable dht = new JPEGHuffmanTable(inputStream);
+            if (!dht.verify()) {
+               SWT.error(SWT.ERROR_INVALID_IMAGE);
+            }
+            if (acHuffmanTables == null) {
+               acHuffmanTables = new JPEGHuffmanTable[4];
+            }
+            if (dcHuffmanTables == null) {
+               dcHuffmanTables = new JPEGHuffmanTable[4];
+            }
+            JPEGHuffmanTable[] dhtTables = dht.getAllTables();
+            for (int i = 0; i < dhtTables.length; i++) {
+               JPEGHuffmanTable dhtTable = dhtTables[i];
+               if (dhtTable.getTableClass() == 0) {
+                    dcHuffmanTables[dhtTable.getTableIdentifier()] = dhtTable;
+               } else {
+                    acHuffmanTables[dhtTable.getTableIdentifier()] = dhtTable;
+               }
+            }
+        }
+        void getDNL() {
+            new JPEGRestartInterval(inputStream);
+        }
+        void getDQT() {
+            JPEGQuantizationTable dqt = new JPEGQuantizationTable(inputStream);
+            int[][] currentTables = quantizationTables;
+            if (currentTables == null) {
+               currentTables = new int[4][];
+            }
+            int[] dqtTablesKeys = dqt.getQuantizationTablesKeys();
+            int[][] dqtTablesValues = dqt.getQuantizationTablesValues();
+            for (int i = 0; i < dqtTablesKeys.length; i++) {
+               int index = dqtTablesKeys[i];
+               currentTables[index] = dqtTablesValues[i];
+            }
+            quantizationTables = currentTables;
+        }
+        void getDRI() {
+            JPEGRestartInterval dri = new JPEGRestartInterval(inputStream);
+            if (!dri.verify()) {
+               SWT.error(SWT.ERROR_INVALID_IMAGE);
+            }
+            restartInterval = dri.getRestartInterval();
+        }
+        static void initialize() {
+            initializeRGBYCbCrTables();
+            initializeYCbCrRGBTables();
+            initializeBitCountTable();
+        }
+        static void initializeBitCountTable() {
+            int nBits = 1;
+            int power2 = 2;
+            NBitsTable = new int[2048];
+            NBitsTable[0] = 0;
+            for (int i = 1; i < NBitsTable.length; i++) {
+               if (!(i < power2)) {
+                    nBits++;
+                    power2 *= 2;
+               }
+               NBitsTable[i] = nBits;
+            }
+        }
+        static void initializeRGBYCbCrTables() {
+            RYTable = new int[256];
+            GYTable = new int[256];
+            BYTable = new int[256];
+            RCbTable = new int[256];
+            GCbTable = new int[256];
+            BCbTable = new int[256];
+            RCrTable = BCbTable;
+            GCrTable = new int[256];
+            BCrTable = new int[256];
+            for (int i = 0; i < 256; i++) {
+               RYTable[i] = i * 19595;
+               GYTable[i] = i * 38470;
+               BYTable[i] = i * 7471 + 32768;
+               RCbTable[i] = i * -11059;
+               GCbTable[i] = i * -21709;
+               BCbTable[i] = i * 32768 + 8388608;
+               GCrTable[i] = i * -27439;
+               BCrTable[i] = i * -5329;
+            }
+        }
+        static void initializeYCbCrRGBTables() {
+            CrRTable = new int[256];
+            CbBTable = new int[256];
+            CrGTable = new int[256];
+            CbGTable = new int[256];
+            for (int i = 0; i < 256; i++) {
+               int x2 = 2 * i - 255;
+               CrRTable[i] = (45941 * x2 + 32768) / 65536;
+               CbBTable[i] = (58065 * x2 + 32768) / 65536;
+               CrGTable[i] = -23401 * x2;
+               CbGTable[i] = -11277 * x2 + 32768;
+            }
+        }
+        void inverseDCT(int[] dataUnit) {
+            for (int row = 0; row < 8; row++) {
+               int rIndex = row * DCTSIZE;
+               /**
+                * Due to quantization, we will usually find that many of the input
+                * coefficients are zero, especially the AC terms.  We can exploit this
+                * by short-circuiting the IDCT calculation for any row in which all
+                * the AC terms are zero.  In that case each output is equal to the
+                * DC coefficient (with scale factor as needed).
+                * With typical images and quantization tables, half or more of the
+                * row DCT calculations can be simplified this way.
+                */
+               if (isZeroInRow(dataUnit, rIndex)) {
+                    int dcVal = dataUnit[rIndex] * 4;
+                    for (int i = rIndex; i < rIndex + 8; i++) {
+                        dataUnit[i] = dcVal;
+                    }
+               } else {
+                    /**
+                     * Even part: reverse the even part of the forward DCT.
+                     * The rotator is sqrt(2)*c(-6).
+                     */
+                    int z2 = dataUnit[rIndex + 2];
+                    int z3 = dataUnit[rIndex + 6];
+                    int z1 = (z2 + z3) * FIX_0_541196100;
+                    int tmp2 = z1 + (z3 * (0 - FIX_1_847759065));
+                    int tmp3 = z1 + (z2 * FIX_0_765366865);
+                    int tmp0 = (dataUnit[rIndex] + dataUnit[rIndex + 4]) * 8192;
+                    int tmp1 = (dataUnit[rIndex] - dataUnit[rIndex + 4]) * 8192;
+                    int tmp10 = tmp0 + tmp3;
+                    int tmp13 = tmp0 - tmp3;
+                    int tmp11 = tmp1 + tmp2;
+                    int tmp12 = tmp1 - tmp2;
+                    /**
+                     * Odd part per figure 8; the matrix is unitary and hence its
+                     * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+                     */
+                    tmp0 = dataUnit[rIndex + 7];
+                    tmp1 = dataUnit[rIndex + 5];
+                    tmp2 = dataUnit[rIndex + 3];
+                    tmp3 = dataUnit[rIndex + 1];
+                    z1 = tmp0 + tmp3;
+                    z2 = tmp1 + tmp2;
+                    z3 = tmp0 + tmp2;
+                    int z4 = tmp1 + tmp3;
+                    int z5 = (z3 + z4)* FIX_1_175875602; /* sqrt(2) * c3 */
+                         
+                    tmp0 = tmp0 * FIX_0_298631336;             /* sqrt(2) * (-c1+c3+c5-c7) */
+                    tmp1 = tmp1 * FIX_2_053119869;             /* sqrt(2) * ( c1+c3-c5+c7) */
+                    tmp2 = tmp2 * FIX_3_072711026;             /* sqrt(2) * ( c1+c3+c5-c7) */
+                    tmp3 = tmp3 * FIX_1_501321110;             /* sqrt(2) * ( c1+c3-c5-c7) */
+                    z1 = z1 * (0 - FIX_0_899976223);   /* sqrt(2) * (c7-c3) */
+                    z2 = z2 * (0 - FIX_2_562915447);   /* sqrt(2) * (-c1-c3) */
+                    z3 = z3 * (0 - FIX_1_961570560);   /* sqrt(2) * (-c3-c5) */
+                    z4 = z4 * (0 - FIX_0_390180644);   /* sqrt(2) * (c5-c3) */
+
+                    z3 = z3 + z5;
+                    z4 = z4 + z5;
+                    tmp0 = tmp0 + z1 + z3;
+                    tmp1 = tmp1 + z2 + z4;
+                    tmp2 = tmp2 + z2 + z3;
+                    tmp3 = tmp3 + z1 + z4;
+
+                    int descaleFactor1 = ExtendTest[11];
+                    int descaleFactor2 = ExtendTest[12];
+                    dataUnit[rIndex] = (tmp10 + tmp3 + descaleFactor1) / descaleFactor2;
+                    dataUnit[rIndex + 7] = (tmp10 - tmp3 + descaleFactor1) / descaleFactor2;
+                    dataUnit[rIndex + 1] = (tmp11 + tmp2 + descaleFactor1) / descaleFactor2;
+                    dataUnit[rIndex + 6] = (tmp11 - tmp2 + descaleFactor1) / descaleFactor2;
+                    dataUnit[rIndex + 2] = (tmp12 + tmp1 + descaleFactor1) / descaleFactor2;
+                    dataUnit[rIndex + 5] = (tmp12 - tmp1 + descaleFactor1) / descaleFactor2;
+                    dataUnit[rIndex + 3] = (tmp13 + tmp0 + descaleFactor1) / descaleFactor2;
+                    dataUnit[rIndex + 4] = (tmp13 - tmp0 + descaleFactor1) / descaleFactor2;
+                }
+            }
+            /**
+             * Pass 2: process columns.
+             * Note that we must descale the results by a factor of 8 == 2**3,
+             * and also undo the PASS1_BITS scaling.
+             */
+            for (int col = 0; col < 8; col++) {
+               int c0 = col;
+               int c1 = col + 8;
+               int c2 = col + 16;
+               int c3 = col + 24;
+               int c4 = col + 32;
+               int c5 = col + 40;
+               int c6 = col + 48;
+               int c7 = col + 56;
+               if (isZeroInColumn(dataUnit, col)) {
+                    int dcVal = (dataUnit[c0] + 16) / 32;
+                    dataUnit[c0] = dcVal;
+                    dataUnit[c1] = dcVal;
+                    dataUnit[c2] = dcVal;
+                    dataUnit[c3] = dcVal;
+                    dataUnit[c4] = dcVal;
+                    dataUnit[c5] = dcVal;
+                    dataUnit[c6] = dcVal;
+                    dataUnit[c7] = dcVal;
+               } else {
+                    /**
+                     * Even part: reverse the even part of the forward DCT.
+                     * The rotator is sqrt(2)*c(-6).
+                     */
+                    int z2 = dataUnit[c2];
+                    int z3 = dataUnit[c6];
+                    int z1 = (z2 + z3) * FIX_0_541196100;
+                    int tmp2 = z1 + (z3 * (0 - FIX_1_847759065));
+                    int tmp3 = z1 + (z2 * FIX_0_765366865);
+                    int tmp0 = (dataUnit[c0] + dataUnit[c4]) * 8192;
+                    int tmp1 = (dataUnit[c0] - dataUnit[c4]) * 8192;
+                    int tmp10 = tmp0 + tmp3;
+                    int tmp13 = tmp0 - tmp3;
+                    int tmp11 = tmp1 + tmp2;
+                    int tmp12 = tmp1 - tmp2;
+                    /**
+                     * Odd part per figure 8; the matrix is unitary and hence its
+                     * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+                     */
+                    tmp0 = dataUnit[c7];
+                    tmp1 = dataUnit[c5];
+                    tmp2 = dataUnit[c3];
+                    tmp3 = dataUnit[c1];
+                    z1 = tmp0 + tmp3;
+                    z2 = tmp1 + tmp2;
+                    z3 = tmp0 + tmp2;
+                    int z4 = tmp1 + tmp3;
+                    int z5 = (z3 + z4) * FIX_1_175875602;      /* sqrt(2) * c3 */
+                       
+                    tmp0 = tmp0 * FIX_0_298631336;             /* sqrt(2) * (-c1+c3+c5-c7) */
+                    tmp1 = tmp1 * FIX_2_053119869;             /* sqrt(2) * ( c1+c3-c5+c7) */
+                    tmp2 = tmp2 * FIX_3_072711026;             /* sqrt(2) * ( c1+c3+c5-c7) */
+                    tmp3 = tmp3 * FIX_1_501321110;             /* sqrt(2) * ( c1+c3-c5-c7) */
+                    z1 = z1 * (0 - FIX_0_899976223);   /* sqrt(2) * (c7-c3) */
+                    z2 = z2 * (0 - FIX_2_562915447);   /* sqrt(2) * (-c1-c3) */
+                    z3 = z3 * (0 - FIX_1_961570560);   /* sqrt(2) * (-c3-c5) */
+                    z4 = z4 * (0 - FIX_0_390180644);   /* sqrt(2) * (c5-c3) */
+                       
+                    z3 = z3 + z5;
+                    z4 = z4 + z5;
+                       
+                    tmp0 = tmp0 + z1 + z3;
+                    tmp1 = tmp1 + z2 + z4;
+                    tmp2 = tmp2 + z2 + z3;
+                    tmp3 = tmp3 + z1 + z4;
+
+                    /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+                    int descaleFactor1 = ExtendTest[18];
+                    int descaleFactor2 = ExtendTest[19];
+                    dataUnit[c0] = (tmp10 + tmp3 + descaleFactor1) / descaleFactor2;
+                    dataUnit[c7] = (tmp10 - tmp3 + descaleFactor1) / descaleFactor2;
+                    dataUnit[c1] = (tmp11 + tmp2 + descaleFactor1) / descaleFactor2;
+                    dataUnit[c6] = (tmp11 - tmp2 + descaleFactor1) / descaleFactor2;
+                    dataUnit[c2] = (tmp12 + tmp1 + descaleFactor1) / descaleFactor2;
+                    dataUnit[c5] = (tmp12 - tmp1 + descaleFactor1) / descaleFactor2;
+                    dataUnit[c3] = (tmp13 + tmp0 + descaleFactor1) / descaleFactor2;
+                    dataUnit[c4] = (tmp13 - tmp0 + descaleFactor1) / descaleFactor2;
+               }
+            }
+        }
+        boolean isFileFormat(LEDataInputStream stream) {
+            try {
+               JPEGStartOfImage soi = new JPEGStartOfImage(stream);
+               stream.unread(soi.reference);
+               return soi.verify();  // we no longer check for appN
+            } catch (Exception e) {
+               return false;
+            }
+        }
+        boolean isZeroInColumn(int[] dataUnit, int col) {
+            return (dataUnit[col + 8] + dataUnit[col + 16] +
+                    dataUnit[col + 24] + dataUnit[col + 32] +
+                    dataUnit[col + 40] + dataUnit[col + 48] +
+                    dataUnit[col + 56]) == 0;
+        }
+        boolean isZeroInRow(int[] dataUnit, int rIndex) {
+            return (dataUnit[rIndex + 1] + dataUnit[rIndex + 2] +
+                    dataUnit[rIndex + 3] + dataUnit[rIndex + 4] +
+                    dataUnit[rIndex + 5] + dataUnit[rIndex + 6] +
+                    dataUnit[rIndex + 7]) == 0;
+        }
+        ImageData[] loadFromByteStream() {
+            JPEGStartOfImage soi = new JPEGStartOfImage(inputStream);
+            if (!soi.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
+            restartInterval = 0;
+
+            /* Process the tables preceding the frame header. */
+            processTables();
+       
+            /* Start of Frame. */
+            frameHeader = new JPEGFrameHeader(inputStream);
+            if (!frameHeader.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
+            imageWidth = frameHeader.getSamplesPerLine();
+            imageHeight = frameHeader.getNumberOfLines();
+            maxH = frameHeader.getMaxHFactor();
+            maxV = frameHeader.getMaxVFactor();
+            int mcuWidth = maxH * DCTSIZE;
+            int mcuHeight = maxV * DCTSIZE;
+            interleavedMcuCols = (imageWidth + mcuWidth - 1) / mcuWidth;
+            interleavedMcuRows = (imageHeight + mcuHeight - 1) / mcuHeight;
+            progressive = frameHeader.isProgressive();
+            samplePrecision = frameHeader.getSamplePrecision();
+            nComponents = frameHeader.getNumberOfImageComponents();
+            frameComponents = frameHeader.componentParameters;
+            componentIds = frameHeader.componentIdentifiers;
+            imageComponents = new byte[nComponents][];
+            if (progressive) {
+               // Progressive jpeg: need to keep all of the data units.
+               dataUnits = new int[nComponents][][];
+            } else {
+               // Sequential jpeg: only need one data unit.
+               dataUnit = new int[8 * 8];
+            }
+            for (int i = 0; i < nComponents; i++) {
+               int[] frameComponent = frameComponents[componentIds[i]];
+               int bufferSize = frameComponent[CW] * frameComponent[CH];
+               imageComponents[i] = new byte[bufferSize];
+               if (progressive) {
+                    dataUnits[i] = new int[bufferSize][];
+               }
+            }
+
+            /* Process the tables preceding the scan header. */
+            processTables();
+       
+            /* Start of Scan. */
+            scanHeader = new JPEGScanHeader(inputStream);
+            if (!scanHeader.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
+       
+            /* Process scan(s) and further tables until EOI. */
+            int progressiveScanCount = 0;
+            boolean done = false;
+            while(!done) {
+               resetInputBuffer();
+               precedingDCs = new int[4];
+               decodeScan();
+               if (progressive && loader.hasListeners()) {
+                    ImageData imageData = createImageData();
+                    loader.notifyListeners(new ImageLoaderEvent(loader, imageData, progressiveScanCount, false));
+                    progressiveScanCount++;
+               }
+
+               /* Unread any buffered data before looking for tables again. */
+               int delta = 512 - bufferCurrentPosition - 1;
+               if (delta > 0) {
+                    byte[] unreadBuffer = new byte[delta];
+                    System.arraycopy(dataBuffer, bufferCurrentPosition + 1, unreadBuffer, 0, delta);
+                    try {
+                        inputStream.unread(unreadBuffer);
+                    } catch (IOException e) {
+                        SWT.error(SWT.ERROR_IO, e);
+                    }
+               }
+               
+               /* Process the tables preceding the next scan header. */
+               JPEGSegment jpegSegment = processTables();
+               if (jpegSegment == null || jpegSegment.getSegmentMarker() == EOI) {
+                    done = true;
+               } else {
+                    scanHeader = new JPEGScanHeader(inputStream);
+                    if (!scanHeader.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
+               }
+            }
+       
+            if (progressive) {
+               for (int ymcu = 0; ymcu < interleavedMcuRows; ymcu++) {
+                    for (int xmcu = 0; xmcu < interleavedMcuCols; xmcu++) {
+                        for (int iComp = 0; iComp < nComponents; iComp++) {
+                            int[] frameComponent = frameComponents[componentIds[iComp]];
+                            int hi = frameComponent[HI];
+                            int vi = frameComponent[VI];
+                            int compWidth = frameComponent[CW];
+                            for (int ivi = 0; ivi < vi; ivi++) {
+                                for (int ihi = 0; ihi < hi; ihi++) {
+                                    int index = (ymcu * vi + ivi) * compWidth + xmcu * hi + ihi;
+                                    dataUnit = dataUnits[iComp][index];
+                                    dequantize(dataUnit, iComp);
+                                    inverseDCT(dataUnit);
+                                    storeData(dataUnit, iComp, xmcu, ymcu, hi, ihi, vi, ivi);
+                                }
+                            }
+                        }
+                    }
+               }
+            }
+            ImageData imageData = createImageData();
+            if (progressive && loader.hasListeners()) {
+               loader.notifyListeners(new ImageLoaderEvent(loader, imageData, progressiveScanCount, true));
+            }
+            return new ImageData[] {imageData};
+        }
+        ImageData createImageData() {
+            return ImageData.internal_new(
+                                          imageWidth,
+                                          imageHeight, 
+                                          nComponents * samplePrecision,
+                                          setUpPalette(),
+                                          nComponents == 1 ? 4 : 1,
+                                          decodeImageComponents(),
+                                          0,
+                                          null,
+                                          null,
+                                          -1,
+                                          -1,
+                                          SWT.IMAGE_JPEG,
+                                          0,
+                                          0,
+                                          0,
+                                          0);
+        }
+        int nextBit() {
+            if (currentBitCount != 0) {
+               currentBitCount--;
+               currentByte *= 2;
+               if (currentByte > 255) {
+                    currentByte -= 256;
+                    return 1;
+               } else {
+                    return 0;
+               }
+            }
+            bufferCurrentPosition++;
+            if (bufferCurrentPosition >= 512) {
+               resetInputBuffer();
+               bufferCurrentPosition = 0;
+            }
+            currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+            currentBitCount = 8;
+            byte nextByte;
+            if (bufferCurrentPosition == 511) {
+               resetInputBuffer();
+               currentBitCount = 8;
+               nextByte = dataBuffer[0];
+            } else {
+               nextByte = dataBuffer[bufferCurrentPosition + 1];
+            }
+            if (currentByte == 0xFF) {
+               if (nextByte == 0) {
+                    bufferCurrentPosition ++;
+                    currentBitCount--;
+                    currentByte *= 2;
+                    if (currentByte > 255) {
+                        currentByte -= 256;
+                        return 1;
+                    } else {
+                        return 0;
+                    }
+               } else {
+                    if ((nextByte & 0xFF) + 0xFF00 == DNL) {
+                        getDNL();
+                        return 0;
+                    } else {
+                        SWT.error(SWT.ERROR_INVALID_IMAGE);
+                        return 0;
+                    }
+               }
+            } else {
+               currentBitCount--;
+               currentByte *= 2;
+               if (currentByte > 255) {
+                    currentByte -= 256;
+                    return 1;
+               } else {
+                    return 0;
+               }
+            }
+        }
+        void processRestartInterval() {
+            do {
+               bufferCurrentPosition++;
+               if (bufferCurrentPosition > 511) {
+                    resetInputBuffer();
+                    bufferCurrentPosition = 0;
+               }
+               currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+            } while (currentByte != 0xFF);
+            while (currentByte == 0xFF) {
+               bufferCurrentPosition++;
+               if (bufferCurrentPosition > 511) {
+                    resetInputBuffer();
+                    bufferCurrentPosition = 0;
+               }
+               currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+            }
+            if (currentByte != ((RST0 + nextRestartNumber) % 256)) {
+               SWT.error(SWT.ERROR_INVALID_IMAGE);
+            }
+            bufferCurrentPosition++;
+            if (bufferCurrentPosition > 511) {
+               resetInputBuffer();
+               bufferCurrentPosition = 0;
+            }
+            currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+            currentBitCount = 8;
+            restartsToGo = restartInterval;
+            nextRestartNumber = (nextRestartNumber + 1) % 8;
+            precedingDCs = new int[4];
+            eobrun = 0;
+        }
+        /* Process all markers until a frame header, scan header, or EOI is found. */
+        JPEGSegment processTables() {
+            while (true) {
+               JPEGSegment jpegSegment = seekUnspecifiedMarker(inputStream);
+               if (jpegSegment == null) return null;
+               JPEGFrameHeader sof = new JPEGFrameHeader(jpegSegment.reference);
+               if (sof.verify()) {
+                    return jpegSegment;
+               }
+               int marker = jpegSegment.getSegmentMarker();
+               switch (marker) {
+                    case SOI: // there should only be one SOI per file
+                        SWT.error(SWT.ERROR_INVALID_IMAGE);
+                    case EOI:
+                    case SOS:
+                        return jpegSegment;
+                    case DQT:
+                        getDQT();
+                        break;
+                    case DHT:
+                        getDHT();
+                        break;
+                    case DAC:
+                        getDAC();
+                        break;
+                    case DRI:
+                        getDRI();
+                        break;
+                    case APP0:
+                        getAPP0();
+                        break;
+                    case COM:
+                        getCOM();
+                        break;
+                    default:
+                        skipSegmentFrom(inputStream);
+                       
+               }
+            }
+        }
+        void quantizeData(int[] dataUnit, int iComp) {
+            int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
+            for (int i = 0; i < dataUnit.length; i++) {
+               int zzIndex = ZigZag8x8[i];
+               int data = dataUnit[zzIndex];
+               int absData = data < 0 ? 0 - data : data;
+               int qValue = qTable[i];
+               int q2 = qValue / 2;
+               absData += q2;
+               if (absData < qValue) {
+                    dataUnit[zzIndex] = 0;
+               } else {
+                    absData /= qValue;
+                    if (data >= 0) {
+                        dataUnit[zzIndex] = absData;
+                    } else {
+                        dataUnit[zzIndex] = 0 - absData;
+                    }
+               }
+            }
+        }
+        int receive(int nBits) {
+            int v = 0;
+            for (int i = 0; i < nBits; i++) {
+               v = v * 2 + nextBit();
+            }
+            return v;
+        }
+        void resetInputBuffer() {
+            if (dataBuffer == null) {
+               dataBuffer = new byte[512];
+            }
+            try {
+               inputStream.read(dataBuffer);
+            } catch (IOException e) {
+               SWT.error(SWT.ERROR_IO, e);
+            }
+            currentBitCount = 0;
+            bufferCurrentPosition = -1;
+        }
+        void resetOutputBuffer() {
+            if (dataBuffer == null) {
+               dataBuffer = new byte[512];
+            } else {
+               try {
+                    outputStream.write(dataBuffer, 0, bufferCurrentPosition);
+               } catch (IOException e) {
+                    SWT.error(SWT.ERROR_IO, e);
+               }
+            }
+            bufferCurrentPosition = 0;
+        }
+        static JPEGSegment seekUnspecifiedMarker(LEDataInputStream byteStream) {
+            byte[] byteArray = new byte[2];
+            try {
+               while (true) {
+                    if (byteStream.read(byteArray, 0, 1) != 1) return null;
+                    if (byteArray[0] == (byte) 0xFF) {
+                        if (byteStream.read(byteArray, 1, 1) != 1) return null;
+                        if (byteArray[1] != (byte) 0xFF && byteArray[1] != 0) {
+                            byteStream.unread(byteArray);
+                            return new JPEGSegment(byteArray);
+                        }
+                    }
+               }
+            } catch (IOException e) {
+               SWT.error(SWT.ERROR_IO, e);
+            }
+            return null;
+        }
+        PaletteData setUpPalette() {
+            if (nComponents == 1) {
+               RGB[] entries = new RGB[256];
+               for (int i = 0; i < 256; i++) {
+                    entries[i] = new RGB(i, i, i);
+               }
+               return new PaletteData(entries);
+            }
+            return new PaletteData(0xFF, 0xFF00, 0xFF0000);
+        }
+        static void skipSegmentFrom(LEDataInputStream byteStream) {
+            try {
+               byte[] byteArray = new byte[4];
+               JPEGSegment jpegSegment = new JPEGSegment(byteArray);
+       
+               if (byteStream.read(byteArray) != byteArray.length) {
+                    SWT.error(SWT.ERROR_INVALID_IMAGE);
+               }
+               if (!(byteArray[0] == -1 && byteArray[1] != 0 && byteArray[1] != -1)) {
+                    SWT.error(SWT.ERROR_INVALID_IMAGE);
+               }
+               int delta = jpegSegment.getSegmentLength() - 2;
+               byteStream.skip(delta);
+            } catch (Exception e) {
+               SWT.error(SWT.ERROR_IO, e);
+            }
+        }
+        void storeData(int[] dataUnit, int iComp, int xmcu, int ymcu, int hi, int ihi, int vi, int ivi) {
+            byte[] compImage = imageComponents[iComp];
+            int[] frameComponent = frameComponents[componentIds[iComp]];
+            int compWidth = frameComponent[CW];
+            int destIndex = ((ymcu * vi + ivi) * compWidth * DCTSIZE) + ((xmcu * hi + ihi) * DCTSIZE);
+            int srcIndex = 0;
+            for (int i = 0; i < DCTSIZE; i++) {
+               for (int col = 0; col < DCTSIZE; col++) {
+                    int x = dataUnit[srcIndex] + 128;
+                    if (x < 0) {
+                        x = 0;
+                    } else {
+                        if (x > 255) x = 255;
+                    }
+                    compImage[destIndex + col] = (byte)x;
+                    srcIndex++;
+               }
+               destIndex += compWidth;
+            }
+        }
+        void unloadIntoByteStream(ImageData image) {
+            if (!new JPEGStartOfImage().writeToStream(outputStream)) {
+               SWT.error(SWT.ERROR_IO);
+            }
+            JPEGAppn appn = new JPEGAppn(new byte[] {(byte)0xFF, (byte)0xE0, 0, 0x10, 0x4A, 0x46, 0x49, 0x46, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0});
+            if (!appn.writeToStream(outputStream)) {
+               SWT.error(SWT.ERROR_IO);
+            }
+            quantizationTables = new int[4][];
+            JPEGQuantizationTable chromDQT = JPEGQuantizationTable.defaultChrominanceTable();
+            chromDQT.scaleBy(encoderQFactor);
+            int[] jpegDQTKeys = chromDQT.getQuantizationTablesKeys();
+            int[][] jpegDQTValues = chromDQT.getQuantizationTablesValues();
+            for (int i = 0; i < jpegDQTKeys.length; i++) {
+               quantizationTables[jpegDQTKeys[i]] = jpegDQTValues[i];
+            }
+            JPEGQuantizationTable lumDQT = JPEGQuantizationTable.defaultLuminanceTable();
+            lumDQT.scaleBy(encoderQFactor);
+            jpegDQTKeys = lumDQT.getQuantizationTablesKeys();
+            jpegDQTValues = lumDQT.getQuantizationTablesValues();
+            for (int i = 0; i < jpegDQTKeys.length; i++) {
+               quantizationTables[jpegDQTKeys[i]] = jpegDQTValues[i];
+            }
+            if (!lumDQT.writeToStream(outputStream)) {
+               SWT.error(SWT.ERROR_IO);
+            }
+            if (!chromDQT.writeToStream(outputStream)) {
+               SWT.error(SWT.ERROR_IO);
+            }
+            int frameLength, scanLength, precision;
+            int[][] frameParams, scanParams;
+            if (image.depth == 1) {
+               frameLength = 11;
+               frameParams = new int[1][];
+               frameParams[0] = new int[] {1, 1, 1, 0, 0};
+               scanParams = new int[1][];
+               scanParams[0] = new int[] {0, 0};
+               scanLength = 8;
+               nComponents = 1;
+               precision = 1;
+            } else {
+               frameLength = 17;
+               frameParams = new int[3][];
+               frameParams[0] = new int[] {0, 2, 2, 0, 0};
+               frameParams[1] = new int[] {1, 1, 1, 0, 0};
+               frameParams[2] = new int[] {1, 1, 1, 0, 0};
+               scanParams = new int[3][];
+               scanParams[0] = new int[] {0, 0};
+               scanParams[1] = new int[] {1, 1};
+               scanParams[2] = new int[] {1, 1};
+               scanLength = 12;
+               nComponents = 3;
+               precision = 8;
+            }
+            imageWidth = image.width;
+            imageHeight = image.height;
+            frameHeader = new JPEGFrameHeader(new byte[19]);
+            frameHeader.setSegmentMarker(SOF0);
+            frameHeader.setSegmentLength(frameLength);
+            frameHeader.setSamplePrecision(precision);
+            frameHeader.setSamplesPerLine(imageWidth);
+            frameHeader.setNumberOfLines(imageHeight);
+            frameHeader.setNumberOfImageComponents(nComponents);
+            frameHeader.componentParameters = frameParams;
+            frameHeader.componentIdentifiers = new int[] {0, 1, 2};
+            frameHeader.initializeContents();
+            if (!frameHeader.writeToStream(outputStream)) {
+               SWT.error(SWT.ERROR_IO);
+            }
+            frameComponents = frameParams;
+            componentIds = frameHeader.componentIdentifiers;
+            maxH = frameHeader.getMaxHFactor();
+            maxV = frameHeader.getMaxVFactor();
+            int mcuWidth = maxH * DCTSIZE;
+            int mcuHeight = maxV * DCTSIZE;
+            interleavedMcuCols = (imageWidth + mcuWidth - 1) / mcuWidth;
+            interleavedMcuRows = (imageHeight + mcuHeight - 1) / mcuHeight;
+            acHuffmanTables = new JPEGHuffmanTable[4];
+            dcHuffmanTables = new JPEGHuffmanTable[4];
+            JPEGHuffmanTable[] dhtTables = new JPEGHuffmanTable[] {
+               JPEGHuffmanTable.getDefaultDCLuminanceTable(),
+               JPEGHuffmanTable.getDefaultDCChrominanceTable(),
+               JPEGHuffmanTable.getDefaultACLuminanceTable(),
+               JPEGHuffmanTable.getDefaultACChrominanceTable()
+            };
+            for (int i = 0; i < dhtTables.length; i++) {
+               JPEGHuffmanTable dhtTable = dhtTables[i];
+               if (!dhtTable.writeToStream(outputStream)) {
+                    SWT.error(SWT.ERROR_IO);
+               }
+               JPEGHuffmanTable[] allTables = dhtTable.getAllTables();
+               for (int j = 0; j < allTables.length; j++) {
+                    JPEGHuffmanTable huffmanTable = allTables[j];
+                    if (huffmanTable.getTableClass() == 0) {
+                        dcHuffmanTables[huffmanTable.getTableIdentifier()] = huffmanTable;
+                    } else {
+                        acHuffmanTables[huffmanTable.getTableIdentifier()] = huffmanTable;
+                    }
+               }
+            }
+            precedingDCs = new int[4];
+            scanHeader = new JPEGScanHeader(new byte[14]);
+            scanHeader.setSegmentMarker(SOS);
+            scanHeader.setSegmentLength(scanLength);
+            scanHeader.setNumberOfImageComponents(nComponents);
+            scanHeader.setStartOfSpectralSelection(0);
+            scanHeader.setEndOfSpectralSelection(63);
+            scanHeader.componentParameters = scanParams;
+            scanHeader.initializeContents();
+            if (!scanHeader.writeToStream(outputStream)) {
+               SWT.error(SWT.ERROR_IO);
+            }
+            convertImageToYCbCr(image);
+            resetOutputBuffer();
+            currentByte = 0;
+            currentBitCount = 0;
+            encodeScan();
+            if (!new JPEGEndOfImage().writeToStream(outputStream)) {
+               SWT.error(SWT.ERROR_IO);
+            }
+        }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    abstract static class JPEGFixedSizeSegment extends JPEGSegment {
+
+       public JPEGFixedSizeSegment() {
+            reference = new byte[fixedSize()];
+            setSegmentMarker(signature());
+       }
+       
+       public JPEGFixedSizeSegment(byte[] reference) {
+            super(reference);
+       }
+       
+       public JPEGFixedSizeSegment(LEDataInputStream byteStream) {
+            reference = new byte[fixedSize()];
+            try {
+                byteStream.read(reference);
+            } catch (Exception e) { 
+                SWT.error(SWT.ERROR_IO, e);
+            }
+       }
+       
+       abstract public int fixedSize();
+
+       public int getSegmentLength() {
+            return fixedSize() - 2;
+       }
+       
+       public void setSegmentLength(int length) {
+       }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2004 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+
+    final static class JPEGFrameHeader extends JPEGVariableSizeSegment {
+       int maxVFactor;
+       int maxHFactor;
+       public int[] componentIdentifiers;
+       public int[][] componentParameters;
+
+       public JPEGFrameHeader(byte[] reference) {
+            super(reference);
+       }
+       
+       public JPEGFrameHeader(LEDataInputStream byteStream) {
+            super(byteStream);
+            initializeComponentParameters();
+       }
+       
+       public int getSamplePrecision() {
+            return reference[4] & 0xFF;
+       }
+       
+       public int getNumberOfLines() {
+            return (reference[5] & 0xFF) << 8 | (reference[6] & 0xFF);
+       }
+       
+       public int getSamplesPerLine() {
+            return (reference[7] & 0xFF) << 8 | (reference[8] & 0xFF);
+       }
+       
+       public int getNumberOfImageComponents() {
+            return reference[9] & 0xFF;
+       }
+       
+       public void setSamplePrecision(int precision) {
+            reference[4] = (byte)(precision & 0xFF);
+       }
+       
+       public void setNumberOfLines(int anInteger) {
+            reference[5] = (byte)((anInteger & 0xFF00) >> 8);
+            reference[6] = (byte)(anInteger & 0xFF);
+       }
+       
+       public void setSamplesPerLine(int samples) {
+            reference[7] = (byte)((samples & 0xFF00) >> 8);
+            reference[8] = (byte)(samples & 0xFF);
+       }
+       
+       public void setNumberOfImageComponents(int anInteger) {
+            reference[9] = (byte)(anInteger & 0xFF);
+       }
+       
+       public int getMaxHFactor() {
+            return maxHFactor;
+       }
+       
+       public int getMaxVFactor() {
+            return maxVFactor;
+       }
+       
+       public void setMaxHFactor(int anInteger) {
+            maxHFactor = anInteger;
+       }
+       
+       public void setMaxVFactor(int anInteger) {
+            maxVFactor = anInteger;
+       }
+       
+       /* Used when decoding. */
+       void initializeComponentParameters() {
+            int nf = getNumberOfImageComponents();
+            componentIdentifiers = new int[nf];
+            int[][] compSpecParams = new int[0][];
+            int hmax = 1;
+            int vmax = 1;
+            for (int i = 0; i < nf; i++) {
+                int ofs = i * 3 + 10;
+                int ci = reference[ofs] & 0xFF;
+                componentIdentifiers[i] = ci;
+                int hi = (reference[ofs + 1] & 0xFF) / 16;
+                int vi = (reference[ofs + 1] & 0xFF) % 16;
+                int tqi = reference[ofs + 2] & 0xFF;
+                if (hi > hmax) {
+                    hmax = hi;
+                }
+                if (vi > vmax) {
+                    vmax = vi;
+                }
+                int[] compParam = new int[5];
+                compParam[0] = tqi;
+                compParam[1] = hi;
+                compParam[2] = vi;
+                if (compSpecParams.length <= ci) {
+                    int[][] newParams = new int[ci + 1][];
+                    System.arraycopy(compSpecParams, 0, newParams, 0, compSpecParams.length);
+                    compSpecParams = newParams;
+                }
+                compSpecParams[ci] = compParam;
+            }
+            int x = getSamplesPerLine();
+            int y = getNumberOfLines();
+            int[] multiples = new int[] { 8, 16, 24, 32 };
+            for (int i = 0; i < nf; i++) {
+                int[] compParam = compSpecParams[componentIdentifiers[i]];
+                int hi = compParam[1];
+                int vi = compParam[2];
+                int compWidth = (x * hi + hmax - 1) / hmax;
+                int compHeight = (y * vi + vmax - 1) / vmax;
+                int dsWidth = roundUpToMultiple(compWidth, multiples[hi - 1]);
+                int dsHeight = roundUpToMultiple(compHeight, multiples[vi - 1]);
+                compParam[3] = dsWidth;
+                compParam[4] = dsHeight;
+            }
+            setMaxHFactor(hmax);
+            setMaxVFactor(vmax);
+            componentParameters = compSpecParams;
+       }
+       
+       /* Used when encoding. */
+       public void initializeContents() {
+            int nf = getNumberOfImageComponents();
+            if (nf == 0 || nf != componentParameters.length) {
+                SWT.error(SWT.ERROR_INVALID_IMAGE);
+            }
+            int hmax = 0;
+            int vmax = 0;
+            int[][] compSpecParams = componentParameters;
+            for (int i = 0; i < nf; i++) {
+                int ofs = i * 3 + 10;
+                int[] compParam = compSpecParams[componentIdentifiers[i]];
+                int hi = compParam[1];
+                int vi = compParam[2];
+                if (hi * vi > 4) {
+                    SWT.error(SWT.ERROR_INVALID_IMAGE);
+                }
+                reference[ofs] = (byte)(i + 1);
+                reference[ofs + 1] = (byte)(hi * 16 + vi);
+                reference[ofs + 2] = (byte)(compParam[0]);
+                if (hi > hmax) hmax = hi;
+                if (vi > vmax) vmax = vi;
+            }
+            int x = getSamplesPerLine();
+            int y = getNumberOfLines();
+            int[] multiples = new int[] {8, 16, 24, 32};
+            for (int i = 0; i < nf; i++) {
+                int[] compParam = compSpecParams[componentIdentifiers[i]];
+                int hi = compParam[1];
+                int vi = compParam[2];
+                int compWidth = (x * hi + hmax - 1) / hmax;
+                int compHeight = (y * vi + vmax - 1) / vmax;
+                int dsWidth = roundUpToMultiple(compWidth, multiples[hi - 1]);
+                int dsHeight = roundUpToMultiple(compHeight, multiples[vi - 1]);
+                compParam[3] = dsWidth;
+                compParam[4] = dsHeight;
+            }
+            setMaxHFactor(hmax);
+            setMaxVFactor(vmax);
+       }
+       
+       int roundUpToMultiple(int anInteger, int mInteger) {
+            int a = anInteger + mInteger - 1;
+            return a - (a % mInteger);
+       }
+       
+       /*
+        * Verify the information contained in the receiver is correct.
+        * Answer true if the header contains a valid marker. Otherwise,
+        * answer false. Valid Start Of Frame markers are:
+        *      SOF_0  - Baseline DCT, Huffman coding
+        *      SOF_1  - Extended sequential DCT, Huffman coding
+        *      SOF_2  - Progressive DCT, Huffman coding
+        *      SOF_3  - Lossless (sequential), Huffman coding
+        *      SOF_5  - Differential sequential, Huffman coding
+        *      SOF_6  - Differential progressive, Huffman coding
+        *      SOF_7  - Differential lossless, Huffman coding
+        *      SOF_9  - Extended sequential DCT, arithmetic coding
+        *      SOF_10 - Progressive DCT, arithmetic coding
+        *      SOF_11 - Lossless (sequential), arithmetic coding
+        *      SOF_13 - Differential sequential, arithmetic coding
+        *      SOF_14 - Differential progressive, arithmetic coding
+        *      SOF_15 - Differential lossless, arithmetic coding
+        */
+       public boolean verify() {
+            int marker = getSegmentMarker();
+            return (marker >= JPEGFileFormat.SOF0 && marker <= JPEGFileFormat.SOF3) ||
+                (marker >= JPEGFileFormat.SOF5 && marker <= JPEGFileFormat.SOF7) ||
+                (marker >= JPEGFileFormat.SOF9 && marker <= JPEGFileFormat.SOF11) ||
+                (marker >= JPEGFileFormat.SOF13 && marker <= JPEGFileFormat.SOF15);
+       }
+
+       public boolean isProgressive() {
+            int marker = getSegmentMarker();
+            return marker == JPEGFileFormat.SOF2
+                || marker == JPEGFileFormat.SOF6
+                || marker == JPEGFileFormat.SOF10
+                || marker == JPEGFileFormat.SOF14;
+       }
+       
+       public boolean isArithmeticCoding() {
+            return getSegmentMarker() >= JPEGFileFormat.SOF9;
+       }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    /**
+     * JPEGHuffmanTable static class actually represents two types of object:
+     * 1) A DHT (Define Huffman Tables) segment, which may represent
+     * as many as 4 Huffman tables. In this case, the tables are
+     * stored in the allTables array.
+     * 2) A single Huffman table. In this case, the allTables array
+     * will be null.
+     * The 'reference' field is stored in both types of object, but
+     * 'initialize' is only called if the object represents a DHT.
+     */
+    final static class JPEGHuffmanTable extends JPEGVariableSizeSegment {
+       JPEGHuffmanTable[] allTables;
+       int tableClass;
+       int tableIdentifier;
+       int[] dhCodes;
+       int[] dhCodeLengths;
+       int[] dhMaxCodes;
+       int[] dhMinCodes;
+       int[] dhValPtrs;
+       int[] dhValues;
+       int[] ehCodes;
+       byte[] ehCodeLengths;
+       static byte[] DCLuminanceTable = {
+            (byte)255, (byte)196, 0, 31, 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+       };
+       static byte[] DCChrominanceTable = {
+            (byte)255, (byte)196, 0, 31, 1, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+       };
+       static byte[] ACLuminanceTable = {
+            (byte)255, (byte)196, 0, (byte)181, 16, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125, 
+            1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 
+            50, (byte)129, (byte)145, (byte)161, 8, 35, 66, (byte)177, (byte)193, 21, 82, (byte)209, (byte)240, 36, 51, 98, 
+            114, (byte)130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 
+            54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 
+            88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 
+            119, 120, 121, 122, (byte)131, (byte)132, (byte)133, (byte)134, (byte)135, (byte)136, (byte)137, (byte)138, (byte)146, (byte)147, (byte)148, 
+            (byte)149, (byte)150, (byte)151, (byte)152, (byte)153, (byte)154, (byte)162, (byte)163, (byte)164, (byte)165, (byte)166, (byte)167, (byte)168, (byte)169, (byte)170, 
+            (byte)178, (byte)179, (byte)180, (byte)181, (byte)182, (byte)183, (byte)184, (byte)185, (byte)186, (byte)194, (byte)195, (byte)196, (byte)197, (byte)198, (byte)199, 
+            (byte)200, (byte)201, (byte)202, (byte)210, (byte)211, (byte)212, (byte)213, (byte)214, (byte)215, (byte)216, (byte)217, (byte)218, (byte)225, (byte)226, (byte)227, 
+            (byte)228, (byte)229, (byte)230, (byte)231, (byte)232, (byte)233, (byte)234, (byte)241, (byte)242, (byte)243, (byte)244, (byte)245, (byte)246, (byte)247, (byte)248, 
+            (byte)249, (byte)250
+       };
+       static byte[] ACChrominanceTable = {
+            (byte)255, (byte)196, 0, (byte)181, 17, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 
+            1, 2, 119, 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34, 
+            50, (byte)129, 8, 20, 66, (byte)145, (byte)161, (byte)177, (byte)193, 9, 35, 
+            51, 82, (byte)240, 21, 98, 114, (byte)209, 10, 22, 36, 52, (byte)225, 37, 
+            (byte)241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67, 
+            68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 
+            103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, (byte)130, 
+            (byte)131, (byte)132, (byte)133, (byte)134, (byte)135, (byte)136, (byte)137, 
+            (byte)138, (byte)146, (byte)147, (byte)148, (byte)149, (byte)150, (byte)151, 
+            (byte)152, (byte)153, (byte)154, (byte)162, (byte)163, (byte)164, (byte)165, 
+            (byte)166, (byte)167, (byte)168, (byte)169, (byte)170, (byte)178, (byte)179, 
+            (byte)180, (byte)181, (byte)182, (byte)183, (byte)184, (byte)185, (byte)186, 
+            (byte)194, (byte)195, (byte)196, (byte)197, (byte)198, (byte)199, (byte)200, 
+            (byte)201, (byte)202, (byte)210, (byte)211, (byte)212, (byte)213, (byte)214, 
+            (byte)215, (byte)216, (byte)217, (byte)218, (byte)226, (byte)227, (byte)228, 
+            (byte)229, (byte)230, (byte)231, (byte)232, (byte)233, (byte)234, (byte)242, 
+            (byte)243, (byte)244, (byte)245, (byte)246, (byte)247, (byte)248, (byte)249, 
+            (byte)250
+       };
+       
+        public JPEGHuffmanTable(byte[] reference) {
+            super(reference);
+        }
+
+        public JPEGHuffmanTable(LEDataInputStream byteStream) {
+            super(byteStream);
+            initialize();
+        }
+
+        public JPEGHuffmanTable[] getAllTables() {
+            return allTables;
+        }
+
+        public static JPEGHuffmanTable getDefaultACChrominanceTable() {
+            JPEGHuffmanTable result = new JPEGHuffmanTable(ACChrominanceTable);
+            result.initialize();
+            return result;
+        }
+
+        public static JPEGHuffmanTable getDefaultACLuminanceTable() {
+            JPEGHuffmanTable result = new JPEGHuffmanTable(ACLuminanceTable);
+            result.initialize();
+            return result;
+        }
+
+        public static JPEGHuffmanTable getDefaultDCChrominanceTable() {
+            JPEGHuffmanTable result = new JPEGHuffmanTable(DCChrominanceTable);
+            result.initialize();
+            return result;
+        }
+
+        public static JPEGHuffmanTable getDefaultDCLuminanceTable() {
+            JPEGHuffmanTable result = new JPEGHuffmanTable(DCLuminanceTable);
+            result.initialize();
+            return result;
+        }
+
+        public int[] getDhMaxCodes() {
+            return dhMaxCodes;
+        }
+
+        public int[] getDhMinCodes() {
+            return dhMinCodes;
+        }
+
+        public int[] getDhValPtrs() {
+            return dhValPtrs;
+        }
+
+        public int[] getDhValues() {
+            return dhValues;
+        }
+
+        public int getTableClass() {
+            return tableClass;
+        }
+
+        public int getTableIdentifier() {
+            return tableIdentifier;
+        }
+
+        void initialize() {
+            int totalLength = getSegmentLength() - 2;
+            int ofs = 4;
+            int[] bits = new int[16];
+            JPEGHuffmanTable[] huffTables = new JPEGHuffmanTable[8]; // maximum is 4 AC + 4 DC
+            int huffTableCount = 0;
+            while (totalLength > 0) {
+               int tc = (reference[ofs] & 0xFF) / 16; // table class: AC (1) or DC (0)
+               int tid = (reference[ofs] & 0xFF) % 16; // table id: 0-1 baseline, 0-3 prog/ext
+               ofs++;
+               
+               /* Read the 16 count bytes and add them together to get the table size. */
+               int count = 0;
+               for (int i = 0; i < bits.length; i++) {
+                    int bCount = reference[ofs + i] & 0xFF;
+                    bits[i] = bCount;
+                    count += bCount;
+               }
+               ofs += 16;
+               totalLength -= 17;
+               
+               /* Read the table. */
+               int[] huffVals = new int[count];
+               for (int i = 0; i < count; i++) {
+                    huffVals[i] = reference[ofs + i] & 0xFF;
+               }
+               ofs += count;
+               totalLength -= count;
+               
+               /* Calculate the lengths. */
+               int[] huffCodeLengths = new int[50]; // start with 50 and increment as needed
+               int huffCodeLengthsIndex = 0;
+               for (int i = 0; i < 16; i++) {
+                    for (int j = 0; j < bits[i]; j++) {
+                        if (huffCodeLengthsIndex >= huffCodeLengths.length) {
+                            int[] newHuffCodeLengths = new int[huffCodeLengths.length + 50];
+                            System.arraycopy(huffCodeLengths, 0, newHuffCodeLengths, 0, huffCodeLengths.length);
+                            huffCodeLengths = newHuffCodeLengths;
+                        }
+                        huffCodeLengths[huffCodeLengthsIndex] = i + 1;
+                        huffCodeLengthsIndex++;
+                    }
+               }
+               
+               /* Truncate huffCodeLengths to the correct size. */
+               if (huffCodeLengthsIndex < huffCodeLengths.length) {
+                    int[] newHuffCodeLengths = new int[huffCodeLengthsIndex];
+                    System.arraycopy(huffCodeLengths, 0, newHuffCodeLengths, 0, huffCodeLengthsIndex);
+                    huffCodeLengths = newHuffCodeLengths;
+               }
+               
+               /* Calculate the Huffman codes. */
+               int[] huffCodes = new int[50]; // start with 50 and increment as needed
+               int huffCodesIndex = 0;
+               int k = 1;
+               int code = 0;
+               int si = huffCodeLengths[0];
+               int p = 0;
+               while (p < huffCodeLengthsIndex) {
+                    while ((p < huffCodeLengthsIndex) && (huffCodeLengths[p] == si)) {
+                        if (huffCodesIndex >= huffCodes.length) {
+                            int[] newHuffCodes = new int[huffCodes.length + 50];
+                            System.arraycopy(huffCodes, 0, newHuffCodes, 0, huffCodes.length);
+                            huffCodes = newHuffCodes;
+                        }
+                        huffCodes[huffCodesIndex] = code;
+                        huffCodesIndex++;
+                        code++;
+                        p++;
+                    }
+                    code *= 2;
+                    si++;
+               }
+               
+               /* Truncate huffCodes to the correct size. */
+               if (huffCodesIndex < huffCodes.length) {
+                    int[] newHuffCodes = new int[huffCodesIndex];
+                    System.arraycopy(huffCodes, 0, newHuffCodes, 0, huffCodesIndex);
+                    huffCodes = newHuffCodes;
+               }
+               
+               /* Calculate the maximum and minimum codes */
+               k = 0;
+               int[] maxCodes = new int[16];
+               int[] minCodes = new int[16];
+               int[] valPtrs = new int[16];
+               for (int i = 0; i < 16; i++) {
+                    int bSize = bits[i];
+                    if (bSize == 0) {
+                        maxCodes[i] = -1;
+                    } else {
+                        valPtrs[i] = k;
+                        minCodes[i] = huffCodes[k];
+                        k += bSize;
+                        maxCodes[i] = huffCodes[k - 1];
+                    }
+               }
+               
+               /* Calculate the eHuffman codes and lengths. */
+               int[] eHuffCodes = new int[256];
+               byte[] eHuffSize = new byte[256];
+               for (int i = 0; i < huffCodesIndex; i++) {
+                    eHuffCodes[huffVals[i]] = huffCodes[i];
+                    eHuffSize[huffVals[i]] = (byte)huffCodeLengths[i];
+               }
+               
+               /* Create the new JPEGHuffmanTable and add it to the allTables array. */
+               JPEGHuffmanTable dhtTable = new JPEGHuffmanTable(reference);
+               dhtTable.tableClass = tc;
+               dhtTable.tableIdentifier = tid;
+               dhtTable.dhValues = huffVals;
+               dhtTable.dhCodes = huffCodes;
+               dhtTable.dhCodeLengths = huffCodeLengths;
+               dhtTable.dhMinCodes = minCodes;
+               dhtTable.dhMaxCodes = maxCodes;
+               dhtTable.dhValPtrs = valPtrs;
+               dhtTable.ehCodes = eHuffCodes;
+               dhtTable.ehCodeLengths = eHuffSize;
+               huffTables[huffTableCount] = dhtTable;
+               huffTableCount++;
+            }
+            allTables = new JPEGHuffmanTable[huffTableCount];
+            System.arraycopy(huffTables, 0, allTables, 0, huffTableCount);
+        }
+
+        public int signature() {
+            return JPEGFileFormat.DHT;
+        }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    final static class JPEGQuantizationTable extends JPEGVariableSizeSegment {
+       public static byte[] DefaultLuminanceQTable = {
+            (byte)255, (byte)219, 0, 67, 0,
+            16, 11, 10, 16, 24, 40, 51, 61,
+            12, 12, 14, 19, 26, 58, 60, 55,
+            14, 13, 16, 24, 40, 57, 69, 56,
+            14, 17, 22, 29, 51, 87, 80, 62,
+            18, 22, 37, 56, 68, 109, 103, 77,
+            24, 35, 55, 64, 81, 104, 113, 92,
+            49, 64, 78, 87, 103, 121, 120, 101,
+            72, 92, 95, 98, 112, 100, 103, 99
+       };
+       public static byte[] DefaultChrominanceQTable = {
+            (byte)255, (byte)219, 0, 67, 1,
+            17, 18, 24, 47, 99, 99, 99, 99,
+            18, 21, 26, 66, 99, 99, 99, 99,
+            24, 26, 56, 99, 99, 99, 99, 99,
+            47, 66, 99, 99, 99, 99, 99, 99,
+            99, 99, 99, 99, 99, 99, 99, 99,
+            99, 99, 99, 99, 99, 99, 99, 99,
+            99, 99, 99, 99, 99, 99, 99, 99,
+            99, 99, 99, 99, 99, 99, 99, 99
+       };
+       
+        public JPEGQuantizationTable(byte[] reference) {
+            super(reference);
+        }
+
+        public JPEGQuantizationTable(LEDataInputStream byteStream) {
+            super(byteStream);
+        }
+
+        public static JPEGQuantizationTable defaultChrominanceTable() {
+            byte[] data = new byte[DefaultChrominanceQTable.length];
+            System.arraycopy(DefaultChrominanceQTable, 0, data, 0, data.length);
+            return new JPEGQuantizationTable(data);
+        }
+
+        public static JPEGQuantizationTable defaultLuminanceTable() {
+            byte[] data = new byte[DefaultLuminanceQTable.length];
+            System.arraycopy(DefaultLuminanceQTable, 0, data, 0, data.length);
+            return new JPEGQuantizationTable(data);
+        }
+
+        public int[] getQuantizationTablesKeys() {
+            int[] keys = new int[4];
+            int keysIndex = 0;
+            int totalLength = getSegmentLength() - 2;
+            int ofs = 4;
+            while (totalLength > 64) {
+               int tq = (reference[ofs] & 0xFF) % 16;
+               int pq = (reference[ofs] & 0xFF) / 16;
+               if (pq == 0) {
+                    ofs += 65;
+                    totalLength -= 65;
+               } else {
+                    ofs += 129;
+                    totalLength -= 129;
+               }
+               if (keysIndex >= keys.length) {
+                    int[] newKeys = new int[keys.length + 4];
+                    System.arraycopy(keys, 0, newKeys, 0, keys.length);
+                    keys = newKeys;
+               }
+               keys[keysIndex] = tq;
+               keysIndex++;
+            }
+            int[] newKeys = new int[keysIndex];
+            System.arraycopy(keys, 0, newKeys, 0, keysIndex);
+            return newKeys;
+        }
+
+        public int[][] getQuantizationTablesValues() {
+            int[][] values = new int[4][];
+            int valuesIndex = 0;
+            int totalLength = getSegmentLength() - 2;
+            int ofs = 4;
+            while (totalLength > 64) {
+               int[] qk = new int[64];
+               int pq = (reference[ofs] & 0xFF) / 16;
+               if (pq == 0) {
+                    for (int i = 0; i < qk.length; i++) {
+                        qk[i] = reference[ofs + i + 1];
+                    }
+                    ofs += 65;
+                    totalLength -= 65;
+               } else {
+                    for (int i = 0; i < qk.length; i++) {
+                        int idx = (i - 1) * 2 ;
+                        qk[i] = (reference[ofs + idx + 1] & 0xFF) * 256 + (reference[ofs + idx + 2] & 0xFF);
+                    }
+                    ofs += 129;
+                    totalLength -= 129;
+               }
+               if (valuesIndex >= values.length) {
+                    int[][] newValues = new int[values.length + 4][];
+                    System.arraycopy(values, 0, newValues, 0, values.length);
+                    values = newValues;
+               }
+               values[valuesIndex] = qk;
+               valuesIndex++;
+            }
+            int[][] newValues = new int[valuesIndex][];
+            System.arraycopy(values, 0, newValues, 0, valuesIndex);
+            return newValues;
+        }
+
+        public void scaleBy(int qualityFactor) {
+            int qFactor = qualityFactor;
+            if (qFactor <= 0) {
+               qFactor = 1;
+            }
+            if (qFactor > 100) {
+               qFactor = 100;
+            }
+            if (qFactor < 50) {
+               qFactor = 5000 / qFactor;
+            } else {
+               qFactor = 200 - (qFactor * 2);
+            }
+            int totalLength = getSegmentLength() - 2;
+            int ofs = 4;
+            while (totalLength > 64) {
+                //             int tq = (reference[ofs] & 0xFF) % 16;
+               int pq = (reference[ofs] & 0xFF) / 16;
+               if (pq == 0) {
+                    for (int i = ofs + 1; i <= ofs + 64; i++) {
+                        int temp = ((reference[i] & 0xFF) * qFactor + 50) / 100;
+                        if (temp <= 0) temp = 1;
+                        if (temp > 255) temp = 255;
+                        reference[i] = (byte)temp;
+                    }
+                    ofs += 65;
+                    totalLength -= 65;
+               } else {
+                    for (int i = ofs + 1; i <= ofs + 128; i += 2) {
+                        int temp = (((reference[i] & 0xFF) * 256 + (reference[i + 1] & 0xFF)) * qFactor + 50) / 100;
+                        if (temp <= 0) temp = 1;
+                        if (temp > 32767) temp = 32767;
+                        reference[i] = (byte)(temp / 256);
+                        reference[i + 1] = (byte)(temp % 256);
+                    }
+                    ofs += 129;
+                    totalLength -= 129;
+               }
+            }
+        }
+
+        public int signature() {
+            return JPEGFileFormat.DQT;
+        }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    final static class JPEGRestartInterval extends JPEGFixedSizeSegment {
+
+       public JPEGRestartInterval(LEDataInputStream byteStream) {
+            super(byteStream);
+       }
+       
+       public int signature() {
+            return JPEGFileFormat.DRI;
+       }
+       
+       public int getRestartInterval() {
+            return ((reference[4] & 0xFF) << 8 | (reference[5] & 0xFF));
+       }
+
+       public int fixedSize() {
+            return 6;
+       }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2004 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+
+    final static class JPEGScanHeader extends JPEGVariableSizeSegment {
+       public int[][] componentParameters;
+
+        public JPEGScanHeader(byte[] reference) {
+            super(reference);
+        }
+
+        public JPEGScanHeader(LEDataInputStream byteStream) {
+            super(byteStream);
+            initializeComponentParameters();
+        }
+
+        public int getApproxBitPositionHigh() {
+            return (reference[(2 * getNumberOfImageComponents()) + 7] & 0xFF) / 16;
+        }
+
+        public int getApproxBitPositionLow() {
+            return (reference[(2 * getNumberOfImageComponents()) + 7] & 0xFF) % 16;
+        }
+
+        public int getEndOfSpectralSelection() {
+            return (reference[(2 * getNumberOfImageComponents()) + 6] & 0xFF);
+        }
+
+        public int getNumberOfImageComponents() {
+            return (reference[4] & 0xFF);
+        }
+
+        public int getStartOfSpectralSelection() {
+            return (reference[(2 * getNumberOfImageComponents()) + 5] & 0xFF);
+        }
+
+        /* Used when decoding. */
+        void initializeComponentParameters() {
+            int compCount = getNumberOfImageComponents();
+            componentParameters = new int[0][];
+            for (int i = 0; i < compCount; i++) {
+               int ofs = 5 + i * 2;
+               int cid = reference[ofs] & 0xFF;
+               int dc = (reference[ofs + 1] & 0xFF) / 16;
+               int ac = (reference[ofs + 1] & 0xFF) % 16;
+               if (componentParameters.length <= cid) {
+                    int[][] newParams = new int[cid + 1][];
+                    System.arraycopy(componentParameters, 0, newParams, 0, componentParameters.length);
+                    componentParameters = newParams;
+               }
+               componentParameters[cid] = new int[] { dc, ac };
+            }
+        }
+
+        /* Used when encoding. */
+        public void initializeContents() {
+            int compCount = getNumberOfImageComponents();
+            int[][] compSpecParams = componentParameters;
+            if (compCount == 0 || compCount != compSpecParams.length) {
+               SWT.error(SWT.ERROR_INVALID_IMAGE);
+            }
+            for (int i = 0; i < compCount; i++) {
+               int ofs = i * 2 + 5;
+               int[] compParams = compSpecParams[i];
+               reference[ofs] = (byte)(i + 1);
+               reference[ofs + 1] = (byte)(compParams[0] * 16 + compParams[1]);
+            }
+        }
+
+        public void setEndOfSpectralSelection(int anInteger) {
+            reference[(2 * getNumberOfImageComponents()) + 6] = (byte)anInteger;
+        }
+
+        public void setNumberOfImageComponents(int anInteger) {
+            reference[4] = (byte)(anInteger & 0xFF);
+        }
+
+        public void setStartOfSpectralSelection(int anInteger) {
+            reference[(2 * getNumberOfImageComponents()) + 5] = (byte)anInteger;
+        }
+
+        public int signature() {
+            return JPEGFileFormat.SOS;
+        }
+
+        public boolean verifyProgressiveScan() {
+            int start = getStartOfSpectralSelection();
+            int end = getEndOfSpectralSelection();
+            int low = getApproxBitPositionLow();
+            int high = getApproxBitPositionHigh();
+            int count = getNumberOfImageComponents();
+            if ((start == 0 && end == 00) || (start <= end && end <= 63)) {
+               if (low <= 13 && high <= 13 && (high == 0 || high == low + 1)) {
+                    return start == 0 || (start > 0 && count == 1);
+               }
+            }
+            return false;
+        }
+
+        public boolean isACProgressiveScan() {
+            return getStartOfSpectralSelection() != 0 && getEndOfSpectralSelection() != 0;
+        }
+
+        public boolean isDCProgressiveScan() {
+            return getStartOfSpectralSelection() == 0 && getEndOfSpectralSelection() == 0;
+        }
+
+        public boolean isFirstScan() {
+            return getApproxBitPositionHigh() == 0;
+        }
+
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    static class JPEGSegment {
+       public byte[] reference;
+
+       JPEGSegment() {
+       }
+       
+       public JPEGSegment(byte[] reference) {
+            this.reference = reference;
+       }
+       
+       public int signature() {
+            return 0;
+       }
+       
+       public boolean verify() {
+            return getSegmentMarker() == signature();
+       }
+       
+       public int getSegmentMarker() {
+            return ((reference[0] & 0xFF) << 8 | (reference[1] & 0xFF));
+       }
+       
+       public void setSegmentMarker(int marker) {
+            reference[0] = (byte)((marker & 0xFF00) >> 8);
+            reference[1] = (byte)(marker & 0xFF);
+       }
+       
+       public int getSegmentLength() {
+            return ((reference[2] & 0xFF) << 8 | (reference[3] & 0xFF));
+       }
+       
+       public void setSegmentLength(int length) {
+            reference[2] = (byte)((length & 0xFF00) >> 8);
+            reference[3] = (byte)(length & 0xFF);
+       }
+       
+       public boolean writeToStream(LEDataOutputStream byteStream) {
+            try {
+                byteStream.write(reference);
+                return true;
+            } catch (Exception e) {
+                return false;
+            }
+       }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+    final static class JPEGStartOfImage extends JPEGFixedSizeSegment {
+
+       public JPEGStartOfImage() {
+            super();
+       }
+       
+       public JPEGStartOfImage(byte[] reference) {
+            super(reference);
+       }
+       
+       public JPEGStartOfImage(LEDataInputStream byteStream) {
+            super(byteStream);
+       }
+       
+       public int signature() {
+            return JPEGFileFormat.SOI;
+       }
+       
+       public int fixedSize() {
+            return 2;
+       }
+    }
+    /*******************************************************************************
+     * Copyright (c) 2000, 2003 IBM Corporation and others.
+     * All rights reserved. This program and the accompanying materials 
+     * are made available under the terms of the Common Public License v1.0
+     * which accompanies this distribution, and is available at
+     * http://www.eclipse.org/legal/cpl-v10.html
+     * 
+     * Contributors:
+     *     IBM Corporation - initial API and implementation
+     *******************************************************************************/
+
+
+
+    abstract static class JPEGVariableSizeSegment extends JPEGSegment {
+
+       public JPEGVariableSizeSegment(byte[] reference) {
+            super(reference);
+       }
+       
+       public JPEGVariableSizeSegment(LEDataInputStream byteStream) {
+            try {
+                byte[] header = new byte[4];
+                byteStream.read(header);
+                reference = header; // to use getSegmentLength()
+                byte[] contents = new byte[getSegmentLength() + 2];
+                contents[0] = header[0];
+                contents[1] = header[1];
+                contents[2] = header[2];
+                contents[3] = header[3];
+                byteStream.read(contents, 4, contents.length - 4);
+                reference = contents;
+            } catch (Exception e) {
+                SWT.error(SWT.ERROR_IO, e);
+            }
+       }
+    }
+}