diff options
Diffstat (limited to 'src/javax/media/j3d/ImageComponentRetained.java')
-rw-r--r-- | src/javax/media/j3d/ImageComponentRetained.java | 2653 |
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; + + } + } + } +} |