aboutsummaryrefslogtreecommitdiffstats
path: root/src/javax/media/j3d/ImageComponentRetained.java
diff options
context:
space:
mode:
authorJulien Gouesse <[email protected]>2015-11-19 20:45:53 +0100
committerJulien Gouesse <[email protected]>2015-11-19 20:45:53 +0100
commita18c3b0789bfc24b49dbaf41c2390159bc683afc (patch)
treeb5236ff2570178de356eab569225108948eb4d30 /src/javax/media/j3d/ImageComponentRetained.java
parent264608060948a634b53a13ee96ed07527eb07340 (diff)
parent7a2e20caac9db6f789a7b3fab344b9758af45335 (diff)
Gets Harvey's changes
Diffstat (limited to 'src/javax/media/j3d/ImageComponentRetained.java')
-rw-r--r--src/javax/media/j3d/ImageComponentRetained.java2653
1 files changed, 2653 insertions, 0 deletions
diff --git a/src/javax/media/j3d/ImageComponentRetained.java b/src/javax/media/j3d/ImageComponentRetained.java
new file mode 100644
index 0000000..8fb9697
--- /dev/null
+++ b/src/javax/media/j3d/ImageComponentRetained.java
@@ -0,0 +1,2653 @@
+/*
+ * 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;
+
+ }
+ }
+ }
+}