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); } } } }