/*
* Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
package javax.media.j3d;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.logging.Level;
/**
* Abstract class that is used to define 2D or 3D ImageComponent classes
* used in a Java 3D scene graph.
* This is used for texture images, background images and raster components
* of Shape3D nodes.
*/
abstract class ImageComponentRetained extends NodeComponentRetained {
// change flag
static final int IMAGE_CHANGED = 0x01;
static final int SUBIMAGE_CHANGED = 0x02;
static final int TYPE_BYTE_BGR = 0x1;
static final int TYPE_BYTE_RGB = 0x2;
static final int TYPE_BYTE_ABGR = 0x4;
static final int TYPE_BYTE_RGBA = 0x8;
static final int TYPE_BYTE_LA = 0x10;
static final int TYPE_BYTE_GRAY = 0x20;
static final int TYPE_USHORT_GRAY = 0x40;
static final int TYPE_INT_BGR = 0x80;
static final int TYPE_INT_RGB = 0x100;
static final int TYPE_INT_ARGB = 0x200;
static final int IMAGE_SIZE_512X512 = 262144;
enum ImageFormatType {
TYPE_UNKNOWN,
TYPE_BYTE_BGR,
TYPE_BYTE_RGB,
TYPE_BYTE_ABGR,
TYPE_BYTE_RGBA,
TYPE_BYTE_LA,
TYPE_BYTE_GRAY,
TYPE_USHORT_GRAY,
TYPE_INT_BGR,
TYPE_INT_RGB,
TYPE_INT_ARGB
}
static final int IMAGE_DATA_TYPE_BYTE_ARRAY = 0x1000;
static final int IMAGE_DATA_TYPE_INT_ARRAY = 0x2000;
static final int IMAGE_DATA_TYPE_BYTE_BUFFER = 0x4000;
static final int IMAGE_DATA_TYPE_INT_BUFFER = 0x8000;
enum ImageDataType {
TYPE_NULL,
TYPE_BYTE_ARRAY,
TYPE_INT_ARRAY,
TYPE_BYTE_BUFFER,
TYPE_INT_BUFFER
}
private int apiFormat; // The format set by user.
int width; // Width of PixelArray
int height; // Height of PixelArray
int depth; // Depth of PixelArray
boolean byReference = false; // Is the imageComponent by reference
boolean yUp = false;
boolean imageTypeIsSupported;
boolean abgrSupported = true;
boolean npotSupported = true;
private int unitsPerPixel;
private int numberOfComponents;
// Note : This is unuse for NioImageBuffer.
// The image type of the input image. Using the constant in BufferedImage
private int imageType;
private ImageFormatType imageFormatType = ImageFormatType.TYPE_UNKNOWN;
ImageData imageData;
private ImageComponent.ImageClass imageClass = ImageComponent.ImageClass.BUFFERED_IMAGE;
// To support Non power of 2 (NPOT) image
// if enforceNonPowerOfTwoSupport is true (for examples Raster and Background)
// and imageData is a non power of 2 image
// and graphics driver doesn't support NPOT extension.
private ImageData imageDataPowerOfTwo;
private AffineTransformOp powerOfTwoATOp;
// The following flag means that if the image is non-power-of-two and the
// card doesn't support NPOT texture, we will scale the image to a power
// of two.
private boolean enforceNonPowerOfTwoSupport = false;
private boolean usedByOffScreenCanvas = false;
// This will store the referenced Images for reference case.
// private RenderedImage refImage[] = null;
private Object refImage[] = null;
// Issue 366: Lock for evaluateExtensions
Object evaluateExtLock = new Object();
// Lock used in the "by ref case"
GeometryLock geomLock = new GeometryLock();
int tilew = 0;
int tileh = 0;
int numXTiles = 0;
int numYTiles = 0;
// lists of Node Components that are referencing this ImageComponent
// object. This list is used to notify the referencing node components
// of any changes of this ImageComponent.
private ArrayList<NodeComponentRetained> userList = new ArrayList<NodeComponentRetained>();
/**
* Retrieves the width of this image component object.
* @return the width of this image component object
*/
int getWidth() {
return width;
}
/**
* Retrieves the height of this image component object.
* @return the height of this image component object
*/
int getHeight() {
return height;
}
/**
* Retrieves the apiFormat of this image component object.
*
* @return the apiFormat of this image component object
*/
int getFormat() {
return apiFormat;
}
void setFormat(int format) {
this.apiFormat = format;
}
void setByReference(boolean byReference) {
this.byReference = byReference;
}
boolean isByReference() {
return byReference;
}
void setYUp( boolean yUp) {
this.yUp = yUp;
}
boolean isYUp() {
return yUp;
}
int getUnitsPerPixel() {
return unitsPerPixel;
}
void setUnitsPerPixel(int ipp) {
unitsPerPixel = ipp;
}
ImageComponent.ImageClass getImageClass() {
return imageClass;
}
void setImageClass(RenderedImage image) {
if(image instanceof BufferedImage) {
imageClass = ImageComponent.ImageClass.BUFFERED_IMAGE;
} else {
imageClass = ImageComponent.ImageClass.RENDERED_IMAGE;
}
}
void setImageClass(NioImageBuffer image) {
imageClass = ImageComponent.ImageClass.NIO_IMAGE_BUFFER;
}
void setEnforceNonPowerOfTwoSupport(boolean npot) {
this.enforceNonPowerOfTwoSupport = npot;
}
void setUsedByOffScreen(boolean used) {
usedByOffScreenCanvas = used;
}
boolean getUsedByOffScreen() {
return usedByOffScreenCanvas;
}
int getNumberOfComponents() {
return numberOfComponents;
}
void setNumberOfComponents(int numberOfComponents) {
this.numberOfComponents = numberOfComponents;
}
int getImageDataTypeIntValue() {
int idtValue = -1;
switch(imageData.imageDataType) {
case TYPE_BYTE_ARRAY:
idtValue = IMAGE_DATA_TYPE_BYTE_ARRAY;
break;
case TYPE_INT_ARRAY:
idtValue = IMAGE_DATA_TYPE_INT_ARRAY;
break;
case TYPE_BYTE_BUFFER:
idtValue = IMAGE_DATA_TYPE_BYTE_BUFFER;
break;
case TYPE_INT_BUFFER:
idtValue = IMAGE_DATA_TYPE_INT_BUFFER;
break;
default :
assert false;
}
return idtValue;
}
int getImageFormatTypeIntValue(boolean powerOfTwoData) {
int iftValue = -1;
switch(imageFormatType) {
case TYPE_BYTE_BGR:
iftValue = TYPE_BYTE_BGR;
break;
case TYPE_BYTE_RGB:
iftValue = TYPE_BYTE_RGB;
break;
case TYPE_BYTE_ABGR:
iftValue = TYPE_BYTE_ABGR;
break;
case TYPE_BYTE_RGBA:
if((imageDataPowerOfTwo != null) && (powerOfTwoData)) {
iftValue = TYPE_BYTE_ABGR;
}
else {
iftValue = TYPE_BYTE_RGBA;
}
break;
case TYPE_BYTE_LA:
iftValue = TYPE_BYTE_LA;
break;
case TYPE_BYTE_GRAY:
iftValue = TYPE_BYTE_GRAY;
break;
case TYPE_USHORT_GRAY:
iftValue = TYPE_USHORT_GRAY;
break;
case TYPE_INT_BGR:
iftValue = TYPE_INT_BGR;
break;
case TYPE_INT_RGB:
iftValue = TYPE_INT_RGB;
break;
case TYPE_INT_ARGB:
iftValue = TYPE_INT_ARGB;
break;
default:
throw new AssertionError();
}
return iftValue;
}
// Note: This method for RenderedImage, can't be used by NioImageBuffer.
int getImageType() {
return imageType;
}
void setImageFormatType(ImageFormatType ift) {
this.imageFormatType = ift;
}
ImageFormatType getImageFormatType() {
return this.imageFormatType;
}
void setRefImage(Object image, int index) {
this.refImage[index] = image;
}
Object getRefImage(int index) {
return this.refImage[index];
}
ImageData getImageData(boolean npotSupportNeeded) {
if(npotSupportNeeded) {
assert enforceNonPowerOfTwoSupport;
if(imageDataPowerOfTwo != null) {
return imageDataPowerOfTwo;
}
}
return imageData;
}
boolean useBilinearFilter() {
if(imageDataPowerOfTwo != null) {
return true;
}
return false;
}
boolean isImageTypeSupported() {
return imageTypeIsSupported;
}
/**
* Check if ImageComponent parameters have valid values.
*/
void processParams(int format, int width, int height, int depth) {
if (width < 1)
throw new IllegalArgumentException(J3dI18N.getString("ImageComponentRetained0"));
if (height < 1)
throw new IllegalArgumentException(J3dI18N.getString("ImageComponentRetained1"));
if (depth < 1)
throw new IllegalArgumentException(J3dI18N.getString("ImageComponentRetained2"));
// If the format is 8bit per component, we may send it down
// to OpenGL directly if its by ref case
switch (format) {
case ImageComponent.FORMAT_RGB:// same as ImageComponent.FORMAT_RGB8
case ImageComponent.FORMAT_RGB4: // Need to be Deprecated
case ImageComponent.FORMAT_RGB5: // Need to be Deprecated
case ImageComponent.FORMAT_R3_G3_B2: // Need to be Deprecated
numberOfComponents = 3;
break;
case ImageComponent.FORMAT_RGBA:// same as ImageComponent.FORMAT_RGBA8
case ImageComponent.FORMAT_RGB5_A1: // Need to be Deprecated
case ImageComponent.FORMAT_RGBA4: // Need to be Deprecated
numberOfComponents = 4;
break;
case ImageComponent.FORMAT_LUM4_ALPHA4: // Need to be Deprecated
case ImageComponent.FORMAT_LUM8_ALPHA8:
numberOfComponents = 2;
break;
case ImageComponent.FORMAT_CHANNEL8:
numberOfComponents = 1;
break;
default:
throw new IllegalArgumentException(J3dI18N.getString("ImageComponentRetained3"));
}
this.setFormat(format);
this.width = width;
this.height = height;
this.depth = depth;
refImage = new Object[depth];
}
int evaluateImageType(RenderedImage ri) {
int imageType = BufferedImage.TYPE_CUSTOM;
if (ri instanceof BufferedImage) {
imageType = ((BufferedImage)ri).getType();
if(imageType != BufferedImage.TYPE_CUSTOM) {
return imageType;
}
}
else {
// Fix to Issue 412. Force copy for RenderedImage of type not equal to BufferedImage.
return imageType;
}
// System.err.println("This is a RenderedImage or BufferedImage with TYPE_CUSTOM. It imageType classification may not be correct.");
ColorModel cm = ri.getColorModel();
ColorSpace cs = cm.getColorSpace();
SampleModel sm = ri.getSampleModel();
int csType = cs.getType();
boolean isAlphaPre = cm.isAlphaPremultiplied();
if (csType == ColorSpace.TYPE_GRAY && cm instanceof ComponentColorModel) {
if (sm.getDataType() == DataBuffer.TYPE_BYTE) {
imageType = BufferedImage.TYPE_BYTE_GRAY;
} else if (sm.getDataType() == DataBuffer.TYPE_USHORT) {
imageType = BufferedImage.TYPE_USHORT_GRAY;
}
}
// RGB , only interested in BYTE ABGR and BGR for now
// all others will be copied to a buffered image
else if(csType == ColorSpace.TYPE_RGB) {
int comparedBit = 0;
int smDataType = sm.getDataType();
if(smDataType == DataBuffer.TYPE_BYTE) {
comparedBit = 8;
} else if(smDataType == DataBuffer.TYPE_INT) {
comparedBit = 32;
}
if(comparedBit != 0) {
int numBands = sm.getNumBands();
if (cm instanceof ComponentColorModel &&
sm instanceof PixelInterleavedSampleModel) {
PixelInterleavedSampleModel csm =
(PixelInterleavedSampleModel) sm;
int[] offs = csm.getBandOffsets();
ComponentColorModel ccm = (ComponentColorModel)cm;
int[] nBits = ccm.getComponentSize();
boolean isNBit = true;
for (int i=0; i < numBands; i++) {
if (nBits[i] != comparedBit) {
isNBit = false;
break;
}
}
// Handle TYPE_BYTE
if( comparedBit == 8) {
if (isNBit &&
offs[0] == numBands-1 &&
offs[1] == numBands-2 &&
offs[2] == numBands-3) {
if (numBands == 3) {
imageType = BufferedImage.TYPE_3BYTE_BGR;
} else if (offs[3] == 0) {
imageType = (isAlphaPre
? BufferedImage.TYPE_4BYTE_ABGR_PRE
: BufferedImage.TYPE_4BYTE_ABGR);
}
}
}
//Handle TYPE_INT
else {
if (isNBit) {
if (numBands == 3) {
if(offs[0] == numBands-1 &&
offs[1] == numBands-2 &&
offs[2] == numBands-3) {
imageType = BufferedImage.TYPE_INT_BGR;
} else if(offs[0] == 0 &&
offs[1] == 1 &&
offs[2] == 2) {
imageType = BufferedImage.TYPE_INT_RGB;
}
} else if(offs[0] == 3 &&
offs[1] == 0 &&
offs[2] == 1 &&
offs[3] == 2) {
imageType = (isAlphaPre
? BufferedImage.TYPE_INT_ARGB_PRE
: BufferedImage.TYPE_INT_ARGB);
}
}
}
}
}
}
return imageType;
}
// Assume ri's imageType is BufferedImage.TYPE_CUSTOM
boolean is3ByteRGB(RenderedImage ri) {
boolean value = false;
int i;
ColorModel cm = ri.getColorModel();
ColorSpace cs = cm.getColorSpace();
SampleModel sm = ri.getSampleModel();
boolean isAlphaPre = cm.isAlphaPremultiplied();
int csType = cs.getType();
if ( csType == ColorSpace.TYPE_RGB) {
int numBands = sm.getNumBands();
if ((numBands == 3) && (sm.getDataType() == DataBuffer.TYPE_BYTE)) {
if (cm instanceof ComponentColorModel &&
sm instanceof PixelInterleavedSampleModel) {
PixelInterleavedSampleModel csm =
(PixelInterleavedSampleModel) sm;
int[] offs = csm.getBandOffsets();
ComponentColorModel ccm = (ComponentColorModel)cm;
int[] nBits = ccm.getComponentSize();
boolean is8Bit = true;
for (i=0; i < numBands; i++) {
if (nBits[i] != 8) {
is8Bit = false;
break;
}
}
if (is8Bit &&
offs[0] == 0 &&
offs[1] == 1 &&
offs[2] == 2) {
value = true;
}
}
}
}
return value;
}
// Assume ri's imageType is BufferedImage.TYPE_CUSTOM
boolean is4ByteRGBA(RenderedImage ri) {
boolean value = false;
int i;
ColorModel cm = ri.getColorModel();
ColorSpace cs = cm.getColorSpace();
SampleModel sm = ri.getSampleModel();
boolean isAlphaPre = cm.isAlphaPremultiplied();
int csType = cs.getType();
if ( csType == ColorSpace.TYPE_RGB) {
int numBands = sm.getNumBands();
if ((numBands == 4) && (sm.getDataType() == DataBuffer.TYPE_BYTE)) {
if (cm instanceof ComponentColorModel &&
sm instanceof PixelInterleavedSampleModel) {
PixelInterleavedSampleModel csm =
(PixelInterleavedSampleModel) sm;
int[] offs = csm.getBandOffsets();
ComponentColorModel ccm = (ComponentColorModel)cm;
int[] nBits = ccm.getComponentSize();
boolean is8Bit = true;
for (i=0; i < numBands; i++) {
if (nBits[i] != 8) {
is8Bit = false;
break;
}
}
if (is8Bit &&
offs[0] == 0 &&
offs[1] == 1 &&
offs[2] == 2 &&
offs[3] == 3 && !isAlphaPre) {
value = true;
}
}
}
}
return value;
}
// Note: This method for RenderedImage, can't be used by NioImageBuffer.
/* Check if sub-image type matches image type */
boolean isSubImageTypeEqual(RenderedImage ri) {
int subImageType = evaluateImageType(ri);
// This test is likely too loose, but the specification isn't clear either.
// Assuming TYPE_CUSTOM of sub-image == the TYPE_CUSTOM of existing image.
if(imageType == subImageType) {
return true;
} else {
return false;
}
}
// This method only support caller of offScreenBuffer and readRaster.
void createBlankImageData() {
assert (imageData == null);
switch(numberOfComponents) {
case 4:
imageType = BufferedImage.TYPE_INT_ARGB;
imageFormatType = ImageFormatType.TYPE_INT_ARGB;
unitsPerPixel = 1;
break;
case 3:
imageType = BufferedImage.TYPE_INT_RGB;
imageFormatType = ImageFormatType.TYPE_INT_RGB;
unitsPerPixel = 1;
break;
default:
// Only valid for 3 and 4 channel case. ( Read back from framebuffer )
assert false;
}
imageTypeIsSupported = true;
imageData = createRenderedImageDataObject(null);
}
// This method will set imageType, imageFormatType, and unitsPerPixel
// as it evaluates NioImageBuffer is supported. It will also reset
// abgrSupported.
boolean isImageTypeSupported(NioImageBuffer nioImgBuf) {
boolean isSupported = true;
NioImageBuffer.ImageType nioImageType = nioImgBuf.getImageType();
switch(numberOfComponents) {
case 4:
switch(nioImageType) {
case TYPE_4BYTE_ABGR:
// TODO : This approach will lead to a very slow path
// for unsupported case.
if(abgrSupported) {
imageFormatType = ImageFormatType.TYPE_BYTE_ABGR;
} else {
// Unsupported format on HW, switch to slow copy.
imageFormatType = ImageFormatType.TYPE_BYTE_RGBA;
isSupported = false;
}
unitsPerPixel = 4;
break;
case TYPE_4BYTE_RGBA:
imageFormatType = ImageFormatType.TYPE_BYTE_RGBA;
unitsPerPixel = 4;
break;
case TYPE_INT_ARGB:
imageFormatType = ImageFormatType.TYPE_INT_ARGB;
unitsPerPixel = 1;
break;
default:
throw new IllegalArgumentException(J3dI18N.getString("ImageComponent5"));
}
break;
case 3:
switch(nioImageType) {
case TYPE_3BYTE_BGR:
imageFormatType = ImageFormatType.TYPE_BYTE_BGR;
unitsPerPixel = 3;
break;
case TYPE_3BYTE_RGB:
imageFormatType = ImageFormatType.TYPE_BYTE_RGB;
unitsPerPixel = 3;
break;
case TYPE_INT_BGR:
imageFormatType = ImageFormatType.TYPE_INT_BGR;
unitsPerPixel = 1;
break;
case TYPE_INT_RGB:
imageFormatType = ImageFormatType.TYPE_INT_RGB;
unitsPerPixel = 1;
break;
default:
throw new IllegalArgumentException(J3dI18N.getString("ImageComponent5"));
}
break;
case 2:
throw new IllegalArgumentException(J3dI18N.getString("ImageComponent5"));
case 1:
if(nioImageType == NioImageBuffer.ImageType.TYPE_BYTE_GRAY) {
imageFormatType = ImageFormatType.TYPE_BYTE_GRAY;
unitsPerPixel = 1;
} else {
throw new IllegalArgumentException(J3dI18N.getString("ImageComponent5"));
}
break;
default:
throw new AssertionError();
}
return isSupported;
}
// This method will set imageType, imageFormatType, and unitsPerPixel
// as it evaluates RenderedImage is supported. It will also reset
// abgrSupported.
boolean isImageTypeSupported(RenderedImage ri) {
boolean isSupported = true;
imageType = evaluateImageType(ri);
switch(numberOfComponents) {
case 4:
if(imageType == BufferedImage.TYPE_4BYTE_ABGR) {
// TODO : This approach will lead to a very slow path
// for unsupported case.
if(abgrSupported) {
imageFormatType = ImageFormatType.TYPE_BYTE_ABGR;
} else {
// Unsupported format on HW, switch to slow copy.
imageFormatType = ImageFormatType.TYPE_BYTE_RGBA;
isSupported = false;
}
unitsPerPixel = 4;
} else if(imageType == BufferedImage.TYPE_INT_ARGB) {
imageFormatType = ImageFormatType.TYPE_INT_ARGB;
unitsPerPixel = 1;
} else if(is4ByteRGBA(ri)) {
imageFormatType = ImageFormatType.TYPE_BYTE_RGBA;
unitsPerPixel = 4;
} else {
// System.err.println("Image format is unsupported --- Case 4");
// Convert unsupported format to TYPE_BYTE_RGBA.
imageFormatType = ImageFormatType.TYPE_BYTE_RGBA;
isSupported = false;
unitsPerPixel = 4;
}
break;
case 3:
if(imageType == BufferedImage.TYPE_3BYTE_BGR) {
imageFormatType = ImageFormatType.TYPE_BYTE_BGR;
unitsPerPixel = 3;
} else if(imageType == BufferedImage.TYPE_INT_BGR) {
imageFormatType = ImageFormatType.TYPE_INT_BGR;
unitsPerPixel = 1;
} else if(imageType == BufferedImage.TYPE_INT_RGB) {
imageFormatType = ImageFormatType.TYPE_INT_RGB;
unitsPerPixel = 1;
} else if(is3ByteRGB(ri)) {
imageFormatType = ImageFormatType.TYPE_BYTE_RGB;
unitsPerPixel = 3;
} else {
// System.err.println("Image format is unsupported --- Case 3");
// Convert unsupported format to TYPE_BYTE_RGB.
imageFormatType = ImageFormatType.TYPE_BYTE_RGB;
isSupported = false;
unitsPerPixel = 3;
}
break;
case 2:
// System.err.println("Image format is unsupported --- Case 2");
// Convert unsupported format to TYPE_BYTE_LA.
imageFormatType = ImageFormatType.TYPE_BYTE_LA;
isSupported = false;
unitsPerPixel = 2;
break;
case 1:
if(imageType == BufferedImage.TYPE_BYTE_GRAY) {
imageFormatType = ImageFormatType.TYPE_BYTE_GRAY;
unitsPerPixel = 1;
} else {
// System.err.println("Image format is unsupported --- Case 1");
// Convert unsupported format to TYPE_BYTE_GRAY.
imageFormatType = ImageFormatType.TYPE_BYTE_GRAY;
isSupported = false;
unitsPerPixel = 1;
}
break;
default:
throw new AssertionError();
}
return isSupported;
}
/*
* This method assume that the following members have been initialized :
* width, height, depth, imageFormatType, and unitsPerPixel.
*/
ImageData createNioImageBufferDataObject(NioImageBuffer nioImageBuffer) {
switch(imageFormatType) {
case TYPE_BYTE_GRAY:
case TYPE_BYTE_LA:
case TYPE_BYTE_RGB:
case TYPE_BYTE_BGR:
case TYPE_BYTE_RGBA:
case TYPE_BYTE_ABGR:
if(nioImageBuffer != null) {
return new ImageData(ImageDataType.TYPE_BYTE_BUFFER,
width * height * depth * unitsPerPixel,
width, height, nioImageBuffer);
} else {
// This is needed only if abgr is unsupported.
return new ImageData(ImageDataType.TYPE_BYTE_BUFFER,
width * height * depth * unitsPerPixel,
width, height);
}
case TYPE_INT_RGB:
case TYPE_INT_BGR:
case TYPE_INT_ARGB:
return new ImageData(ImageDataType.TYPE_INT_BUFFER,
width * height * depth * unitsPerPixel,
width, height, nioImageBuffer);
default:
throw new AssertionError();
}
}
/*
* This method assume that the following members have been initialized :
* depth, imageType, imageFormatType, and unitsPerPixel.
*/
ImageData createRenderedImageDataObject(RenderedImage byRefImage, int dataWidth, int dataHeight) {
switch(imageFormatType) {
case TYPE_BYTE_GRAY:
case TYPE_BYTE_LA:
case TYPE_BYTE_RGB:
case TYPE_BYTE_BGR:
case TYPE_BYTE_RGBA:
case TYPE_BYTE_ABGR:
if(byRefImage != null) {
return new ImageData(ImageDataType.TYPE_BYTE_ARRAY,
dataWidth * dataHeight * depth * unitsPerPixel,
dataWidth, dataHeight, byRefImage);
} else {
return new ImageData(ImageDataType.TYPE_BYTE_ARRAY,
dataWidth * dataHeight * depth * unitsPerPixel,
dataWidth, dataHeight);
}
case TYPE_INT_RGB:
case TYPE_INT_BGR:
case TYPE_INT_ARGB:
if(byRefImage != null) {
return new ImageData(ImageDataType.TYPE_INT_ARRAY,
dataWidth * dataHeight * depth * unitsPerPixel,
dataWidth, dataHeight, byRefImage);
} else {
return new ImageData(ImageDataType.TYPE_INT_ARRAY,
dataWidth * dataHeight * depth * unitsPerPixel,
dataWidth, dataHeight);
}
default:
throw new AssertionError();
}
}
private void updateImageDataPowerOfTwo(int depthIndex) {
assert enforceNonPowerOfTwoSupport;
BufferedImage bufImage = imageData.createBufferedImage(depthIndex);
BufferedImage scaledImg = powerOfTwoATOp.filter(bufImage, null);
copySupportedImageToImageData(scaledImg, 0, imageDataPowerOfTwo);
}
/*
* This method assume that the following members have been initialized :
* width, height, depth, imageType, imageFormatType, and bytesPerPixel.
*/
ImageData createRenderedImageDataObject(RenderedImage byRefImage) {
return createRenderedImageDataObject(byRefImage, width, height);
}
/**
* Copy specified region of image data from RenderedImage to
* ImageComponent's imageData object
*/
void copySupportedImageToImageData(RenderedImage ri, int srcX, int srcY,
int dstX, int dstY, int depthIndex, int copyWidth, int copyHeight, ImageData data) {
assert (data != null);
ColorModel cm = ri.getColorModel();
int xoff = ri.getTileGridXOffset(); // tile origin x offset
int yoff = ri.getTileGridYOffset(); // tile origin y offset
int minTileX = ri.getMinTileX(); // min tile x index
int minTileY = ri.getMinTileY(); // min tile y index
tilew = ri.getTileWidth(); // tile width in pixels
tileh = ri.getTileHeight(); // tile height in pixels
// determine the first tile of the image
float mt;
mt = (float)(srcX - xoff) / (float)tilew;
if (mt < 0) {
minTileX = (int)(mt - 1);
} else {
minTileX = (int)mt;
}
mt = (float)(srcY - yoff) / (float)tileh;
if (mt < 0) {
minTileY = (int)(mt - 1);
} else {
minTileY = (int)mt;
}
// determine the pixel offset of the upper-left corner of the
// first tile
int startXTile = minTileX * tilew + xoff;
int startYTile = minTileY * tileh + yoff;
// image dimension in the first tile
int curw = (startXTile + tilew - srcX);
int curh = (startYTile + tileh - srcY);
// check if the to-be-copied region is less than the tile image
// if so, update the to-be-copied dimension of this tile
if (curw > copyWidth) {
curw = copyWidth;
}
if (curh > copyHeight) {
curh = copyHeight;
}
// save the to-be-copied width of the left most tile
int startw = curw;
// temporary variable for dimension of the to-be-copied region
int tmpw = copyWidth;
int tmph = copyHeight;
// offset of the first pixel of the tile to be copied; offset is
// relative to the upper left corner of the title
int x = srcX - startXTile;
int y = srcY - startYTile;
// determine the number of tiles in each direction that the
// image spans
numXTiles = (copyWidth + x) / tilew;
numYTiles = (copyHeight + y) / tileh;
if (((float)(copyWidth + x ) % (float)tilew) > 0) {
numXTiles += 1;
}
if (((float)(copyHeight + y ) % (float)tileh) > 0) {
numYTiles += 1;
}
int offset;
int w, h, i, j, m, n;
int dstBegin;
Object pixel = null;
java.awt.image.Raster ras;
int lineUnits; // nbytes per line in dst image buffer
int sign; // -1 for going down
int dstLineUnits; // sign * lineUnits
int tileStart; // destination buffer offset
// at the next left most tile
byte[] dstByteBuffer = null;
int[] dstIntBuffer = null;
switch(data.getType()) {
case TYPE_BYTE_ARRAY:
dstByteBuffer = data.getAsByteArray();
break;
case TYPE_INT_ARRAY:
dstIntBuffer = data.getAsIntArray();
break;
default:
assert false;
}
int dataWidth = data.dataWidth;
int dataHeight = data.dataHeight;
lineUnits = dataWidth * unitsPerPixel;
if (yUp) {
// destination buffer offset
tileStart = (depthIndex * dataWidth * dataHeight + dstY * dataWidth + dstX) * unitsPerPixel;
sign = 1;
dstLineUnits = lineUnits;
} else {
// destination buffer offset
tileStart = (depthIndex * dataWidth * dataHeight + (dataHeight - dstY - 1) * dataWidth + dstX) * unitsPerPixel;
sign = -1;
dstLineUnits = -lineUnits;
}
/*
System.err.println("tileStart= " + tileStart + " dstLineUnits= " + dstLineUnits);
System.err.println("startw= " + startw);
*/
// allocate memory for a pixel
ras = ri.getTile(minTileX,minTileY);
pixel = getDataElementBuffer(ras);
int srcOffset, dstOffset;
int tileLineUnits = tilew * unitsPerPixel;
int copyUnits;
for (n = minTileY; n < minTileY+numYTiles; n++) {
dstBegin = tileStart; // destination buffer offset
tmpw = copyWidth; // reset the width to be copied
curw = startw; // reset the width to be copied of
// the left most tile
x = srcX - startXTile; // reset the starting x offset of
// the left most tile
for (m = minTileX; m < minTileX+numXTiles; m++) {
// retrieve the raster for the next tile
ras = ri.getTile(m,n);
srcOffset = (y * tilew + x) * unitsPerPixel;
dstOffset = dstBegin;
copyUnits = curw * unitsPerPixel;
//System.err.println("curh = "+curh+" curw = "+curw);
//System.err.println("x = "+x+" y = "+y);
switch(data.getType()) {
case TYPE_BYTE_ARRAY:
byte[] srcByteBuffer = ((DataBufferByte)ras.getDataBuffer()).getData();
for (h = 0; h < curh; h++) {
System.arraycopy(srcByteBuffer, srcOffset, dstByteBuffer, dstOffset,
copyUnits);
srcOffset += tileLineUnits;
dstOffset += dstLineUnits;
}
break;
case TYPE_INT_ARRAY:
int[] srcIntBuffer = ((DataBufferInt)ras.getDataBuffer()).getData();
for (h = 0; h < curh; h++) {
System.arraycopy(srcIntBuffer, srcOffset, dstIntBuffer, dstOffset,
copyUnits);
srcOffset += tileLineUnits;
dstOffset += dstLineUnits;
}
break;
default:
assert false;
}
// advance the destination buffer offset
dstBegin += curw * unitsPerPixel;
// move to the next tile in x direction
x = 0;
// determine the width of copy region of the next tile
tmpw -= curw;
if (tmpw < tilew) {
curw = tmpw;
} else {
curw = tilew;
}
}
// we are done copying an array of tiles in the x direction
// advance the tileStart offset
tileStart += dataWidth * unitsPerPixel * curh * sign;
// move to the next set of tiles in y direction
y = 0;
// determine the height of copy region for the next set
// of tiles
tmph -= curh;
if (tmph < tileh) {
curh = tmph;
} else {
curh = tileh;
}
}
if((imageData == data) && (imageDataPowerOfTwo != null)) {
updateImageDataPowerOfTwo(depthIndex);
}
}
// Quick line by line copy
void copyImageLineByLine(BufferedImage bi, int srcX, int srcY,
int dstX, int dstY, int depthIndex, int copyWidth, int copyHeight, ImageData data) {
assert (data != null);
int h;
int rowBegin, // src begin row index
srcBegin, // src begin offset
dstBegin; // dst begin offset
int dataWidth = data.dataWidth;
int dataHeight = data.dataHeight;
int dstUnitsPerRow = dataWidth * unitsPerPixel; // bytes per row in dst image
rowBegin = srcY;
if (yUp) {
dstBegin = (depthIndex * dataWidth * dataHeight + dstY * dataWidth + dstX) * unitsPerPixel;
} else {
dstBegin = (depthIndex * dataWidth * dataHeight + (dataHeight - dstY - 1) * dataWidth + dstX) * unitsPerPixel;
dstUnitsPerRow = - 1 * dstUnitsPerRow;
}
int copyUnits = copyWidth * unitsPerPixel;
int srcWidth = bi.getWidth();
int srcUnitsPerRow = srcWidth * unitsPerPixel;
srcBegin = (rowBegin * srcWidth + srcX) * unitsPerPixel;
switch(data.getType()) {
case TYPE_BYTE_ARRAY:
byte[] srcByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
byte[] dstByteBuffer = data.getAsByteArray();
for (h = 0; h < copyHeight; h++) {
System.arraycopy(srcByteBuffer, srcBegin, dstByteBuffer, dstBegin, copyUnits);
dstBegin += dstUnitsPerRow;
srcBegin += srcUnitsPerRow;
}
break;
case TYPE_INT_ARRAY:
int[] srcIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
int[] dstIntBuffer = data.getAsIntArray();
for (h = 0; h < copyHeight; h++) {
System.arraycopy(srcIntBuffer, srcBegin, dstIntBuffer, dstBegin, copyUnits);
dstBegin += dstUnitsPerRow;
srcBegin += srcUnitsPerRow;
}
break;
default:
assert false;
}
if((imageData == data) && (imageDataPowerOfTwo != null)) {
updateImageDataPowerOfTwo(depthIndex);
}
}
// Quick block copy for yUp image
void copyImageByBlock(BufferedImage bi, int depthIndex, ImageData data) {
assert ((data != null) && yUp);
int dataWidth = data.dataWidth;
int dataHeight = data.dataHeight;
int dstBegin; // dst begin offset
dstBegin = depthIndex * dataWidth * dataHeight * unitsPerPixel;
switch(imageData.getType()) {
case TYPE_BYTE_ARRAY:
byte[] srcByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
byte[] dstByteBuffer = data.getAsByteArray();
System.arraycopy(srcByteBuffer, 0, dstByteBuffer, dstBegin, (dataWidth * dataHeight * unitsPerPixel));
break;
case TYPE_INT_ARRAY:
int[] srcIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
int[] dstIntBuffer = data.getAsIntArray();
System.arraycopy(srcIntBuffer, 0, dstIntBuffer, dstBegin, (dataWidth * dataHeight * unitsPerPixel));
break;
default:
assert false;
}
if((imageData == data) && (imageDataPowerOfTwo != null)) {
updateImageDataPowerOfTwo(depthIndex);
}
}
/**
* copy complete region of a RenderedImage to ImageComponent's imageData object.
*/
void copySupportedImageToImageData(RenderedImage ri, int depthIndex, ImageData data) {
if (ri instanceof BufferedImage) {
if(yUp) {
/* Use quick block copy when ( format is OK, Yup is true, and byRef is false). */
// System.err.println("ImageComponentRetained.copySupportedImageToImageData() : (imageTypeSupported && !byReference && yUp) --- (2 BI)");
copyImageByBlock((BufferedImage)ri, depthIndex, data);
} else {
/* Use quick inverse line by line copy when (format is OK and Yup is false). */
// System.err.println("ImageComponentRetained.copySupportedImageToImageData() : (imageTypeSupported && !yUp) --- (3 BI)");
copyImageLineByLine((BufferedImage)ri, 0, 0, 0, 0, depthIndex, data.dataWidth, data.dataHeight, data);
}
} else {
// System.err.println("ImageComponentRetained.copySupportedImageToImageData() : (imageTypeSupported && !byReference ) --- (2 RI)");
copySupportedImageToImageData(ri, ri.getMinX(), ri.getMinY(), 0, 0, depthIndex, data.dataWidth, data.dataHeight, data);
/*
* An alternative approach.
*
// Create a buffered image from renderImage
ColorModel cm = ri.getColorModel();
WritableRaster wRaster = ri.copyData(null);
BufferedImage bi = new BufferedImage(cm,
wRaster,
cm.isAlphaPremultiplied()
,null);
copySupportedImageToImageData((BufferedImage)ri, 0, 0, 0, 0, depthIndex, data.dataWidth, data.dataHeight, data);
*
*
*/
}
}
/*
* copy the complete unsupported NioImageBuffer into a supported BYTE_BUFFER format
*/
void copyUnsupportedNioImageToImageData(NioImageBuffer nioImage, int srcX, int srcY,
int dstX, int dstY, int copyWidth, int copyHeight, ImageData iData) {
if (MasterControl.isDevLoggable(Level.INFO)) {
MasterControl.getDevLogger().info("ImageComponent - Copying Unsupported NioImage, use a different image type");
}
assert (iData.getType() == ImageDataType.TYPE_BYTE_BUFFER);
assert (getImageFormatType() == ImageFormatType.TYPE_BYTE_RGBA);
int length = copyWidth * copyHeight;
ByteBuffer srcBuffer = (ByteBuffer) nioImage.getDataBuffer();
srcBuffer.rewind();
ByteBuffer dstBuffer = iData.getAsByteBuffer();
dstBuffer.rewind();
// Do copy and swap.
for(int i = 0; i < length; i +=4) {
dstBuffer.put(i, srcBuffer.get(i+3));
dstBuffer.put(i+1, srcBuffer.get(i+2));
dstBuffer.put(i+2, srcBuffer.get(i+1));
dstBuffer.put(i+3, srcBuffer.get(i));
}
}
/*
* copy the complete unsupported image into a supported BYTE_ARRAY format
*/
void copyUnsupportedImageToImageData(RenderedImage ri, int depthIndex, ImageData data) {
assert (data.getType() == ImageDataType.TYPE_BYTE_ARRAY);
if (MasterControl.isDevLoggable(Level.INFO)) {
MasterControl.getDevLogger().info("ImageComponent - Copying Unsupported Image, use a different image type");
}
if (ri instanceof BufferedImage) {
copyUnsupportedImageToImageData((BufferedImage)ri, 0, 0, 0, 0,
depthIndex, data.dataWidth, data.dataHeight, data);
} else {
copyUnsupportedImageToImageData(ri, ri.getMinX(), ri.getMinY(),
0, 0, depthIndex, data.dataWidth, data.dataHeight, data);
}
}
void copyUnsupportedImageToImageData(BufferedImage bi, int srcX, int srcY,
int dstX, int dstY, int depthIndex, int copyWidth, int copyHeight, ImageData data) {
int w, h, i, j;
int rowBegin, // src begin row index
srcBegin, // src begin offset
dstBegin, // dst begin offset
rowInc, // row increment
// -1 --- ydown
// 1 --- yup
row;
rowBegin = srcY;
rowInc = 1;
assert (data != null);
int dataWidth = data.dataWidth;
int dataHeight = data.dataHeight;
int dstBytesPerRow = dataWidth * unitsPerPixel; // bytes per row in dst image
if (yUp) {
dstBegin = (depthIndex * dataWidth * dataHeight + dstY * dataWidth + dstX) * unitsPerPixel;
} else {
dstBegin = (depthIndex * dataWidth * dataHeight + (dataHeight - dstY - 1) * dataWidth + dstX) * unitsPerPixel;
dstBytesPerRow = - 1 * dstBytesPerRow;
}
WritableRaster ras = bi.getRaster();
ColorModel cm = bi.getColorModel();
Object pixel = getDataElementBuffer(ras);
byte[] dstBuffer = data.getAsByteArray();
switch(numberOfComponents) {
case 4: {
for (row = rowBegin, h = 0;
h < copyHeight; h++, row += rowInc) {
j = dstBegin;
for (w = srcX; w < (copyWidth + srcX); w++) {
ras.getDataElements(w, row, pixel);
dstBuffer[j++] = (byte)cm.getRed(pixel);
dstBuffer[j++] = (byte)cm.getGreen(pixel);
dstBuffer[j++] = (byte)cm.getBlue(pixel);
dstBuffer[j++] = (byte)cm.getAlpha(pixel);
}
dstBegin += dstBytesPerRow;
}
}
break;
case 3: {
for (row = rowBegin, h = 0;
h < copyHeight; h++, row += rowInc) {
j = dstBegin;
for (w = srcX; w < (copyWidth + srcX); w++) {
ras.getDataElements(w, row, pixel);
dstBuffer[j++] = (byte)cm.getRed(pixel);
dstBuffer[j++] = (byte)cm.getGreen(pixel);
dstBuffer[j++] = (byte)cm.getBlue(pixel);
}
dstBegin += dstBytesPerRow;
}
}
break;
case 2: {
for (row = rowBegin, h = 0;
h < copyHeight; h++, row += rowInc) {
j = dstBegin;
for (w = srcX; w < (copyWidth + srcX); w++) {
ras.getDataElements(w, row, pixel);
dstBuffer[j++] = (byte)cm.getRed(pixel);
dstBuffer[j++] = (byte)cm.getAlpha(pixel);
}
dstBegin += dstBytesPerRow;
}
}
break;
case 1: {
for (row = rowBegin, h = 0;
h < copyHeight; h++, row += rowInc) {
j = dstBegin;
for (w = srcX; w < (copyWidth + srcX); w++) {
ras.getDataElements(w, row, pixel);
dstBuffer[j++] = (byte)cm.getRed(pixel);
}
dstBegin += dstBytesPerRow;
}
}
break;
default:
assert false;
}
if((imageData == data) && (imageDataPowerOfTwo != null)) {
updateImageDataPowerOfTwo(depthIndex);
}
}
void copyUnsupportedImageToImageData(RenderedImage ri, int srcX, int srcY,
int dstX, int dstY, int depthIndex, int copyWidth, int copyHeight, ImageData data) {
int w, h, i, j, m, n;
int dstBegin;
Object pixel = null;
java.awt.image.Raster ras;
// dst image buffer
int sign; // -1 for going down
int dstLineBytes; // sign * lineBytes
int tileStart; // destination buffer offset
// at the next left most tile
int offset;
ColorModel cm = ri.getColorModel();
int xoff = ri.getTileGridXOffset(); // tile origin x offset
int yoff = ri.getTileGridYOffset(); // tile origin y offset
int minTileX = ri.getMinTileX(); // min tile x index
int minTileY = ri.getMinTileY(); // min tile y index
tilew = ri.getTileWidth(); // tile width in pixels
tileh = ri.getTileHeight(); // tile height in pixels
// determine the first tile of the image
float mt;
mt = (float)(srcX - xoff) / (float)tilew;
if (mt < 0) {
minTileX = (int)(mt - 1);
} else {
minTileX = (int)mt;
}
mt = (float)(srcY - yoff) / (float)tileh;
if (mt < 0) {
minTileY = (int)(mt - 1);
} else {
minTileY = (int)mt;
}
// determine the pixel offset of the upper-left corner of the
// first tile
int startXTile = minTileX * tilew + xoff;
int startYTile = minTileY * tileh + yoff;
// image dimension in the first tile
int curw = (startXTile + tilew - srcX);
int curh = (startYTile + tileh - srcY);
// check if the to-be-copied region is less than the tile image
// if so, update the to-be-copied dimension of this tile
if (curw > copyWidth) {
curw = copyWidth;
}
if (curh > copyHeight) {
curh = copyHeight;
}
// save the to-be-copied width of the left most tile
int startw = curw;
// temporary variable for dimension of the to-be-copied region
int tmpw = copyWidth;
int tmph = copyHeight;
// offset of the first pixel of the tile to be copied; offset is
// relative to the upper left corner of the title
int x = srcX - startXTile;
int y = srcY - startYTile;
// determine the number of tiles in each direction that the
// image spans
numXTiles = (copyWidth + x) / tilew;
numYTiles = (copyHeight + y) / tileh;
if (((float)(copyWidth + x ) % (float)tilew) > 0) {
numXTiles += 1;
}
if (((float)(copyHeight + y ) % (float)tileh) > 0) {
numYTiles += 1;
}
assert (data != null);
int dataWidth = data.dataWidth;
int dataHeight = data.dataHeight;
int lineBytes = dataWidth * unitsPerPixel; // nbytes per line in
if (yUp) {
// destination buffer offset
tileStart = (depthIndex * dataWidth * dataHeight + dstY * dataWidth + dstX) * unitsPerPixel;
sign = 1;
dstLineBytes = lineBytes;
} else {
// destination buffer offset
tileStart = (depthIndex * dataWidth * dataHeight + (dataHeight - dstY - 1) * dataWidth + dstX) * unitsPerPixel;
sign = -1;
dstLineBytes = -lineBytes;
}
/*
System.err.println("tileStart= " + tileStart + " dstLineBytes= " + dstLineBytes);
System.err.println("startw= " + startw);
*/
// allocate memory for a pixel
ras = ri.getTile(minTileX,minTileY);
pixel = getDataElementBuffer(ras);
byte[] dstBuffer = imageData.getAsByteArray();
switch(numberOfComponents) {
case 4: {
// System.err.println("Case 1: byReference = "+byReference);
for (n = minTileY; n < minTileY+numYTiles; n++) {
dstBegin = tileStart; // destination buffer offset
tmpw = copyWidth; // reset the width to be copied
curw = startw; // reset the width to be copied of
// the left most tile
x = srcX - startXTile; // reset the starting x offset of
// the left most tile
for (m = minTileX; m < minTileX+numXTiles; m++) {
// retrieve the raster for the next tile
ras = ri.getTile(m,n);
j = dstBegin;
offset = 0;
//System.err.println("curh = "+curh+" curw = "+curw);
//System.err.println("x = "+x+" y = "+y);
for (h = y; h < (y + curh); h++) {
// System.err.println("j = "+j);
for (w = x; w < (x + curw); w++) {
ras.getDataElements(w, h, pixel);
dstBuffer[j++] = (byte)cm.getRed(pixel);
dstBuffer[j++] = (byte)cm.getGreen(pixel);
dstBuffer[j++] = (byte)cm.getBlue(pixel);
dstBuffer[j++] = (byte)cm.getAlpha(pixel);
}
offset += dstLineBytes;
j = dstBegin + offset;
}
// advance the destination buffer offset
dstBegin += curw * unitsPerPixel;
// move to the next tile in x direction
x = 0;
// determine the width of copy region of the next tile
tmpw -= curw;
if (tmpw < tilew) {
curw = tmpw;
} else {
curw = tilew;
}
}
// we are done copying an array of tiles in the x direction
// advance the tileStart offset
tileStart += dataWidth * unitsPerPixel * curh * sign;
// move to the next set of tiles in y direction
y = 0;
// determine the height of copy region for the next set
// of tiles
tmph -= curh;
if (tmph < tileh) {
curh = tmph;
} else {
curh = tileh;
}
}
}
break;
case 3: {
for (n = minTileY; n < minTileY+numYTiles; n++) {
dstBegin = tileStart; // destination buffer offset
tmpw = copyWidth; // reset the width to be copied
curw = startw; // reset the width to be copied of
// the left most tile
x = srcX - startXTile; // reset the starting x offset of
// the left most tile
for (m = minTileX; m < minTileX+numXTiles; m++) {
// retrieve the raster for the next tile
ras = ri.getTile(m,n);
j = dstBegin;
offset = 0;
//System.err.println("curh = "+curh+" curw = "+curw);
//System.err.println("x = "+x+" y = "+y);
for (h = y; h < (y + curh); h++) {
// System.err.println("j = "+j);
for (w = x; w < (x + curw); w++) {
ras.getDataElements(w, h, pixel);
dstBuffer[j++] = (byte)cm.getRed(pixel);
dstBuffer[j++] = (byte)cm.getGreen(pixel);
dstBuffer[j++] = (byte)cm.getBlue(pixel);
}
offset += dstLineBytes;
j = dstBegin + offset;
}
// advance the destination buffer offset
dstBegin += curw * unitsPerPixel;
// move to the next tile in x direction
x = 0;
// determine the width of copy region of the next tile
tmpw -= curw;
if (tmpw < tilew) {
curw = tmpw;
} else {
curw = tilew;
}
}
// we are done copying an array of tiles in the x direction
// advance the tileStart offset
tileStart += dataWidth * unitsPerPixel * curh * sign;
// move to the next set of tiles in y direction
y = 0;
// determine the height of copy region for the next set
// of tiles
tmph -= curh;
if (tmph < tileh) {
curh = tmph;
} else {
curh = tileh;
}
}
}
break;
case 2: {
for (n = minTileY; n < minTileY+numYTiles; n++) {
dstBegin = tileStart; // destination buffer offset
tmpw = copyWidth; // reset the width to be copied
curw = startw; // reset the width to be copied of
// the left most tile
x = srcX - startXTile; // reset the starting x offset of
// the left most tile
for (m = minTileX; m < minTileX+numXTiles; m++) {
// retrieve the raster for the next tile
ras = ri.getTile(m,n);
j = dstBegin;
offset = 0;
//System.err.println("curh = "+curh+" curw = "+curw);
//System.err.println("x = "+x+" y = "+y);
for (h = y; h < (y + curh); h++) {
// System.err.println("j = "+j);
for (w = x; w < (x + curw); w++) {
ras.getDataElements(w, h, pixel);
dstBuffer[j++] = (byte)cm.getRed(pixel);
dstBuffer[j++] = (byte)cm.getAlpha(pixel);
}
offset += dstLineBytes;
j = dstBegin + offset;
}
// advance the destination buffer offset
dstBegin += curw * unitsPerPixel;
// move to the next tile in x direction
x = 0;
// determine the width of copy region of the next tile
tmpw -= curw;
if (tmpw < tilew) {
curw = tmpw;
} else {
curw = tilew;
}
}
// we are done copying an array of tiles in the x direction
// advance the tileStart offset
tileStart += dataWidth * unitsPerPixel * curh * sign;
// move to the next set of tiles in y direction
y = 0;
// determine the height of copy region for the next set
// of tiles
tmph -= curh;
if (tmph < tileh) {
curh = tmph;
} else {
curh = tileh;
}
}
}
break;
case 1: {
for (n = minTileY; n < minTileY+numYTiles; n++) {
dstBegin = tileStart; // destination buffer offset
tmpw = copyWidth; // reset the width to be copied
curw = startw; // reset the width to be copied of
// the left most tile
x = srcX - startXTile; // reset the starting x offset of
// the left most tile
for (m = minTileX; m < minTileX+numXTiles; m++) {
// retrieve the raster for the next tile
ras = ri.getTile(m,n);
j = dstBegin;
offset = 0;
//System.err.println("curh = "+curh+" curw = "+curw);
//System.err.println("x = "+x+" y = "+y);
for (h = y; h < (y + curh); h++) {
// System.err.println("j = "+j);
for (w = x; w < (x + curw); w++) {
ras.getDataElements(w, h, pixel);
dstBuffer[j++] = (byte)cm.getRed(pixel);
}
offset += dstLineBytes;
j = dstBegin + offset;
}
// advance the destination buffer offset
dstBegin += curw * unitsPerPixel;
// move to the next tile in x direction
x = 0;
// determine the width of copy region of the next tile
tmpw -= curw;
if (tmpw < tilew) {
curw = tmpw;
} else {
curw = tilew;
}
}
// we are done copying an array of tiles in the x direction
// advance the tileStart offset
tileStart += dataWidth * unitsPerPixel * curh * sign;
// move to the next set of tiles in y direction
y = 0;
// determine the height of copy region for the next set
// of tiles
tmph -= curh;
if (tmph < tileh) {
curh = tmph;
} else {
curh = tileh;
}
}
}
break;
default:
assert false;
}
if((imageData == data) && (imageDataPowerOfTwo != null)) {
updateImageDataPowerOfTwo(depthIndex);
}
}
void evaluateExtensions(Canvas3D canvas) {
// Issue 366: need to synchronize since it could be called concurrently
// from multiple renderers (and maybe the renderer(s) and renderbin)
synchronized (evaluateExtLock) {
// For performance reason the ordering of the following 2 statements is intentional.
// So that we only need to do format conversion for imageData only
evaluateExtABGR(canvas.extensionsSupported);
evaluateExtNonPowerOfTwo(canvas.textureExtendedFeatures);
}
}
void evaluateExtABGR(int ext) {
// If abgrSupported is false, a copy has been created so
// we don't have to check again.
if(!abgrSupported) {
return;
}
if(getImageFormatType() != ImageFormatType.TYPE_BYTE_ABGR) {
return;
}
if((ext & Canvas3D.EXT_ABGR) != 0) {
return;
}
// ABGR is unsupported, set flag to false.
abgrSupported = false;
convertImageDataFromABGRToRGBA();
}
private int getClosestPowerOf2(int value) {
if (value < 1)
return value;
int powerValue = 1;
for (;;) {
powerValue *= 2;
if (value < powerValue) {
// Found max bound of power, determine which is closest
int minBound = powerValue/2;
if ((powerValue - value) >
(value - minBound))
return minBound;
else
return powerValue;
}
}
}
private int getCeilPowerOf2(int value) {
if (value < 1)
return value;
int powerValue = 1;
for (;;) {
powerValue *= 2;
if (value <= powerValue) {
// Found max bound of power
return powerValue;
}
}
}
void evaluateExtNonPowerOfTwo(int ext) {
// Only need to enforce for Raster or Background.
if(!enforceNonPowerOfTwoSupport) {
return;
}
// If npotSupported is false, a copy power of two image has been created
// so we don't have to check again.
if(!npotSupported) {
return;
}
if (imageData == null && !isByReference()) {
return;
}
if((ext & Canvas3D.TEXTURE_NON_POWER_OF_TWO) != 0) {
return;
}
// NPOT is unsupported, set flag to false.
npotSupported = false;
int npotWidth;
int npotHeight;
// Always scale up if image size is smaller 512*512.
if((width * height) < IMAGE_SIZE_512X512) {
npotWidth = getCeilPowerOf2(width);
npotHeight = getCeilPowerOf2(height);
} else {
npotWidth = getClosestPowerOf2(width);
npotHeight = getClosestPowerOf2(height);
}
// System.err.println("width " + width + " height " + height + " npotWidth " + npotWidth + " npotHeight " + npotHeight);
float xScale = (float)npotWidth/(float)width;
float yScale = (float)npotHeight/(float)height;
// scale if scales aren't 1.0
if (!(xScale == 1.0f && yScale == 1.0f)) {
if (imageData == null) {
// This is a byRef, support format and is a RenderedImage case.
// See ImageComponent2DRetained.set(RenderedImage image)
RenderedImage ri = (RenderedImage) getRefImage(0);
assert !(ri instanceof BufferedImage);
// Create a buffered image from renderImage
ColorModel cm = ri.getColorModel();
WritableRaster wRaster = ri.copyData(null);
ri = new BufferedImage(cm,
wRaster,
cm.isAlphaPremultiplied()
,null);
// Create image data object with buffer for image. */
imageData = createRenderedImageDataObject(null);
copySupportedImageToImageData(ri, 0, imageData);
}
assert imageData != null;
// Create a supported BufferedImage type.
BufferedImage bi = imageData.createBufferedImage(0);
int imageType = bi.getType();
BufferedImage scaledImg = new BufferedImage(npotWidth, npotHeight, imageType);
AffineTransform at = AffineTransform.getScaleInstance(xScale,
yScale);
powerOfTwoATOp = new AffineTransformOp(at,
AffineTransformOp.TYPE_BILINEAR);
powerOfTwoATOp.filter(bi, scaledImg);
// System.err.println("bi " + bi.getColorModel());
// System.err.println("scaledImg " + scaledImg.getColorModel());
imageDataPowerOfTwo = createRenderedImageDataObject(null, npotWidth, npotHeight);
// Since bi is created from imageData, it's imageType is supported.
copySupportedImageToImageData(scaledImg, 0, imageDataPowerOfTwo);
} else {
imageDataPowerOfTwo = null;
}
}
void convertImageDataFromABGRToRGBA() {
// Unsupported format on HW, switch to slow copy.
imageFormatType = ImageFormatType.TYPE_BYTE_RGBA;
imageTypeIsSupported = false;
// Only need to convert imageData
imageData.convertFromABGRToRGBA();
}
/**
* Copy supported ImageType from ImageData to the user defined bufferedImage
*/
void copyToRefImage(int depth) {
int h;
int rowBegin, // src begin row index
srcBegin, // src begin offset
dstBegin; // dst begin offset
// refImage has to be a BufferedImage for off screen and read raster
assert refImage[depth] != null;
assert (refImage[depth] instanceof BufferedImage);
BufferedImage bi = (BufferedImage)refImage[depth];
int dstUnitsPerRow = width * unitsPerPixel; // bytes per row in dst image
rowBegin = 0;
if (yUp) {
dstBegin = (depth * width * height) * unitsPerPixel;
} else {
dstBegin = (depth * width * height + (height - 1) * width ) * unitsPerPixel;
dstUnitsPerRow = - 1 * dstUnitsPerRow;
}
int scanline = width * unitsPerPixel;
srcBegin = (rowBegin * width ) * unitsPerPixel;
switch(imageData.getType()) {
case TYPE_BYTE_ARRAY:
byte[] dstByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
byte[] srcByteBuffer = imageData.getAsByteArray();
for (h = 0; h < height; h++) {
System.arraycopy(srcByteBuffer, srcBegin, dstByteBuffer, dstBegin, scanline);
dstBegin += dstUnitsPerRow;
srcBegin += scanline;
}
break;
case TYPE_INT_ARRAY:
int[] dstIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
int[] srcIntBuffer = imageData.getAsIntArray();
for (h = 0; h < height; h++) {
System.arraycopy(srcIntBuffer, srcBegin, dstIntBuffer, dstBegin, scanline);
dstBegin += dstUnitsPerRow;
srcBegin += scanline;
}
break;
default:
assert false;
}
}
/**
* Copy image to the user defined bufferedImage ( 3 or 4 components only )
*/
void copyToRefImageWithFormatConversion(int depth) {
int w, h, i, j;
int dstBegin, dstInc, dstIndex, dstIndexInc;
// refImage has to be a BufferedImage for off screen and read raster
assert refImage[depth] != null;
assert (refImage[depth] instanceof BufferedImage);
BufferedImage bi = (BufferedImage)refImage[depth];
int biType = bi.getType();
byte[] buf = imageData.getAsByteArray();
// convert from Ydown to Yup for texture
if (!yUp) {
dstInc = -1 * width;
dstBegin = (height - 1) * width;
dstIndex = height -1;
dstIndexInc = -1;
} else {
dstInc = width;
dstBegin = 0;
dstIndex = 0;
dstIndexInc = 1;
}
switch (biType) {
case BufferedImage.TYPE_INT_ARGB:
int[] intData =
((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
// Multiply by 4 to get the byte incr and start point
j = 0;
switch (imageFormatType) {
case TYPE_BYTE_RGBA:
for(h = 0; h < height; h++, dstBegin += dstInc) {
i = dstBegin;
for (w = 0; w < width; w++, j+=4, i++) {
intData[i] = (((buf[j+3] &0xff) << 24) | // a
((buf[j] &0xff) << 16) | // r
((buf[j+1] &0xff) << 8) | // g
(buf[j+2] & 0xff)); // b
}
}
break;
case TYPE_BYTE_RGB:
for(h = 0; h < height; h++, dstBegin += dstInc) {
i = dstBegin;
for (w = 0; w < width; w++, j+=3, i++) {
intData[i] = (0xff000000 | // a
((buf[j] &0xff) << 16) | // r
((buf[j+1] &0xff) << 8) | // g
(buf[j+2] & 0xff)); // b
}
}
break;
default:
assert false;
}
break;
case BufferedImage.TYPE_INT_RGB:
intData =
((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
// Multiply by 4 to get the byte incr and start point
j = 0;
for(h = 0; h < height; h++, dstBegin += dstInc) {
i = dstBegin;
for (w = 0; w < width; w++, j+=4, i++) {
intData[i] = (0xff000000 | // a
((buf[j] &0xff) << 16) | // r
((buf[j+1] &0xff) << 8) | // g
(buf[j+2] & 0xff)); // b
}
}
break;
case BufferedImage.TYPE_4BYTE_ABGR:
byte[] byteData =
((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
// Multiply by 4 to get the byte incr and start point
j = 0;
//Issue 381: dstBegin contains pixel count, but we are looping over byte count. In case of YDown, it contains a count that is decremented and if we do not multiply, we have an AIOOB thrown at 25% of the copy.
dstBegin <<= 2;
switch (imageFormatType) {
case TYPE_BYTE_RGBA:
for(h = 0; h < height; h++, dstBegin += (dstInc << 2)) {
i = dstBegin;
for (w = 0; w < width; w++, j+=4) {
byteData[i++] = buf[j+3]; // a
byteData[i++] = buf[j+2]; // b
byteData[i++] = buf[j+1];// g
byteData[i++] = buf[j]; // r
}
}
break;
case TYPE_BYTE_RGB:
for(h = 0; h < height; h++, dstBegin += (dstInc << 2)) {
i = dstBegin;
for (w = 0; w < width; w++, j+=3) {
byteData[i++] = (byte) 0xff; // a
byteData[i++] = buf[j+2]; // b
byteData[i++] = buf[j+1];// g
byteData[i++] = buf[j]; // r
}
}
break;
default:
assert false;
}
break;
case BufferedImage.TYPE_INT_BGR:
intData =
((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
// Multiply by 4 to get the byte incr and start point
j = 0;
for(h = 0; h < height; h++, dstBegin += dstInc) {
i = dstBegin;
for (w = 0; w < width; w++, j+=4, i++) {
intData[i] = (0xff000000 | // a
((buf[j] &0xff) ) | // r
((buf[j+1] &0xff) << 8) | // g
(buf[j+2] & 0xff)<< 16); // b
}
}
break;
default:
assert false;
}
}
// Add a user to the userList
synchronized void addUser(NodeComponentRetained node) {
userList.add(node);
}
// Add a user to the userList
synchronized void removeUser(NodeComponentRetained node) {
int i = userList.indexOf(node);
if (i >= 0) {
userList.remove(i);
}
}
/*
*
* @exception IllegalSharingException if this image is
* being used by a Canvas3D as an off-screen buffer.
*/
@Override
void setLive(boolean inBackgroundGroup, int refCount) {
// Do illegalSharing check.
if(getUsedByOffScreen()) {
throw new IllegalSharingException(J3dI18N.getString("ImageComponent3"));
}
super.setLive(inBackgroundGroup, refCount);
}
/**
* ImageComponent object doesn't really have mirror object.
* But it's using the updateMirrorObject interface to propagate
* the changes to the users
*/
@Override
synchronized void updateMirrorObject(int component, Object value) {
// System.err.println("ImageComponent.updateMirrorObject");
if ((component & IMAGE_CHANGED) == 0 &&
(component & SUBIMAGE_CHANGED) == 0)
return;
for (int i = userList.size() - 1; i >= 0; i--) {
NodeComponentRetained user = userList.get(i);
if (user == null)
continue;
if (user instanceof TextureRetained) {
((TextureRetained)user).notifyImageComponentImageChanged(this, (ImageComponentUpdateInfo)value);
}
else if (user instanceof RasterRetained) {
((RasterRetained)user).notifyImageComponentImageChanged(this, (ImageComponentUpdateInfo)value);
}
}
}
final void sendMessage(int attrMask, Object attr) {
J3dMessage createMessage = new J3dMessage();
createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES |
J3dThread.UPDATE_RENDER;
createMessage.type = J3dMessage.IMAGE_COMPONENT_CHANGED;
createMessage.universe = null;
createMessage.args[0] = this;
createMessage.args[1]= new Integer(attrMask);
createMessage.args[2] = attr;
createMessage.args[3] = new Integer(changedFrequent);
VirtualUniverse.mc.processMessage(createMessage);
}
@Override
void handleFrequencyChange(int bit) {
if (bit == ImageComponent.ALLOW_IMAGE_WRITE) {
setFrequencyChangeMask(ImageComponent.ALLOW_IMAGE_WRITE, 0x1);
}
}
static Object getDataElementBuffer(java.awt.image.Raster ras) {
int nc = ras.getNumDataElements();
switch (ras.getTransferType()) {
case DataBuffer.TYPE_INT:
return new int[nc];
case DataBuffer.TYPE_BYTE:
return new byte[nc];
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_SHORT:
return new short[nc];
case DataBuffer.TYPE_FLOAT:
return new float[nc];
case DataBuffer.TYPE_DOUBLE:
return new double[nc];
}
// Should not happen
return null;
}
/**
* Wrapper class for image data.
* Currently supports byte array and int array.
* Will eventually support NIO ByteBuffer and IntBuffer.
*/
class ImageData {
private Object data = null;
private ImageDataType imageDataType = ImageDataType.TYPE_NULL;
private int length = 0;
private boolean dataIsByRef = false;
private int dataWidth, dataHeight;
/**
* Constructs a new ImageData buffer of the specified type with the
* specified length.
*/
ImageData(ImageDataType imageDataType, int length, int dataWidth, int dataHeight) {
this.imageDataType = imageDataType;
this.length = length;
this.dataWidth = dataWidth;
this.dataHeight = dataHeight;
this.dataIsByRef = false;
switch (imageDataType) {
case TYPE_BYTE_ARRAY:
data = new byte[length];
break;
case TYPE_INT_ARRAY:
data = new int[length];
break;
case TYPE_BYTE_BUFFER:
ByteOrder order = ByteOrder.nativeOrder();
data = ByteBuffer.allocateDirect(length).order(order);
break;
case TYPE_INT_BUFFER:
default:
throw new AssertionError();
}
}
/**
* Constructs a new ImageData buffer of the specified type with the
* specified length and the specified byRefImage as data.
*/
ImageData(ImageDataType imageDataType, int length, int dataWidth, int dataHeight,
Object byRefImage) {
BufferedImage bi;
NioImageBuffer nio;
this.imageDataType = imageDataType;
this.length = length;
this.dataWidth = dataWidth;
this.dataHeight = dataHeight;
this.dataIsByRef = true;
switch (imageDataType) {
case TYPE_BYTE_ARRAY:
bi = (BufferedImage) byRefImage;
data = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
break;
case TYPE_INT_ARRAY:
bi = (BufferedImage) byRefImage;
data = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
break;
case TYPE_BYTE_BUFFER:
case TYPE_INT_BUFFER:
nio = (NioImageBuffer) byRefImage;
data = nio.getDataBuffer();
break;
default:
throw new AssertionError();
}
}
/**
* Constructs a new ImageData buffer from the specified
* object. This object stores a reference to the input image data.
*/
ImageData(Object data, boolean isByRef) {
this.data = data;
dataIsByRef = isByRef;
dataWidth = ((ImageData) data).dataWidth;
dataHeight = ((ImageData) data).dataHeight;
if (data == null) {
imageDataType = ImageDataType.TYPE_NULL;
length = 0;
} else if (data instanceof byte[]) {
imageDataType = ImageDataType.TYPE_BYTE_ARRAY;
length = ((byte[]) data).length;
} else if (data instanceof int[]) {
imageDataType = ImageDataType.TYPE_INT_ARRAY;
length = ((int[]) data).length;
} else if (data instanceof ByteBuffer) {
imageDataType = ImageDataType.TYPE_BYTE_BUFFER;
length = ((ByteBuffer) data).limit();
} else if (data instanceof IntBuffer) {
imageDataType = ImageDataType.TYPE_INT_BUFFER;
length = ((IntBuffer) data).limit();
} else {
assert false;
}
}
/**
* Returns the type of this DataBuffer.
*/
ImageDataType getType() {
return imageDataType;
}
/**
* Returns the number of elements in this DataBuffer.
*/
int length() {
return length;
}
/**
* Returns the width of this DataBuffer.
*/
int getWidth() {
return dataWidth;
}
/**
* Returns the height of this DataBuffer.
*/
int getHeight() {
return dataHeight;
}
/**
* Returns this DataBuffer as an Object.
*/
Object get() {
return data;
}
/**
* Returns is this data is byRef. No internal data is made.
*/
boolean isDataByRef() {
return dataIsByRef;
}
/**
* Returns this DataBuffer as a byte array.
*/
byte[] getAsByteArray() {
return (byte[]) data;
}
/**
* Returns this DataBuffer as an int array.
*/
int[] getAsIntArray() {
return (int[]) data;
}
/**
* Returns this DataBuffer as an nio ByteBuffer.
*/
ByteBuffer getAsByteBuffer() {
return (ByteBuffer) data;
}
/**
* Returns this DataBuffer as an nio IntBuffer.
*/
IntBuffer getAsIntBuffer() {
return (IntBuffer) data;
}
// Handle TYPE_BYTE_LA only
void copyByLineAndExpand(BufferedImage bi, int depthIndex) {
int h;
int srcBegin, // src begin offset
dstBegin; // dst begin offset
assert (imageData.getType() == ImageDataType.TYPE_BYTE_ARRAY);
assert (imageFormatType == ImageFormatType.TYPE_BYTE_LA);
int unitsPerRow = width * unitsPerPixel; // bytes per row
int scanline = unitsPerRow;
if (yUp) {
srcBegin = (depthIndex * width * height) * unitsPerPixel;
} else {
srcBegin = (depthIndex * width * height + (height - 1) * width) * unitsPerPixel;
unitsPerRow = - 1 * unitsPerRow;
}
dstBegin = 0;
// ABGR is 4 bytes per pixel
int dstUnitsPerRow = width * 4;
byte[] dstByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
byte[] srcByteBuffer = imageData.getAsByteArray();
for (h = 0; h < height; h++) {
for( int v = 0, w = 0; w < scanline; w += unitsPerPixel, v += 4) {
dstByteBuffer[dstBegin+v] = srcByteBuffer[srcBegin+w+1]; // Alpha
dstByteBuffer[dstBegin+v+1] = 0;
dstByteBuffer[dstBegin+v+2] = 0;
dstByteBuffer[dstBegin+v+3] = srcByteBuffer[srcBegin+w]; // Red
}
dstBegin += dstUnitsPerRow;
srcBegin += unitsPerRow;
}
}
// Quick line by line copy
void copyByLine(BufferedImage bi, int depthIndex, boolean swapNeeded) {
int h;
int srcBegin, // src begin offset
dstBegin; // dst begin offset
int unitsPerRow = width * unitsPerPixel; // bytes per row
int copyUnits = unitsPerRow;
if (yUp) {
srcBegin = (depthIndex * width * height) * unitsPerPixel;
} else {
srcBegin = (depthIndex * width * height + (height - 1) * width) * unitsPerPixel;
unitsPerRow = - 1 * unitsPerRow;
}
dstBegin = 0;
switch(imageData.getType()) {
case TYPE_BYTE_ARRAY:
byte[] dstByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
byte[] srcByteBuffer = imageData.getAsByteArray();
for (h = 0; h < height; h++) {
if(!swapNeeded) {
System.arraycopy(srcByteBuffer, srcBegin,
dstByteBuffer, dstBegin, copyUnits);
} else {
if(imageFormatType == ImageFormatType.TYPE_BYTE_RGB) {
assert (unitsPerPixel == 3);
for(int w = 0; w < copyUnits; w += unitsPerPixel) {
dstByteBuffer[dstBegin+w] = srcByteBuffer[srcBegin+w+2];
dstByteBuffer[dstBegin+w+1] = srcByteBuffer[srcBegin+w+1];
dstByteBuffer[dstBegin+w+2] = srcByteBuffer[srcBegin+w];
}
} else if(imageFormatType == ImageFormatType.TYPE_BYTE_RGBA) {
assert (unitsPerPixel == 4);
for(int w = 0; w < copyUnits; w += unitsPerPixel) {
dstByteBuffer[dstBegin+w] = srcByteBuffer[srcBegin+w+3];
dstByteBuffer[dstBegin+w+1] = srcByteBuffer[srcBegin+w+2];
dstByteBuffer[dstBegin+w+2] = srcByteBuffer[srcBegin+w+1];
dstByteBuffer[dstBegin+w+3] = srcByteBuffer[srcBegin+w];
}
} else {
assert false;
}
}
dstBegin += copyUnits;
srcBegin += unitsPerRow;
}
break;
// INT case doesn't required to handle swapNeeded
case TYPE_INT_ARRAY:
assert (!swapNeeded);
int[] dstIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
int[] srcIntBuffer = imageData.getAsIntArray();
for (h = 0; h < height; h++) {
System.arraycopy(srcIntBuffer, srcBegin, dstIntBuffer, dstBegin, copyUnits);
dstBegin += copyUnits;
srcBegin += unitsPerRow;
}
break;
default:
assert false;
}
}
void copyByBlock(BufferedImage bi, int depthIndex) {
// src begin offset
int srcBegin = depthIndex * width * height * unitsPerPixel;
switch(imageData.getType()) {
case TYPE_BYTE_ARRAY:
byte[] dstByteBuffer = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
byte[] srcByteBuffer = imageData.getAsByteArray();
System.arraycopy(srcByteBuffer, srcBegin, dstByteBuffer, 0, (height * width * unitsPerPixel));
break;
case TYPE_INT_ARRAY:
int[] dstIntBuffer = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
int[] srcIntBuffer = imageData.getAsIntArray();
System.arraycopy(srcIntBuffer, srcBegin, dstIntBuffer, 0, (height * width * unitsPerPixel));
break;
default:
assert false;
}
}
// Need to check for imageData is null. if it is null return null.
BufferedImage createBufferedImage(int depthIndex) {
if(data != null) {
int bufferType = BufferedImage.TYPE_CUSTOM;
boolean swapNeeded = false;
switch(imageFormatType) {
case TYPE_BYTE_BGR:
bufferType = BufferedImage.TYPE_3BYTE_BGR;
break;
case TYPE_BYTE_RGB:
bufferType = BufferedImage.TYPE_3BYTE_BGR;
swapNeeded = true;
break;
case TYPE_BYTE_ABGR:
bufferType = BufferedImage.TYPE_4BYTE_ABGR;
break;
case TYPE_BYTE_RGBA:
bufferType = BufferedImage.TYPE_4BYTE_ABGR;
swapNeeded = true;
break;
// This is a special case. Need to handle separately.
case TYPE_BYTE_LA:
bufferType = BufferedImage.TYPE_4BYTE_ABGR;
break;
case TYPE_BYTE_GRAY:
bufferType = BufferedImage.TYPE_BYTE_GRAY;
break;
case TYPE_INT_BGR:
bufferType = BufferedImage.TYPE_INT_BGR;
break;
case TYPE_INT_RGB:
bufferType = BufferedImage.TYPE_INT_RGB;
break;
case TYPE_INT_ARGB:
bufferType = BufferedImage.TYPE_INT_ARGB;
break;
// Unsupported case, so shouldn't be here.
case TYPE_USHORT_GRAY:
bufferType = BufferedImage.TYPE_USHORT_GRAY;
default:
assert false;
}
BufferedImage bi = new BufferedImage(width, height, bufferType);
if((!swapNeeded) && (imageFormatType != ImageFormatType.TYPE_BYTE_LA)) {
if(yUp) {
copyByBlock(bi, depthIndex);
} else {
copyByLine(bi, depthIndex, false);
}
} else if(swapNeeded) {
copyByLine(bi, depthIndex, swapNeeded);
} else if(imageFormatType == ImageFormatType.TYPE_BYTE_LA) {
copyByLineAndExpand(bi, depthIndex);
} else {
assert false;
}
return bi;
}
return null;
}
void convertFromABGRToRGBA() {
int i;
if(imageDataType == ImageComponentRetained.ImageDataType.TYPE_BYTE_ARRAY) {
// Note : Highly inefficient for depth > 0 case.
// This method doesn't take into account of depth, it is assuming that
// depth == 0, which is true for ImageComponent2D.
byte[] srcBuffer, dstBuffer;
srcBuffer = getAsByteArray();
if(dataIsByRef) {
dstBuffer = new byte[length];
// Do copy and swap.
for(i = 0; i < length; i +=4) {
dstBuffer[i] = srcBuffer[i+3];
dstBuffer[i+1] = srcBuffer[i+2];
dstBuffer[i+2] = srcBuffer[i+1];
dstBuffer[i+3] = srcBuffer[i];
}
data = dstBuffer;
dataIsByRef = false;
} else {
byte a, b;
// Do swap in place.
for(i = 0; i < length; i +=4) {
a = srcBuffer[i];
b = srcBuffer[i+1];
srcBuffer[i] = srcBuffer[i+3];
srcBuffer[i+1] = srcBuffer[i+2];
srcBuffer[i+2] = b;
srcBuffer[i+3] = a;
}
}
}
else if(imageDataType == ImageComponentRetained.ImageDataType.TYPE_BYTE_BUFFER) {
assert dataIsByRef;
ByteBuffer srcBuffer, dstBuffer;
srcBuffer = getAsByteBuffer();
srcBuffer.rewind();
ByteOrder order = ByteOrder.nativeOrder();
dstBuffer = ByteBuffer.allocateDirect(length).order(order);
dstBuffer.rewind();
// Do copy and swap.
for(i = 0; i < length; i +=4) {
dstBuffer.put(i, srcBuffer.get(i+3));
dstBuffer.put(i+1, srcBuffer.get(i+2));
dstBuffer.put(i+2, srcBuffer.get(i+1));
dstBuffer.put(i+3, srcBuffer.get(i));
}
dataIsByRef = false;
}
}
}
}