/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.swt.graphics;
import java.io.*;
import org.eclipse.swt.*;
import org.eclipse.swt.internal.CloneableCompatibility;
/**
* 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.
*
* 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
IMAGE_BMP_RLE
IMAGE_GIF
IMAGE_ICO
IMAGE_JPEG
IMAGE_PNG
DM_UNSPECIFIED
DM_FILL_NONE
DM_FILL_BACKGROUND
DM_FILL_PREVIOUS
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()
.
*
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()
.
*
* 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 offsetx
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 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 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 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 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 RGB
s which comprise the
* indexed color table of the receiver, or null if the receiver
* has a direct color model.
*
* @return the RGB values for the image or null if direct color
*
* @see PaletteData#getRGBs()
*/
public RGB[] getRGBs() {
return palette.getRGBs();
}
/**
* Returns an 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 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 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 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 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 * 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. Ifvertical
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(); } }