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