/* * Copyright 1999-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.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Paint; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.RenderingHints.Key; import java.awt.Shape; import java.awt.Stroke; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.ImageObserver; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.RenderableImage; import java.text.AttributedCharacterIterator; import java.util.Map; /** * Implementation class for J3DGraphics2D */ final class J3DGraphics2DImpl extends J3DGraphics2D { private boolean hasBeenDisposed = false; private Graphics2D offScreenGraphics2D; private BufferedImage g3dImage = null; private byte[] data = null; private boolean isFlushed = true; private Canvas3D canvas3d; private int width, height; private int texWidth, texHeight; private int xmin, ymin, xmax, ymax; private Object extentLock = new Object(); private boolean abgr; private boolean initTexMap = false; private boolean strokeSet=false; private Point2D.Float ptSrc = new Point2D.Float(); private Point2D.Float ptDst1 = new Point2D.Float(); private Point2D.Float ptDst2 = new Point2D.Float(); private Color xOrModeColor = null; private volatile boolean initCtx = false; private volatile boolean threadWaiting = false; static final Color blackTransparent = new Color(0,0,0,0); int objectId = -1; // Package scope contructor J3DGraphics2DImpl(Canvas3D c) { canvas3d = c; synchronized (VirtualUniverse.mc.contextCreationLock) { if (c.ctx == null) { // create a dummy bufferImage width = 1; height = 1; g3dImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); offScreenGraphics2D = g3dImage.createGraphics(); } else { init(); } } } // This is invoke from Renderer callback when the first // time createContext() finish which set // canvas3d.extensionSupported correctly. void init() { // if ABGR extension is supported, we want to use // TYPE_4BYTE_ABGR to make a fast copy if (!initCtx) { abgr = ((canvas3d.extensionsSupported & Canvas3D.EXT_ABGR) != 0); width = canvas3d.getWidth(); height = canvas3d.getHeight(); initTexMap = false; if (width <= 0) { width = 1; } if (height <= 0) { height = 1; } synchronized (extentLock) { xmax = width; ymax = height; xmin = 0; ymin = 0; } g3dImage = new BufferedImage(width, height, (abgr ? BufferedImage.TYPE_4BYTE_ABGR: BufferedImage.TYPE_INT_ARGB)); offScreenGraphics2D = g3dImage.createGraphics(); clearOffScreen(); if (!abgr) { data = new byte[width*height*4]; } // should be the last flag to set initCtx = true; } } /** * Flushes all previously executed rendering operations to the * drawing buffer for this 2D graphics object. * * @param wait flag indicating whether or not to wait for the * rendering to be complete before returning from this call. */ @Override public void flush(boolean waiting) { if (hasBeenDisposed) { throw new IllegalStateException(J3dI18N.getString("J3DGraphics2D0")); } if (!isFlushed) { // Composite g3dImage into Canvas3D if (Thread.currentThread() == canvas3d.screen.renderer) { if (!initCtx) { return; } doFlush(); } else { if (!initCtx) { if (waiting && (canvas3d.pendingView != null) && canvas3d.pendingView.activeStatus) { // wait until Renderer init() this context while (!initCtx) { MasterControl.threadYield(); } } else { return; } } // Behavior Scheduler or other threads // XXXX: may not be legal for behaviorScheduler // May cause deadlock if it is in behaviorScheduler // and we wait for Renderer to finish boolean renderRun = (Thread.currentThread() != canvas3d.view.universe.behaviorScheduler); // This must put before sendRenderMessage() threadWaiting = true; sendRenderMessage(renderRun, GraphicsContext3D.FLUSH2D, null, null, null); if (waiting) { // It is possible that thread got notify BEFORE // the following runMonitor invoke. runMonitor(J3dThread.WAIT); } } isFlushed = true; } } // copy the data into a byte buffer that will be passed to opengl void doFlush() { assert !hasBeenDisposed; // clip to offscreen buffer size if (canvas3d.ctx == null) { canvas3d.getGraphicsContext3D().doClear(); } synchronized (extentLock) { if (xmin < 0) { xmin = 0; } if (xmax > width) { xmax = width; } if (ymin < 0) { ymin = 0; } if (ymax > height) { ymax = height; } if ((xmax - xmin > 0) && (ymax - ymin > 0)) { if (abgr) { data = ((DataBufferByte)g3dImage.getRaster().getDataBuffer()).getData(); } else { copyImage(g3dImage, data, width, height, xmin, ymin, xmax, ymax); } copyDataToCanvas(0, 0, xmin, ymin, xmax, ymax, width, height); } else { runMonitor(J3dThread.NOTIFY); } // this define an empty region xmax = 0; ymax = 0; xmin = width; ymin = height; } } // borrowed from ImageComponentRetained since ImageComponent2D // seems to do stuff we don't need to final void copyImage(BufferedImage bi, byte[] image, int width, int height, int x1, int y1, int x2, int y2) { assert !hasBeenDisposed; int biType = bi.getType(); int w, h, i, j; int row, rowBegin, rowInc, dstBegin; dstBegin = 0; rowInc = 1; rowBegin = 0; // convert format to RGBA for underlying OGL use if ((biType == BufferedImage.TYPE_INT_ARGB) || (biType == BufferedImage.TYPE_INT_RGB)) { // optimized cases rowBegin = y1; int colBegin = x1; int[] intData = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData(); int rowOffset = rowInc * width; int intPixel; rowBegin = rowBegin*width + colBegin; dstBegin = rowBegin*4; if (biType == BufferedImage.TYPE_INT_ARGB) { for (h = y1; h < y2; h++) { i = rowBegin; j = dstBegin; for (w = x1; w < x2; w++, i++) { intPixel = intData[i]; image[j++] = (byte)((intPixel >> 16) & 0xff); image[j++] = (byte)((intPixel >> 8) & 0xff); image[j++] = (byte)(intPixel & 0xff); image[j++] = (byte)((intPixel >> 24) & 0xff); } rowBegin += rowOffset; dstBegin += (rowOffset*4); } } else { for (h = y1; h < y2; h++) { i = rowBegin; j = dstBegin; for (w = x1; w < x2; w++, i++) { intPixel = intData[i]; image[j++] = (byte)((intPixel >> 16) & 0xff); image[j++] = (byte)((intPixel >> 8) & 0xff); image[j++] = (byte)(intPixel & 0xff); image[j++] = (byte)255; } rowBegin += rowOffset; dstBegin += (rowOffset*4); } } } else { // non-optimized cases WritableRaster ras = bi.getRaster(); ColorModel cm = bi.getColorModel(); Object pixel = ImageComponentRetained.getDataElementBuffer(ras); j = (y1*width + x1)*4; for (h = y1; h < y2; h++) { i = j; for (w = x1; w < x2; w++) { ras.getDataElements(w, h, pixel); image[j++] = (byte)cm.getRed(pixel); image[j++] = (byte)cm.getGreen(pixel); image[j++] = (byte)cm.getBlue(pixel); image[j++] = (byte)cm.getAlpha(pixel); } j = i+ width*4; } } } void sendRenderMessage(boolean renderRun, int command, Object arg1, Object arg2, Object arg3) { // send a message to the request renderer J3dMessage renderMessage = new J3dMessage(); renderMessage.threads = J3dThread.RENDER_THREAD; renderMessage.type = J3dMessage.RENDER_IMMEDIATE; renderMessage.universe = null; renderMessage.view = null; renderMessage.args[0] = canvas3d; renderMessage.args[1] = new Integer(command); renderMessage.args[2] = arg1; renderMessage.args[3] = arg2; renderMessage.args[4] = arg3; while (!canvas3d.view.inRenderThreadData) { // wait until the renderer thread data in added in // MC:RenderThreadData array ready to receive message MasterControl.threadYield(); } canvas3d.screen.renderer.rendererStructure.addMessage(renderMessage); if (renderRun) { // notify mc that there is work to do VirtualUniverse.mc.sendRunMessage(canvas3d.view, J3dThread.RENDER_THREAD); } else { // notify mc that there is work for the request renderer VirtualUniverse.mc.setWorkForRequestRenderer(); } } final void validate() { validate(0, 0, width, height); } void validate(float x1, float y1, float x2, float y2, AffineTransform xform) { float t; if (xform == null) { validate(x1, y1, x2, y2); } else { ptSrc.x = x1; ptSrc.y = y1; xform.transform(ptSrc, ptDst1); ptSrc.x = x2; ptSrc.y = y2; xform.transform(ptSrc, ptDst2); if (ptDst1.x > ptDst2.x) { t = ptDst1.x; ptDst1.x = ptDst2.x; ptDst2.x = t; } if (ptDst1.y > ptDst2.y) { t = ptDst1.y; ptDst1.y = ptDst2.y; ptDst2.y = t; } // take care of numerical error by adding 1 validate(ptDst1.x-1, ptDst1.y-1, ptDst2.x+1, ptDst2.y+1); } } void validate(float x1, float y1, float x2, float y2) { boolean doResize = false; isFlushed = false; synchronized(canvas3d) { if (initCtx && canvas3d.resizeGraphics2D) { doResize = true; canvas3d.resizeGraphics2D = false; } } if (doResize) { synchronized (VirtualUniverse.mc.contextCreationLock) { Graphics2D oldOffScreenGraphics2D = offScreenGraphics2D; initCtx = false; init(); copyGraphics2D(oldOffScreenGraphics2D); } } else { AffineTransform tr = getTransform(); ptSrc.x = x1; ptSrc.y = y1; tr.transform(ptSrc, ptDst1); ptSrc.x = x2; ptSrc.y = y2; tr.transform(ptSrc, ptDst2); synchronized (extentLock) { if (ptDst1.x < xmin) { xmin = (int) ptDst1.x; } if (ptDst1.y < ymin) { ymin = (int) ptDst1.y; } if (ptDst2.x > xmax) { xmax = (int) ptDst2.x; } if (ptDst2.y > ymax) { ymax = (int) ptDst2.y; } } } } void copyGraphics2D(Graphics2D oldg) { // restore the original setting of Graphics2D when resizing the windows setColor(oldg.getColor()); setFont(oldg.getFont()); setClip(oldg.getClip()); setComposite(oldg.getComposite()); setTransform(oldg.getTransform()); setPaint(oldg.getPaint()); setStroke(oldg.getStroke()); if (xOrModeColor != null) { setXORMode(xOrModeColor); } } // Implementation of Graphics2D methods @Override public final void clip(Shape s) { offScreenGraphics2D.clip(s); } @Override public FontMetrics getFontMetrics() { return offScreenGraphics2D.getFontMetrics(); } @Override public Rectangle getClipBounds(Rectangle r) { return offScreenGraphics2D.getClipBounds(r); } @Override public Rectangle getClipRect() { return offScreenGraphics2D.getClipRect(); } @Override public String toString() { return offScreenGraphics2D.toString(); } @Override public final AffineTransform getTransform() { return offScreenGraphics2D.getTransform(); } @Override public final Color getColor() { return offScreenGraphics2D.getColor(); } @Override public final Composite getComposite() { return offScreenGraphics2D.getComposite(); } @Override public final Font getFont() { return offScreenGraphics2D.getFont(); } @Override public final FontMetrics getFontMetrics(Font f) { return offScreenGraphics2D.getFontMetrics(f); } @Override public final FontRenderContext getFontRenderContext() { return offScreenGraphics2D.getFontRenderContext(); } @Override public final GraphicsConfiguration getDeviceConfiguration() { return offScreenGraphics2D.getDeviceConfiguration(); } @Override public final Object getRenderingHint(Key hintKey) { return offScreenGraphics2D.getRenderingHint(hintKey); } @Override public final Paint getPaint() { return offScreenGraphics2D.getPaint(); } @Override public final Rectangle getClipBounds() { return offScreenGraphics2D.getClipBounds(); } @Override public final RenderingHints getRenderingHints() { return offScreenGraphics2D.getRenderingHints(); } @Override public final Shape getClip() { return offScreenGraphics2D.getClip(); } @Override public final Stroke getStroke() { return offScreenGraphics2D.getStroke(); } @Override public final boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { validate(0, 0, img.getWidth(obs), img.getHeight(obs), xform); return offScreenGraphics2D.drawImage(img, xform, obs); } @Override public final void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { if (op != null) { img = op.filter(img, null); } validate(x, y, x+img.getWidth(), y+img.getHeight()); offScreenGraphics2D.drawImage(img, null, x, y); } @Override public final boolean drawImage(Image img, int x, int y, ImageObserver observer) { validate(x, y, x + img.getWidth(observer), y + img.getWidth(observer)); return offScreenGraphics2D.drawImage(img, x, y, observer); } @Override public final boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { validate(x, y, x+width, y+height); return offScreenGraphics2D.drawImage(img, x, y, width, height, observer); } @Override public final boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { validate(x, y, x+width, y+height); return offScreenGraphics2D.drawImage(img, x, y, width, height, bgcolor, observer); } public final void drawImage(BufferedImage img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { validate(dx1, dy1, dx2, dy2); offScreenGraphics2D.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } @Override public final boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { validate(dx1, dy1, dx2, dy2); return offScreenGraphics2D.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } @Override public final boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { validate(dx1, dy1, dx2, dy2); return offScreenGraphics2D.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); } @Override public final boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { validate(x, y, x+img.getWidth(observer), y+img.getHeight(observer)); return offScreenGraphics2D.drawImage(img, x, y, bgcolor, observer); } @Override public final boolean hit(Rectangle rect, Shape s, boolean onStroke) { return offScreenGraphics2D.hit(rect, s, onStroke); } @Override public final void addRenderingHints(Map hints) { offScreenGraphics2D.addRenderingHints(hints); } @Override public final void clipRect(int x, int y, int width, int height) { offScreenGraphics2D.clipRect(x, y, width, height); } @Override public final void copyArea(int x, int y, int width, int height, int dx, int dy) { validate(x+dx, y+dy, x+dx+width, y+dy+height); offScreenGraphics2D.copyArea(x, y, width, height, dx, dy); } @Override public final void draw(Shape s) { Rectangle rect = s.getBounds(); validate(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); offScreenGraphics2D.draw(s); } @Override public final void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawArc(x, y, width, height, startAngle, arcAngle); } @Override public final void drawGlyphVector(GlyphVector g, float x, float y) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawGlyphVector(g, x, y); } @Override public final void drawLine(int x1, int y1, int x2, int y2) { int minx, miny, maxx, maxy; if (!strokeSet) { if (x1 > x2) { minx = x2; maxx = x1; } else { minx = x1; maxx = x2; } if (y1 > y2) { miny = y2; maxy = y1; } else { miny = y1; maxy = y2; } validate(minx, miny, maxx, maxy); } else { // XXXX: call validate with bounding box of primitive // XXXX: Need to consider Stroke width validate(); } offScreenGraphics2D.drawLine(x1, y1, x2, y2); } @Override public final void drawOval(int x, int y, int width, int height) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawOval(x, y, width, height); } @Override public final void drawPolygon(int xPoints[], int yPoints[], int nPoints) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawPolygon(xPoints, yPoints, nPoints); } @Override public final void drawPolyline(int xPoints[], int yPoints[], int nPoints) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawPolyline(xPoints, yPoints, nPoints); } @Override public final void drawRenderableImage(RenderableImage img, AffineTransform xform) { validate(0, 0, img.getWidth(), img.getHeight(), xform); offScreenGraphics2D.drawRenderableImage(img, xform); } @Override public final void drawRenderedImage(RenderedImage img, AffineTransform xform) { validate(0, 0, img.getWidth(), img.getHeight(), xform); offScreenGraphics2D.drawRenderedImage(img, xform); } @Override public final void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawRoundRect(x, y, width, height, arcWidth, arcHeight); } @Override public final void drawString(AttributedCharacterIterator iterator, int x, int y) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawString(iterator, x, y); } @Override public final void drawString(AttributedCharacterIterator iterator, float x, float y) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawString(iterator, x, y); } @Override public final void drawString(String s, float x, float y) { TextLayout layout = new TextLayout(s, getFont(), getFontRenderContext()); Rectangle2D bounds = layout.getBounds(); float x1 = (float) bounds.getX(); float y1 = (float) bounds.getY(); validate(x1+x, y1+y, x1 + x + (float) bounds.getWidth(), y1 + y + (float) bounds.getHeight()); offScreenGraphics2D.drawString(s, x, y); } @Override public final void drawString(String s, int x, int y) { drawString(s, (float) x, (float) y); } @Override public final void fill(Shape s) { Rectangle rect = s.getBounds(); validate(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); offScreenGraphics2D.fill(s); } @Override public final void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.fillArc(x, y, width, height, startAngle, arcAngle); } @Override public final void fillOval(int x, int y, int width, int height) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.fillOval(x, y, width, height); } @Override public final void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.fillRoundRect(x, y, width, height, arcWidth, arcHeight); } @Override public final void rotate(double theta) { offScreenGraphics2D.rotate(theta); } @Override public final void rotate(double theta, double x, double y) { offScreenGraphics2D.rotate(theta, x, y); } @Override public final void scale(double sx, double sy) { offScreenGraphics2D.scale(sx, sy); } @Override public final void setClip(Shape clip) { offScreenGraphics2D.setClip(clip); } @Override public final void setClip(int x, int y, int width, int height) { offScreenGraphics2D.setClip(x, y, width, height); } @Override public final void setColor(Color c) { offScreenGraphics2D.setColor(c); } @Override public final void setComposite(Composite comp) { offScreenGraphics2D.setComposite(comp); } @Override public final void setFont(Font font) { offScreenGraphics2D.setFont(font); } @Override public final void setPaint( Paint paint ) { offScreenGraphics2D.setPaint(paint); } @Override public final void setPaintMode() { xOrModeColor = null; offScreenGraphics2D.setPaintMode(); } @Override public final void setRenderingHint(Key hintKey, Object hintValue) { offScreenGraphics2D.setRenderingHint(hintKey, hintValue); } @Override public final void setRenderingHints(Map hints) { offScreenGraphics2D.setRenderingHints(hints); } @Override public final void setStroke(Stroke s) { strokeSet = (s != null); offScreenGraphics2D.setStroke(s); } @Override public final void setTransform(AffineTransform Tx) { offScreenGraphics2D.setTransform(Tx); } @Override public final void setXORMode(Color c1) { xOrModeColor = c1; offScreenGraphics2D.setXORMode(c1); } @Override public final void shear(double shx, double shy) { offScreenGraphics2D.shear(shx, shy); } @Override public final void transform(AffineTransform Tx) { offScreenGraphics2D.transform(Tx); } @Override public final void translate(double tx, double ty) { offScreenGraphics2D.translate(tx, ty); } @Override public final void translate(int x, int y) { offScreenGraphics2D.translate(x, y); } @Override public boolean hitClip(int x, int y, int width, int height) { return offScreenGraphics2D.hitClip(x, y, width, height); } @Override public void draw3DRect(int x, int y, int width, int height, boolean raised) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.draw3DRect(x, y, width, height, raised); } @Override public void drawBytes(byte data[], int offset, int length, int x, int y) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawBytes(data, offset, length, x, y); } @Override public void drawChars(char data[], int offset, int length, int x, int y) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawChars(data, offset, length, x, y); } @Override public void drawPolygon(Polygon p) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.drawPolygon(p); } @Override public void drawRect(int x, int y, int width, int height) { // XXXX: call validate with bounding box of primitive // XXXX: need to consider Stroke width validate(); offScreenGraphics2D.drawRect(x, y, width, height); } @Override public void fill3DRect(int x, int y, int width, int height, boolean raised) { // XXXX: call validate with bounding box of primitive // XXXX: need to consider Stroke width validate(); offScreenGraphics2D.fill3DRect(x, y, width, height, raised); } @Override public void fillPolygon(Polygon p) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.fillPolygon(p); } @Override public final void fillPolygon(int xPoints[], int yPoints[], int nPoints) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.fillPolygon(xPoints, yPoints, nPoints); } @Override public final void fillRect(int x, int y, int width, int height) { // XXXX: call validate with bounding box of primitive validate(); offScreenGraphics2D.fillRect(x, y, width, height); } // Issue 121 - release all resources, mark as disposed @Override public void dispose() { // Issue 583 - do nothing if graphics has already been disposed if (hasBeenDisposed) { return; } if (Thread.currentThread() == canvas3d.screen.renderer) { doDispose(); } else { // Behavior Scheduler or other threads // XXXX: may not be legal for behaviorScheduler // May cause deadlock if it is in behaviorScheduler // and we wait for Renderer to finish boolean renderRun = (Thread.currentThread() != canvas3d.view.universe.behaviorScheduler); sendRenderMessage(renderRun, GraphicsContext3D.DISPOSE2D, null, null, null); } } public void doDispose() { if (hasBeenDisposed) { return; } if (objectId != -1) { Canvas3D.freeTexture(canvas3d.ctx, objectId); objectId = -1; } // Dispose of the underlying Graphics2D offScreenGraphics2D.dispose(); // Mark as disposed hasBeenDisposed = true; // Issue 583 - set graphics2D field to null so it will get recreated canvas3d.graphics2D = null; } @Override public void drawAndFlushImage(BufferedImage img, int x, int y, ImageObserver observer) { if (hasBeenDisposed) { throw new IllegalStateException(J3dI18N.getString("J3DGraphics2D0")); } if (!(initCtx && abgr && (img.getType() == BufferedImage.TYPE_4BYTE_ABGR))) { drawImage(img, x, y, observer); flush(false); return; } if (Thread.currentThread() == canvas3d.screen.renderer) { doDrawAndFlushImage(img, x, y, observer); } else { // Behavior Scheduler or other threads // XXXX: may not be legal for behaviorScheduler // May cause deadlock if it is in behaviorScheduler // and we wait for Renderer to finish boolean renderRun = (Thread.currentThread() != canvas3d.view.universe.behaviorScheduler); sendRenderMessage(renderRun, GraphicsContext3D.DRAWANDFLUSH2D, img, new Point(x, y), observer); } } void doDrawAndFlushImage(BufferedImage img, int x, int y, ImageObserver observer) { assert !hasBeenDisposed; int imgWidth = img.getWidth(observer); int imgHeight = img.getHeight(observer); int px, py, x1, y1, x2, y2; if (canvas3d.ctx == null) { canvas3d.getGraphicsContext3D().doClear(); } // format needs to be 4BYTE_ABGR and abgr needs to be supported // also must be in canvas callback data = ((DataBufferByte)img.getRaster().getDataBuffer()).getData(); // Transform the affine transform, // note we do not handle scale/rotate etc. AffineTransform tr = getTransform(); ptSrc.x = x; ptSrc.y = y; tr.transform(ptSrc, ptDst1); px = (int) ptDst1.x; py = (int) ptDst1.y; // clip to offscreen buffer size if (px + imgWidth > width) { x2 = width - px; } else { x2 = imgWidth; } if (px < 0) { x1 = -px; px = 0; } else { x1 = 0; } if (py + imgHeight > height) { y2 = height - py; } else { y2 = imgHeight; } if (py < 0) { y1 = -py; py = 0; } else { y1 = 0; } if ((y2 - y1 > 0) && (x2 - x1 > 0)) { copyDataToCanvas(px, py, x1,y1, x2, y2,imgWidth, imgHeight); } } void copyDataToCanvas(int px, int py, int x1, int y1, int x2, int y2, int w, int h) { try { if (!canvas3d.drawingSurfaceObject.renderLock()) { return; } if (!initTexMap) { if (objectId == -1) { objectId = Canvas3D.generateTexID(canvas3d.ctx); } texWidth = getGreaterPowerOf2(w); texHeight = getGreaterPowerOf2(h); // Canvas got resize, need to init texture map again // in Renderer thread if (!canvas3d.initTexturemapping(canvas3d.ctx, texWidth, texHeight, objectId)) { // Fail to get the texture surface, most likely // there is not enough texture memory initTexMap = false; Canvas3D.freeTexture(canvas3d.ctx, objectId); objectId = -1; // TODO : Need to find a better way to report no resource problem --- Chien. System.err.println("J3DGraphics2DImpl.copyDataToCanvas() : Fail to get texture resources ..."); } else { initTexMap = true; } } if (initTexMap) { canvas3d.texturemapping(canvas3d.ctx, px, py, x1, y1, x2, y2, texWidth, texHeight, w, (abgr ? ImageComponentRetained.TYPE_BYTE_ABGR: ImageComponentRetained.TYPE_BYTE_RGBA), objectId, data, width, height); } canvas3d.drawingSurfaceObject.unLock(); } catch (NullPointerException ne) { canvas3d.drawingSurfaceObject.unLock(); throw ne; } clearOffScreen(); runMonitor(J3dThread.NOTIFY); } void clearOffScreen() { Composite comp = offScreenGraphics2D.getComposite(); Color c = offScreenGraphics2D.getColor(); offScreenGraphics2D.setComposite(AlphaComposite.Src); offScreenGraphics2D.setColor(blackTransparent); offScreenGraphics2D.fillRect(xmin, ymin, (xmax-xmin), (ymax-ymin)); offScreenGraphics2D.setComposite(comp); offScreenGraphics2D.setColor(c); } /** * Return an integer of power 2 greater than x */ static int getGreaterPowerOf2(int x) { int i = -1; if (x >= 0) { for (i = 1; i < x; i <<= 1); } return i; } /** * MC may not scheduler Renderer thread or Renderer thread * may not process message FLUSH. This will hang user * thread. */ synchronized void runMonitor(int action) { if (action == J3dThread.WAIT) { // Issue 279 - loop until ready while (threadWaiting) { try { wait(); } catch (InterruptedException e){} } } else if (action == J3dThread.NOTIFY) { notify(); threadWaiting = false; } } }