/* * Copyright 1997-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.Dimension; import java.awt.Point; import java.util.ArrayList; import javax.vecmath.Point2d; import javax.vecmath.Point2i; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Point4d; /** * A Retained Raster. */ class RasterRetained extends GeometryRetained { /** * Raster type */ int type = Raster.RASTER_COLOR; private int clipMode = Raster.CLIP_POSITION; private Point3f position = new Point3f(); private int xSrcOffset = 0; private int ySrcOffset = 0; private int width = 0; private int height = 0; private int xDstOffset = 0; private int yDstOffset = 0; ImageComponent2DRetained image = null; Texture2DRetained texture = null; DepthComponentRetained depthComponent = null; RasterRetained() { this.geoType = GEO_TYPE_RASTER; } /** * Set the Raster position * @param position new raster position */ final void setPosition(Point3f pos) { geomLock.getLock(); position.x = pos.x; position.y = pos.y; position.z = pos.z; geomLock.unLock(); sendChangedMessage(J3dThread.UPDATE_GEOMETRY, null, null); } /** * Retrieves the Raster's position * @param position the variable to receive the position vector */ final void getPosition(Point3f pos) { pos.x = position.x; pos.y = position.y; pos.z = position.z; } /** * Sets the type of this raster object to one of: RASTER_COLOR, * RASTER_DEPTH, or RASTER_COLOR_DEPTH. * @param type the new type of this raster */ final void setType(int type) { geomLock.getLock(); this.type = type; geomLock.unLock(); } /** * Retrieves the current type of this raster object, one of: RASTER_COLOR, * RASTER_DEPTH, or RASTER_COLOR_DEPTH. * @return type the type of this raster */ final int getType() { return type; } /** * Sets the clipping mode of this raster object. * @param clipMode the new clipping mode of this raster, * one of: CLIP_POSITION or CLIP_IMAGE. The default mode * is CLIP_POSITION. */ final void setClipMode(int clipMode) { geomLock.getLock(); this.clipMode = clipMode; geomLock.unLock(); computeBoundingBox(); if(source.isLive()) { //update the Shape3Ds that refer to this Raster int un = userLists.size(); Shape3DRetained ms, shape; int sn; for(int i = 0; i < un; i++) { ArrayList shapeList = userLists.get(i); sn = shapeList.size(); for(int j = 0; j < sn; j++) { ms = shapeList.get(j); shape = (Shape3DRetained)ms.sourceNode; shape.setBoundsAutoCompute(false); shape.setBounds(geoBounds); } } } } /** * Retrieves the current clipping mode of this raster object. * @return clipMode the clipping mode of this raster, * one of: CLIP_POSITION or CLIP_IMAGE. */ final int getClipMode() { return clipMode; } /** * Sets the offset within the source array of pixels at which * to start copying. * @param xSrcOffset the x offset within the source array of pixels * at which to start copying * @param ySrcOffset the y offset within the source array of pixels * at which to start copying */ final void setSrcOffset(int xSrcOffset, int ySrcOffset) { geomLock.getLock(); this.xSrcOffset = xSrcOffset; this.ySrcOffset = ySrcOffset; geomLock.unLock(); } /** * Retrieves the current source pixel offset. * @param srcOffset the object that will receive the source offset */ final void getSrcOffset(Point srcOffset) { srcOffset.setLocation(xSrcOffset, ySrcOffset); } /** * Sets the number of pixels to be copied from the pixel array. * @param width the number of columns in the array of pixels to copy * @param height the number of rows in the array of pixels to copy */ final void setSize(int width, int height) { geomLock.getLock(); this.width = width; this.height = height; geomLock.unLock(); } /** * Gets the size of the array of pixels to be copied. * @param size the new size */ final void getSize(Dimension size) { size.setSize(width, height); } /** * Sets the destination pixel offset of the upper-left * corner of the rendered image relative to the transformed position. * @param xDstOffset the x coordinate of the new offset * @param yDstOffset the y coordinate of the new offset */ final void setDstOffset(int xDstOffset, int yDstOffset) { geomLock.getLock(); this.xDstOffset = xDstOffset; this.yDstOffset = yDstOffset; geomLock.unLock(); } /** * Retrieves the current destination pixel offset. * @param dstOffset the object that will receive the destination offset */ final void getDstOffset(Point dstOffset) { dstOffset.setLocation(xDstOffset, yDstOffset); } /** * Initializes the raster image to the specified image. * @param image new ImageCompoent2D object used as the raster image */ final void initImage(ImageComponent2D img) { int texFormat; if(img == null) { image = null; texture = null; return; } image = (ImageComponent2DRetained) img.retained; image.setEnforceNonPowerOfTwoSupport(true); switch(image.getNumberOfComponents()) { case 1: texFormat = Texture.INTENSITY; break; case 2: texFormat = Texture.LUMINANCE_ALPHA; break; case 3: texFormat = Texture.RGB; break; case 4: texFormat = Texture.RGBA; break; default: assert false; return; } Texture2D tex2D = new Texture2D(Texture.BASE_LEVEL, texFormat, img.getWidth(), img.getHeight()); texture = (Texture2DRetained) tex2D.retained; texture.setUseAsRaster(true); // Fix to issue 372 : ImageComponent.set(BufferedImage) ignored when used by Raster image.addUser(texture); texture.initImage(0,img); } /** * Sets the pixel array used to copy pixels to/from a Canvas3D. * This is used when the type is RASTER_COLOR or RASTER_COLOR_DEPTH. * @param image the ImageComponent2D object containing the * color data */ final void setImage(ImageComponent2D img) { if((img != null) && (img.getImageClass() == ImageComponent.ImageClass.NIO_IMAGE_BUFFER)) { throw new IllegalArgumentException(J3dI18N.getString("Background14")); } TextureRetained oldTex = this.texture; if (source.isLive()) { if (this.texture != null) { this.texture.clearLive(refCount); } } // Issue 370: only hold the geomLock while calling initImage // (cannot hold it while sending a message). geomLock.getLock(); initImage(img); geomLock.unLock(); if (source.isLive()) { if (texture != null) { texture.setLive(inBackgroundGroup, refCount); } sendChangedMessage((J3dThread.UPDATE_RENDER|J3dThread.UPDATE_RENDERING_ATTRIBUTES), oldTex, this.texture); } } /** * Retrieves the current pixel array object. * @return image the ImageComponent2D object containing the * color data */ final ImageComponent2D getImage() { return (image == null ? null : (ImageComponent2D)image.source); } /** * Sets the depth image used to copy pixels to/from a Canvas3D. * This is used when the type is RASTER_DEPTH or RASTER_COLOR_DEPTH. * @param depthImage the DepthComponent object containing the * depth (z-buffer) data */ final void setDepthComponent(DepthComponent depthComponent) { geomLock.getLock(); if (this.source.isLive()) { if (this.depthComponent != null) { this.depthComponent.clearLive(refCount); } if (depthComponent != null) { ((DepthComponentRetained)depthComponent.retained).setLive(inBackgroundGroup, refCount); } } if (depthComponent == null) { this.depthComponent = null; } else { this.depthComponent = (DepthComponentRetained)depthComponent.retained; } geomLock.unLock(); } /** * Retrieves the current depth image object. * @return depthImage DepthComponent containing the * depth (z-buffer) data */ final DepthComponent getDepthComponent() { return (depthComponent == null ? null : (DepthComponent)depthComponent.source); } @Override void setLive(boolean inBackgroundGroup, int refCount) { super.doSetLive(inBackgroundGroup, refCount); if (texture != null) { texture.setLive(inBackgroundGroup, refCount); } if (depthComponent != null) { depthComponent.setLive(inBackgroundGroup, refCount); } isEditable = source.getCapability(Raster.ALLOW_OFFSET_WRITE) || source.getCapability(Raster.ALLOW_POSITION_WRITE) || ((type & Raster.RASTER_COLOR) != 0 && source.getCapability(Raster.ALLOW_IMAGE_WRITE)) || ((type & Raster.RASTER_DEPTH) != 0 && source.getCapability( Raster.ALLOW_DEPTH_COMPONENT_WRITE)) || source.getCapability( Raster.ALLOW_SIZE_WRITE); super.markAsLive(); } @Override void clearLive(int refCount) { super.clearLive(refCount); if (texture != null) texture.clearLive(refCount); if (depthComponent != null) depthComponent.clearLive(refCount); } /* // Simply pass along to the NodeComponents void compile(CompileState compState) { setCompiled(); if (image != null) image.compile(compState); if (depthComponent != null) depthComponent.compile(compState); } */ @Override void computeBoundingBox() { if(clipMode == Raster.CLIP_IMAGE) { // Disable view frustum culling by setting the raster's bounds to // infinity. Point3d minBounds = new Point3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); Point3d maxBounds = new Point3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); geoBounds.setUpper(maxBounds); geoBounds.setLower(minBounds); } else { Point3d center = new Point3d(); center.x = position.x; center.y = position.y; center.z = position.z; geoBounds.setUpper(center); geoBounds.setLower(center); } } @Override void update() { computeBoundingBox(); } private void sendChangedMessage(int threads, Object arg1, Object arg2) { synchronized(liveStateLock) { if (source.isLive()) { synchronized (universeList) { int numMessages = universeList.size(); J3dMessage[] m = new J3dMessage[numMessages]; for (int i=0; i 0) && (winCoord.y > 0)) { return; } // Check if the Raster point will be culled // Note that w use 1 instead of 0, because when hardware // tranform the coordinate back to winCoord it may get // a small negative value due to numerically inaccurancy. // This clip the Raster away and cause flickering // (see bug 4732965) if(winCoord.x < 1) { // Negate the window position and use this as the offset srcOffset.x = (int)-winCoord.x+1; winCoord.x = 1; } if(winCoord.y < 1) { // Negate the window position and use this as the offset srcOffset.y = (int)-winCoord.y+1; winCoord.y = 1; } //check if user-specified subimage is smaller than the clipped image if (srcOffset.x < xSrcOffset) srcOffset.x = xSrcOffset; if(srcOffset.y < ySrcOffset) srcOffset.y = ySrcOffset; } private boolean isRasterClipPositionInside(Point3d clipCoord) { return (clipCoord.x >= -1.0) && (clipCoord.x <= 1.0) && (clipCoord.y >= -1.0) && (clipCoord.y <= 1.0); } private void computeObjCoord(Canvas3D canvas, Point2d winCoord, Point3d objCoord, Transform3D localToImagePlate) { // Back transform this pt. from window to object coordinates // Assumes this method is ALWAYS called after computeWinCoord has been // called. computeWinCoord calculates the Vworld to Image Plate Xform. // This method simply uses it without recomputing it. canvas.getPixelLocationInImagePlate(winCoord.x, winCoord.y, objCoord.z, objCoord); // Get image plate to object coord transform // inv(P x M) localToImagePlate.invert(); localToImagePlate.transform(objCoord); } private Point3d computeWinCoord(Canvas3D canvas, RenderAtom ra, Point2d winCoord, Point3d objCoord, Transform3D localToImagePlate) { // Get local to Vworld transform RenderMolecule rm = ra.renderMolecule; if (rm == null) { // removeRenderAtom() may set ra.renderMolecule to null // in RenderBin before this renderer thread run. return null; } // MT safe issue: We can't reference ra.renderMolecule below since // RenderBin thread may set it to null anytime. Use rm instead. Transform3D lvw = rm.localToVworld[rm.localToVworldIndex[ NodeRetained.LAST_LOCAL_TO_VWORLD]]; Point3d clipCoord3 = new Point3d(); clipCoord3.set(objCoord); Point4d clipCoord4 = new Point4d(); // Transform point from local coord. to clipping coord. lvw.transform(clipCoord3); canvas.vworldToEc.transform(clipCoord3); canvas.projTrans.transform(clipCoord3, clipCoord4); // clip check in Z if((clipCoord4.w <= 0.0) || (clipCoord4.z > clipCoord4.w) || (-clipCoord4.z > clipCoord4.w)) { return null; } double invW = 1.0 / clipCoord4.w; clipCoord3.x = clipCoord4.x * invW; clipCoord3.y = clipCoord4.y * invW; clipCoord3.z = clipCoord4.z * invW; // Get Vworld to image plate Xform canvas.getLastVworldToImagePlate(localToImagePlate); // v' = vwip x lvw x v // where v' = transformed vertex, // lvw = local to Vworld Xform // vwip = Vworld to Image plate Xform // v = vertex // Compute composite local to image plate Xform localToImagePlate.mul(lvw); // Transform the Raster's position from object to world coordinates localToImagePlate.transform(objCoord); // Get the window coordinates of this point canvas.getPixelLocationFromImagePlate(objCoord, winCoord); return clipCoord3; } @Override int getClassType() { return RASTER_TYPE; } // notifies the Raster mirror object that the image data in a referenced // ImageComponent object is changed. // Currently we are not making use of this information. void notifyImageComponentImageChanged(ImageComponentRetained image, ImageComponentUpdateInfo value) { } @Override boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, GeometryRetained geom, int geomIndex) { return false; } @Override boolean intersect(Bounds targetBound) { return false; } @Override boolean intersect(Point3d[] pnts) { return false; } @Override boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom) { return false; } @Override boolean intersect(Transform3D thisLocalToVworld, Transform3D otherLocalToVworld, GeometryRetained geom) { return false; } @Override boolean intersect(Transform3D thisLocalToVworld, Bounds targetBound) { return false; } @Override void handleFrequencyChange(int bit) { if (bit == Raster.ALLOW_IMAGE_WRITE) setFrequencyChangeMask(bit, 0x1); } }