From: adam Date: Mon, 10 Jan 2005 10:38:37 +0000 (+0000) Subject: imported and butchered SWTs JPEG decoder X-Git-Tag: 01-July-2005~27 X-Git-Url: http://git.megacz.com/?p=org.ibex.core.git;a=commitdiff_plain;h=7699db5b21b75ea5a6ad31681ff6d7260b78e15e imported and butchered SWTs JPEG decoder darcs-hash:20050110103837-5007d-7bfb77acf496c26038ee18656c04114c0d5a29e4.gz --- diff --git a/src/org/ibex/graphics/JPEG.java b/src/org/ibex/graphics/JPEG.java new file mode 100644 index 0000000..1857e82 --- /dev/null +++ b/src/org/ibex/graphics/JPEG.java @@ -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. + *

+ * After creating an instance of a class that implements + * this interface it can be added to an image loader using the + * addImageLoaderListener method and removed using + * the removeImageLoaderListener method. When + * image data is either partially or completely loaded, this + * method will be invoked. + *

+ * + * @see ImageLoader + * @see ImageLoaderEvent + */ + + public static interface ImageLoaderListener { + + /** + * Sent when image data is either partially or completely loaded. + *

+ * The timing of when this method is called varies depending on + * the format of the image being loaded. + *

+ * + * @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. + *

+ * Notes: + *

+ * + * @see ImageLoader + * @see ImageLoaderListener + */ + + public static class ImageLoaderEvent { + + /** + * if the endOfImage flag is false, then this is a + * partially complete copy of the current ImageData, + * otherwise this is a completely loaded ImageData + */ + 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. + *

+ * Currently supported image formats are: + *

+ * ImageLoaders can be used to: + * + */ + + 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 + * imageLoaderListeners field. + */ + void reset() { + data = null; + logicalScreenWidth = 0; + logicalScreenHeight = 0; + backgroundPixel = -1; + repeatCount = 1; + } + + /** + * Loads an array of ImageData 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 ImageData objects loaded from the specified input stream + * + * @exception IllegalArgumentException + * @exception SWTException + */ + 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 ImageData 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 ImageData objects loaded from the specified file + * + * @exception IllegalArgumentException + * @exception SWTException + */ + 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: + *
+ *
IMAGE_BMP
+ *
Windows BMP file format, no compression
+ *
IMAGE_BMP_RLE
+ *
Windows BMP file format, RLE compression if appropriate
+ *
IMAGE_GIF
+ *
GIF file format
+ *
IMAGE_ICO
+ *
Windows ICO file format
+ *
IMAGE_JPEG
+ *
JPEG file format
+ *
IMAGE_PNG
+ *
PNG file format
+ *
+ * + * @param stream the output stream to write the images to + * @param format the format to write the images in + * + * @exception IllegalArgumentException + * @exception SWTException + */ + 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: + *
+ *
IMAGE_BMP
+ *
Windows BMP file format, no compression
+ *
IMAGE_BMP_RLE
+ *
Windows BMP file format, RLE compression if appropriate
+ *
IMAGE_GIF
+ *
GIF file format
+ *
IMAGE_ICO
+ *
Windows ICO file format
+ *
IMAGE_JPEG
+ *
JPEG file format
+ *
IMAGE_PNG
+ *
PNG file format
+ *
+ * + * @param filename the name of the file to write the images to + * @param format the format to write the images in + * + * @exception IllegalArgumentException + * @exception SWTException + */ + 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. + *

+ * An ImageLoaderListener should be added before invoking + * one of the receiver's load methods. The listener's + * imageDataLoaded 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

+ * + * @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 + * + * @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 true if the receiver has image loader + * listeners, and false otherwise. + * + * @return true if there are ImageLoaderListeners, and false 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 ImageLoaderEvent to send to each ImageLoaderListener + */ + 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. + *

+ * 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. + *

+ *

+ * Application code does not need to explicitly release the + * resources managed by each instance when those instances are no longer + * required, and thus no dispose() method is provided. + *

+ * + * @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 + */ + 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 same object using a class + * specific comparison. + * + * @param object the object to compare with this object + * @return true if the object is the same as this object and false 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 true when passed to + * equals 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 RGB + */ + 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. + *

+ * Depending on the depth of the image, the PaletteData can take one + * of two forms, indicated by the isDirect field: + *

+ *
+ *
+ * isDirect is false + *
+ *
+ * If isDirect is false, this palette is an indexed + * palette which maps pixel values to RGBs. The actual RGB values + * may be retrieved by using the getRGBs() method. + *
+ *
+ * isDirect is true + *
+ *
+ * If isDirect is true, 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. + *

+ * 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. + *

+ *
+ *
+ * + * @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 RGBs for the palette + * + * @exception IllegalArgumentException + */ + 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 RBG. + * + * @param rgb the RGB to get the pixel value for + * @return the pixel value for the given RGB + * + * @exception IllegalArgumentException + */ + 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 RGB 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 + */ + 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 RGBs 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 + * Image. + *

+ * Note that the public fields x, y, + * disposalMethod and delayTime are + * typically only used when the image is in a set of images used + * for animation. + *

+ * + * @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. + *

+ * 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. + *

+ * If one scanline of the image is not a multiple of + * this number, it will be padded with zeros until it is. + *

+ */ + public int scanlinePad; + + /** + * The number of bytes per scanline. + *

+ * This is a multiple of the scanline padding. + *

+ */ + public int bytesPerLine; + + /** + * The pixel data of the image. + *

+ * 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. + *

+ */ + public byte[] data; + + /** + * The color table for the image. + */ + public PaletteData palette; + + /** + * The transparent pixel. + *

+ * Pixels with this value are transparent. + *

+ * The default is -1 which means 'no transparent pixel'. + *

+ */ + public int transparentPixel; + + /** + * An icon-specific field containing the data from the icon mask. + *

+ * 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'. + *

+ * The default is null which means 'no transparency mask'. + *

+ */ + public byte[] maskData; + + /** + * An icon-specific field containing the scanline pad of the mask. + *

+ * If one scanline of the transparency mask is not a + * multiple of this number, it will be padded with zeros until + * it is. + *

+ */ + public int maskPad; + + /** + * The alpha data of the image. + *

+ * Every pixel can have an alpha blending value that + * varies from 0, meaning fully transparent, to 255 meaning + * fully opaque. The number of bytes per scanline is + * 'width'. + *

+ */ + public byte[] alphaData; + + /** + * The global alpha value to be used for every pixel. + *

+ * If this value is set, the alphaData field + * is ignored and when the image is rendered each pixel + * will be blended with the background an amount + * proportional to this value. + *

+ * The default is -1 which means 'no global alpha value' + *

+ */ + public int alpha; + + /** + * The type of file from which the image was read. + * + * It is expressed as one of the following values: + *
+ *
IMAGE_BMP
+ *
Windows BMP file format, no compression
+ *
IMAGE_BMP_RLE
+ *
Windows BMP file format, RLE compression if appropriate
+ *
IMAGE_GIF
+ *
GIF file format
+ *
IMAGE_ICO
+ *
Windows ICO file format
+ *
IMAGE_JPEG
+ *
JPEG file format
+ *
IMAGE_PNG
+ *
PNG file format
+ *
+ */ + 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: + *
+ *
DM_UNSPECIFIED
+ *
disposal method not specified
+ *
DM_FILL_NONE
+ *
do nothing - leave the image in place
+ *
DM_FILL_BACKGROUND
+ *
fill with the background color
+ *
DM_FILL_PREVIOUS
+ *
restore the previous picture
+ *
+ * (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 + */ + 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 + */ + 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 ImageData 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. + *

+ * 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 + * ImageLoader.load(). + *

+ * + * @param stream the input stream to load the image from (must not be null) + * + * @exception IllegalArgumentException + * @exception SWTException + * + * @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 ImageData 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. + *

+ * 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 + * ImageLoader.load(). + *

+ * + * @param filename the name of the file to load the image from (must not be null) + * + * @exception IllegalArgumentException + * @exception SWTException + */ + 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. + *

+ * This method is for internal use, and is not described further. + *

+ */ + 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. + *

+ * This method is for internal use, and is not described further. + *

+ */ + 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. + *

+ * IMPORTANT: This method is not part of the public + * API for ImageData. 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. + *

+ *

+ * This method is for internal use, and is not described further. + *

+ */ + 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 copies of + * the values in the slots of the receiver. That is, the + * returned object is a deep copy 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 x in + * scanline y 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 + */ + 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 getWidth alpha values starting at offset + * x in scanline y in the receiver's alpha + * data starting at startIndex. + * + * @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 + */ + 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 x in + * scanline y 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 + * @exception SWTException + */ + 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 getWidth pixel values starting at offset + * x in scanline y in the receiver's + * data starting at startIndex. + * + * @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 + * @exception SWTException + */ + 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 getWidth pixel values starting at offset + * x in scanline y in the receiver's + * data starting at startIndex. + * + * @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 + * @exception SWTException + */ + 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 RGBs 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 ImageData 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 x in + * scanline y 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 + */ + 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 x in + * scanline y in the receiver's alpha data to the + * values from the array alphas starting at + * startIndex. + * + * @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 + */ + 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 x in + * scanline y 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 + * @exception SWTException + */ + 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 x in + * scanline y in the receiver's data to the + * values from the array pixels starting at + * startIndex. + * + * @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 + * @exception SWTException + */ + 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 x in + * scanline y in the receiver's data to the + * values from the array pixels starting at + * startIndex. + * + * @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 + * @exception SWTException + */ + 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. + *

+ * 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. + *

+ * + * @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. + *

+ * Note: The source and destination red, green, and blue + * arrays may be null if no alpha blending or dither is to be + * performed. + *

+ * + * @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. + *

+ * Note: The source and destination masks and palettes must + * always be fully specified. + *

+ * + * @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. + *

+ * Note: The source and destination masks and palettes must + * always be fully specified. + *

+ * + * @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 vertical 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. + *

+ * This is a GC helper. + *

+ * + * @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 buf. 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 length bytes from this LEDataInputStream and + * stores them in byte array buffer starting at offset. + *

+ * 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 count. + *

+ * @param buffer the byte array in which to store the read bytes. + * @param offset the offset in buffer to store the read bytes. + * @param length the maximum number of bytes to store in buffer. + * + * @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 b. + *

+ * 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 b, + * an IOException will be thrown and no byte will be pushed back. + *

+ * + * @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); + } + } + } +}