From bade983019dff85f59cdaadc1be25410ea7afc93 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sun, 16 Jan 2022 14:51:02 -0500 Subject: [PATCH] removing JavaFX (in progress) --- .idea/modules.xml | 1 - java/libraries/javafx/.classpath | 17 - java/libraries/javafx/.gitignore | 5 - java/libraries/javafx/.project | 17 - .../.settings/org.eclipse.jdt.core.prefs | 15 - java/libraries/javafx/build.xml | 181 -- java/libraries/javafx/library.properties | 2 - java/libraries/javafx/processing4-javafx.iml | 35 - .../src/processing/javafx/PGraphicsFX2D.java | 2330 ----------------- .../src/processing/javafx/PSurfaceFX.java | 1090 -------- 10 files changed, 3693 deletions(-) delete mode 100644 java/libraries/javafx/.classpath delete mode 100644 java/libraries/javafx/.gitignore delete mode 100644 java/libraries/javafx/.project delete mode 100644 java/libraries/javafx/.settings/org.eclipse.jdt.core.prefs delete mode 100644 java/libraries/javafx/build.xml delete mode 100644 java/libraries/javafx/library.properties delete mode 100644 java/libraries/javafx/processing4-javafx.iml delete mode 100644 java/libraries/javafx/src/processing/javafx/PGraphicsFX2D.java delete mode 100644 java/libraries/javafx/src/processing/javafx/PSurfaceFX.java diff --git a/.idea/modules.xml b/.idea/modules.xml index ab31269e4..c60fa0002 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -15,7 +15,6 @@ - \ No newline at end of file diff --git a/java/libraries/javafx/.classpath b/java/libraries/javafx/.classpath deleted file mode 100644 index cfc5fc9ac..000000000 --- a/java/libraries/javafx/.classpath +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/java/libraries/javafx/.gitignore b/java/libraries/javafx/.gitignore deleted file mode 100644 index 4d04bf96b..000000000 --- a/java/libraries/javafx/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# ignore the sdk download files -javafx-*.zip - -# everything is downloaded from online -/library diff --git a/java/libraries/javafx/.project b/java/libraries/javafx/.project deleted file mode 100644 index 7dbb5da11..000000000 --- a/java/libraries/javafx/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-javafx - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/java/libraries/javafx/.settings/org.eclipse.jdt.core.prefs b/java/libraries/javafx/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index cd8d089a1..000000000 --- a/java/libraries/javafx/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,15 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=11 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=11 diff --git a/java/libraries/javafx/build.xml b/java/libraries/javafx/build.xml deleted file mode 100644 index b6a9b6882..000000000 --- a/java/libraries/javafx/build.xml +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/java/libraries/javafx/library.properties b/java/libraries/javafx/library.properties deleted file mode 100644 index 00d042f25..000000000 --- a/java/libraries/javafx/library.properties +++ /dev/null @@ -1,2 +0,0 @@ -name = JavaFX -version = 1 diff --git a/java/libraries/javafx/processing4-javafx.iml b/java/libraries/javafx/processing4-javafx.iml deleted file mode 100644 index e169dfb69..000000000 --- a/java/libraries/javafx/processing4-javafx.iml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/java/libraries/javafx/src/processing/javafx/PGraphicsFX2D.java b/java/libraries/javafx/src/processing/javafx/PGraphicsFX2D.java deleted file mode 100644 index 4e73a1709..000000000 --- a/java/libraries/javafx/src/processing/javafx/PGraphicsFX2D.java +++ /dev/null @@ -1,2330 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2015 The Processing Foundation - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation, version 2.1. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA -*/ - -package processing.javafx; - -import com.sun.javafx.geom.Path2D; -import com.sun.javafx.geom.PathIterator; -import com.sun.javafx.geom.Shape; - -import java.nio.IntBuffer; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; - -import javafx.scene.SnapshotParameters; -import javafx.scene.canvas.GraphicsContext; -import javafx.scene.effect.BlendMode; -import javafx.scene.image.PixelFormat; -import javafx.scene.image.PixelReader; -import javafx.scene.image.PixelWriter; -import javafx.scene.image.WritableImage; -import javafx.scene.image.WritablePixelFormat; -import javafx.scene.paint.Color; -import javafx.scene.shape.ArcType; -import javafx.scene.shape.StrokeLineCap; -import javafx.scene.shape.StrokeLineJoin; -import javafx.scene.text.Font; -import javafx.scene.text.Text; -import javafx.scene.transform.Affine; -import javafx.scene.transform.Transform; - -import processing.core.*; - - -public class PGraphicsFX2D extends PGraphics { - GraphicsContext context; - - static final WritablePixelFormat argbFormat = - PixelFormat.getIntArgbInstance(); - - WritableImage snapshotImage; - - Path2D workPath = new Path2D(); - Path2D auxPath = new Path2D(); - boolean openContour; - boolean adjustedForThinLines; - /// break the shape at the next vertex (next vertex() call is a moveto()) - boolean breakShape; - - private float[] pathCoordsBuffer = new float[6]; - - /// coordinates for internal curve calculation - float[] curveCoordX; - float[] curveCoordY; - float[] curveDrawX; - float[] curveDrawY; - - int transformCount; - Affine[] transformStack = new Affine[MATRIX_STACK_DEPTH]; - -// Line2D.Float line = new Line2D.Float(); -// Ellipse2D.Float ellipse = new Ellipse2D.Float(); -// Rectangle2D.Float rect = new Rectangle2D.Float(); -// Arc2D.Float arc = new Arc2D.Float(); -// -// protected Color tintColorObject; -// -// protected Color fillColorObject; -// public boolean fillGradient; -// public Paint fillGradientObject; -// -// protected Color strokeColorObject; -// public boolean strokeGradient; -// public Paint strokeGradientObject; - - - - ////////////////////////////////////////////////////////////// - - // INTERNAL - - - public PGraphicsFX2D() { } - - - //public void setParent(PApplet parent) - - - //public void setPrimary(boolean primary) - - - //public void setPath(String path) - - - //public void setSize(int width, int height) - - - //public void dispose() - - - @Override - public PSurface createSurface() { - return surface = new PSurfaceFX(this); - } - - - /** Returns the javafx.scene.canvas.GraphicsContext used by this renderer. */ - @Override - public Object getNative() { - return context; - } - - - ////////////////////////////////////////////////////////////// - - // FRAME - - -// @Override -// public boolean canDraw() { -// return true; -// } - - - @Override - public void beginDraw() { - checkSettings(); - resetMatrix(); // reset model matrix - vertexCount = 0; - } - - - @Override - public void endDraw() { - flush(); - - if (!primaryGraphics) { - // TODO this is probably overkill for most tasks... - loadPixels(); - } - } - - - - ////////////////////////////////////////////////////////////// - - // SETTINGS - - - //protected void checkSettings() - - - //protected void defaultSettings() - - - //protected void reapplySettings() - - - - ////////////////////////////////////////////////////////////// - - // HINT - - - //public void hint(int which) - - - - ////////////////////////////////////////////////////////////// - - // SHAPE CREATION - - - //protected PShape createShapeFamily(int type) - - - //protected PShape createShapePrimitive(int kind, float... p) - - - - ////////////////////////////////////////////////////////////// - - // SHAPE - - - @Override - public void beginShape(int kind) { - shape = kind; - vertexCount = 0; - curveVertexCount = 0; - - workPath.reset(); - auxPath.reset(); - - flushPixels(); - - if (drawingThinLines()) { - adjustedForThinLines = true; - translate(0.5f, 0.5f); - } - } - - - //public boolean edge(boolean e) - - - //public void normal(float nx, float ny, float nz) { - - - //public void textureMode(int mode) - - - @Override - public void texture(PImage image) { - showMethodWarning("texture"); - } - - - @Override - public void vertex(float x, float y) { - if (vertexCount == vertices.length) { - float[][] temp = new float[vertexCount<<1][VERTEX_FIELD_COUNT]; - System.arraycopy(vertices, 0, temp, 0, vertexCount); - vertices = temp; - //message(CHATTER, "allocating more vertices " + vertices.length); - } - // not everyone needs this, but just easier to store rather - // than adding another moving part to the code... - vertices[vertexCount][X] = x; - vertices[vertexCount][Y] = y; - vertexCount++; - - switch (shape) { - - case POINTS: - point(x, y); - break; - - case LINES: - if ((vertexCount % 2) == 0) { - line(vertices[vertexCount-2][X], - vertices[vertexCount-2][Y], x, y); - } - break; - - case TRIANGLES: - if ((vertexCount % 3) == 0) { - triangle(vertices[vertexCount - 3][X], - vertices[vertexCount - 3][Y], - vertices[vertexCount - 2][X], - vertices[vertexCount - 2][Y], - x, y); - } - break; - - case TRIANGLE_STRIP: - if (vertexCount >= 3) { - triangle(vertices[vertexCount - 2][X], - vertices[vertexCount - 2][Y], - vertices[vertexCount - 1][X], - vertices[vertexCount - 1][Y], - vertices[vertexCount - 3][X], - vertices[vertexCount - 3][Y]); - } - break; - - case TRIANGLE_FAN: - if (vertexCount >= 3) { - // This is an unfortunate implementation because the stroke for an - // adjacent triangle will be repeated. However, if the stroke is not - // redrawn, it will replace the adjacent line (when it lines up - // perfectly) or show a faint line (when off by a small amount). - // The alternative would be to wait, then draw the shape as a - // polygon fill, followed by a series of vertices. But that's a - // poor method when used with PDF, DXF, or other recording objects, - // since discrete triangles would likely be preferred. - triangle(vertices[0][X], - vertices[0][Y], - vertices[vertexCount - 2][X], - vertices[vertexCount - 2][Y], - x, y); - } - break; - - case QUAD: - case QUADS: - if ((vertexCount % 4) == 0) { - quad(vertices[vertexCount - 4][X], - vertices[vertexCount - 4][Y], - vertices[vertexCount - 3][X], - vertices[vertexCount - 3][Y], - vertices[vertexCount - 2][X], - vertices[vertexCount - 2][Y], - x, y); - } - break; - - case QUAD_STRIP: - // 0---2---4 - // | | | - // 1---3---5 - if ((vertexCount >= 4) && ((vertexCount % 2) == 0)) { - quad(vertices[vertexCount - 4][X], - vertices[vertexCount - 4][Y], - vertices[vertexCount - 2][X], - vertices[vertexCount - 2][Y], - x, y, - vertices[vertexCount - 3][X], - vertices[vertexCount - 3][Y]); - } - break; - - case POLYGON: - if (workPath.getNumCommands() == 0 || breakShape) { - workPath.moveTo(x, y); - breakShape = false; - } else { - workPath.lineTo(x, y); - } - break; - } - } - - - @Override - public void vertex(float x, float y, float z) { - showDepthWarningXYZ("vertex"); - } - - - @Override - public void vertex(float[] v) { - vertex(v[X], v[Y]); - } - - - @Override - public void vertex(float x, float y, float u, float v) { - showVariationWarning("vertex(x, y, u, v)"); - } - - - @Override - public void vertex(float x, float y, float z, float u, float v) { - showDepthWarningXYZ("vertex"); - } - - - @Override - public void beginContour() { - if (openContour) { - PGraphics.showWarning("Already called beginContour()"); - return; - } - - // draw contours to auxiliary path so main path can be closed later - Path2D contourPath = auxPath; - auxPath = workPath; - workPath = contourPath; - - if (contourPath.getNumCommands() > 0) { // first contour does not break - breakShape = true; - } - - openContour = true; - } - - - @Override - public void endContour() { - if (!openContour) { - PGraphics.showWarning("Need to call beginContour() first"); - return; - } - - if (workPath.getNumCommands() > 0) workPath.closePath(); - - Path2D temp = workPath; - workPath = auxPath; - auxPath = temp; - - openContour = false; - } - - - @Override - public void endShape(int mode) { - if (openContour) { // correct automagically, notify user - endContour(); - PGraphics.showWarning("Missing endContour() before endShape()"); - } - if (workPath.getNumCommands() > 0) { - if (shape == POLYGON) { - if (mode == CLOSE) { - workPath.closePath(); - } - if (auxPath.getNumCommands() > 0) { - workPath.append(auxPath, false); - } - drawShape(workPath); - } - } - shape = 0; - if (adjustedForThinLines) { - adjustedForThinLines = false; - translate(-0.5f, -0.5f); - } - loaded = false; - } - - - private void drawShape(Shape s) { - context.beginPath(); - PathIterator pi = s.getPathIterator(null); - while (!pi.isDone()) { - int piType = pi.currentSegment(pathCoordsBuffer); - switch (piType) { - case PathIterator.SEG_MOVETO: - context.moveTo(pathCoordsBuffer[0], pathCoordsBuffer[1]); - break; - case PathIterator.SEG_LINETO: - context.lineTo(pathCoordsBuffer[0], pathCoordsBuffer[1]); - break; - case PathIterator.SEG_QUADTO: - context.quadraticCurveTo(pathCoordsBuffer[0], pathCoordsBuffer[1], - pathCoordsBuffer[2], pathCoordsBuffer[3]); - break; - case PathIterator.SEG_CUBICTO: - context.bezierCurveTo(pathCoordsBuffer[0], pathCoordsBuffer[1], - pathCoordsBuffer[2], pathCoordsBuffer[3], - pathCoordsBuffer[4], pathCoordsBuffer[5]); - break; - case PathIterator.SEG_CLOSE: - context.closePath(); - break; - default: - showWarning("Unknown segment type " + piType); - } - pi.next(); - } - if (fill) context.fill(); - if (stroke) context.stroke(); - } - - - - ////////////////////////////////////////////////////////////// - - // CLIPPING - - - @Override - protected void clipImpl(float x1, float y1, float x2, float y2) { - //g2.setClip(new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1)); - showTodoWarning("clip()", 3274); - } - - - @Override - public void noClip() { - //g2.setClip(null); - showTodoWarning("noClip()", 3274); - } - - - - ////////////////////////////////////////////////////////////// - - // BLEND - - - @Override - protected void blendModeImpl() { - BlendMode mode = BlendMode.SRC_OVER; - switch (blendMode) { - case REPLACE: showWarning("blendMode(REPLACE) is not supported"); break; - case BLEND: break; // this is SRC_OVER, the default - case ADD: mode = BlendMode.ADD; break; // everyone's favorite - case SUBTRACT: showWarning("blendMode(SUBTRACT) is not supported"); break; - case LIGHTEST: mode = BlendMode.LIGHTEN; break; - case DARKEST: mode = BlendMode.DARKEN; break; - case DIFFERENCE: mode = BlendMode.DIFFERENCE; break; - case EXCLUSION: mode = BlendMode.EXCLUSION; break; - case MULTIPLY: mode = BlendMode.MULTIPLY; break; - case SCREEN: mode = BlendMode.SCREEN; break; - case OVERLAY: mode = BlendMode.OVERLAY; break; - case HARD_LIGHT: mode = BlendMode.HARD_LIGHT; break; - case SOFT_LIGHT: mode = BlendMode.SOFT_LIGHT; break; - case DODGE: mode = BlendMode.COLOR_DODGE; break; - case BURN: mode = BlendMode.COLOR_BURN; break; - } - context.setGlobalBlendMode(mode); - } - - - - ////////////////////////////////////////////////////////////// - - // BEZIER VERTICES - - - @Override - protected void bezierVertexCheck() { - if (shape == 0 || shape != POLYGON) { - throw new RuntimeException("beginShape() or beginShape(POLYGON) " + - "must be used before bezierVertex() or quadraticVertex()"); - } - if (workPath.getNumCommands() == 0) { - throw new RuntimeException("vertex() must be used at least once " + - "before bezierVertex() or quadraticVertex()"); - } - } - - @Override - public void bezierVertex(float x1, float y1, - float x2, float y2, - float x3, float y3) { - bezierVertexCheck(); - workPath.curveTo(x1, y1, x2, y2, x3, y3); - } - - - @Override - public void bezierVertex(float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4) { - showDepthWarningXYZ("bezierVertex"); - } - - - - ////////////////////////////////////////////////////////////// - - // QUADRATIC BEZIER VERTICES - - - @Override - public void quadraticVertex(float ctrlX, float ctrlY, - float endX, float endY) { - bezierVertexCheck(); - workPath.quadTo(ctrlX, ctrlY, endX, endY); - } - - - @Override - public void quadraticVertex(float x2, float y2, float z2, - float x4, float y4, float z4) { - showDepthWarningXYZ("quadVertex"); - } - - - - ////////////////////////////////////////////////////////////// - - // CURVE VERTICES - - - @Override - protected void curveVertexSegment(float x1, float y1, - float x2, float y2, - float x3, float y3, - float x4, float y4) { - if (curveCoordX == null) { - curveCoordX = new float[4]; - curveCoordY = new float[4]; - curveDrawX = new float[4]; - curveDrawY = new float[4]; - } - - curveCoordX[0] = x1; - curveCoordY[0] = y1; - - curveCoordX[1] = x2; - curveCoordY[1] = y2; - - curveCoordX[2] = x3; - curveCoordY[2] = y3; - - curveCoordX[3] = x4; - curveCoordY[3] = y4; - - curveToBezierMatrix.mult(curveCoordX, curveDrawX); - curveToBezierMatrix.mult(curveCoordY, curveDrawY); - - // since the paths are continuous, - // only the first point needs the actual moveto - if (workPath.getNumCommands() == 0) { - workPath.moveTo(curveDrawX[0], curveDrawY[0]); - breakShape = false; - } - - workPath.curveTo(curveDrawX[1], curveDrawY[1], - curveDrawX[2], curveDrawY[2], - curveDrawX[3], curveDrawY[3]); - } - - - @Override - public void curveVertex(float x, float y, float z) { - showDepthWarningXYZ("curveVertex"); - } - - - - ////////////////////////////////////////////////////////////// - - // RENDERER - - @Override - public void flush() { - flushPixels(); - } - - - protected void flushPixels() { - boolean hasPixels = modified && pixels != null; - if (hasPixels) { - // If the user has been manipulating individual pixels, - // the changes need to be copied to the screen before - // drawing any new geometry. - int mx1 = getModifiedX1(); - int mx2 = getModifiedX2(); - int my1 = getModifiedY1(); - int my2 = getModifiedY2(); - int mw = mx2 - mx1; - int mh = my2 - my1; - - if (pixelDensity == 1) { - PixelWriter pw = context.getPixelWriter(); - pw.setPixels(mx1, my1, mw, mh, argbFormat, pixels, - mx1 + my1 * pixelWidth, pixelWidth); - } else { - // The only way to push all the pixels is to draw a scaled-down image - if (snapshotImage == null || - snapshotImage.getWidth() != pixelWidth || - snapshotImage.getHeight() != pixelHeight) { - snapshotImage = new WritableImage(pixelWidth, pixelHeight); - } - - PixelWriter pw = snapshotImage.getPixelWriter(); - pw.setPixels(mx1, my1, mw, mh, argbFormat, pixels, - mx1 + my1 * pixelWidth, pixelWidth); - context.save(); - resetMatrix(); - context.scale(1d / pixelDensity, 1d / pixelDensity); - context.drawImage(snapshotImage, mx1, my1, mw, mh, mx1, my1, mw, mh); - context.restore(); - } - } - - modified = false; - } - - - protected void beforeContextDraw() { - flushPixels(); - loaded = false; - } - - - ////////////////////////////////////////////////////////////// - - // POINT, LINE, TRIANGLE, QUAD - - - @Override - public void point(float x, float y) { - if (stroke) { -// if (strokeWeight > 1) { - line(x, y, x + EPSILON, y + EPSILON); -// } else { -// set((int) screenX(x, y), (int) screenY(x, y), strokeColor); -// } - } - } - - - @Override - public void line(float x1, float y1, float x2, float y2) { - beforeContextDraw(); - if (drawingThinLines()) { - x1 += 0.5f; - x2 += 0.5f; - y1 += 0.5f; - y2 += 0.5f; - } - context.strokeLine(x1, y1, x2, y2); - } - - - @Override - public void triangle(float x1, float y1, float x2, float y2, - float x3, float y3) { - beforeContextDraw(); - if (drawingThinLines()) { - x1 += 0.5f; - x2 += 0.5f; - x3 += 0.5f; - y1 += 0.5f; - y2 += 0.5f; - y3 += 0.5f; - } - context.beginPath(); - context.moveTo(x1, y1); - context.lineTo(x2, y2); - context.lineTo(x3, y3); - context.closePath(); - if (fill) context.fill(); - if (stroke) context.stroke(); - } - - - @Override - public void quad(float x1, float y1, float x2, float y2, - float x3, float y3, float x4, float y4) { - beforeContextDraw(); - if (drawingThinLines()) { - x1 += 0.5f; - x2 += 0.5f; - x3 += 0.5f; - x4 += 0.5f; - y1 += 0.5f; - y2 += 0.5f; - y3 += 0.5f; - y4 += 0.5f; - } - context.beginPath(); - context.moveTo(x1, y1); - context.lineTo(x2, y2); - context.lineTo(x3, y3); - context.lineTo(x4, y4); - context.closePath(); - if (fill) context.fill(); - if (stroke) context.stroke(); - } - - - - ////////////////////////////////////////////////////////////// - - // RECT - - - //public void rectMode(int mode) - - - //public void rect(float a, float b, float c, float d) - - - @Override - protected void rectImpl(float x1, float y1, float x2, float y2) { - beforeContextDraw(); - if (drawingThinLines()) { - x1 += 0.5f; - x2 += 0.5f; - y1 += 0.5f; - y2 += 0.5f; - } - if (fill) context.fillRect(x1, y1, x2 - x1, y2 - y1); - if (stroke) context.strokeRect(x1, y1, x2 - x1, y2 - y1); - } - - - - ////////////////////////////////////////////////////////////// - - // ELLIPSE - - - //public void ellipseMode(int mode) - - - //public void ellipse(float a, float b, float c, float d) - - - @Override - protected void ellipseImpl(float x, float y, float w, float h) { - beforeContextDraw(); - if (drawingThinLines()) { - x += 0.5f; - y += 0.5f; - } - if (fill) context.fillOval(x, y, w, h); - if (stroke) context.strokeOval(x, y, w, h); - } - - - - ////////////////////////////////////////////////////////////// - - // ARC - - - //public void arc(float a, float b, float c, float d, - // float start, float stop) - - - @Override - protected void arcImpl(float x, float y, float w, float h, - float start, float stop, int mode) { - beforeContextDraw(); - - if (drawingThinLines()) { - x += 0.5f; - y += 0.5f; - } - - // 0 to 90 in java would be 0 to -90 for p5 renderer - // but that won't work, so -90 to 0? - start = -start; - stop = -stop; - - float sweep = stop - start; - - // The defaults, before 2.0b7, were to stroke as Arc2D.OPEN, and then fill - // using Arc2D.PIE. That's a little wonky, but it's here for compatibility. - ArcType fillMode = ArcType.ROUND; // Arc2D.PIE - ArcType strokeMode = ArcType.OPEN; - - if (mode == OPEN) { - fillMode = ArcType.OPEN; - - } else if (mode == PIE) { - strokeMode = ArcType.ROUND; // PIE - - } else if (mode == CHORD) { - fillMode = ArcType.CHORD; - strokeMode = ArcType.CHORD; - } - - if (fill) { - context.fillArc(x, y, w, h, PApplet.degrees(start), PApplet.degrees(sweep), fillMode); - } - if (stroke) { - context.strokeArc(x, y, w, h, PApplet.degrees(start), PApplet.degrees(sweep), strokeMode); - } - } - - - - ////////////////////////////////////////////////////////////// - - // BOX - - - //public void box(float size) - - - @Override - public void box(float w, float h, float d) { - showMethodWarning("box"); - } - - - - ////////////////////////////////////////////////////////////// - - // SPHERE - - - //public void sphereDetail(int res) - - - //public void sphereDetail(int ures, int vres) - - - @Override - public void sphere(float r) { - showMethodWarning("sphere"); - } - - - - ////////////////////////////////////////////////////////////// - - // BEZIER - - - //public float bezierPoint(float a, float b, float c, float d, float t) - - - //public float bezierTangent(float a, float b, float c, float d, float t) - - - //protected void bezierInitCheck() - - - //protected void bezierInit() - - - /** Ignored (not needed) by this renderer. */ - @Override - public void bezierDetail(int detail) { } - - - //public void bezier(float x1, float y1, - // float x2, float y2, - // float x3, float y3, - // float x4, float y4) - - - //public void bezier(float x1, float y1, float z1, - // float x2, float y2, float z2, - // float x3, float y3, float z3, - // float x4, float y4, float z4) - - - - ////////////////////////////////////////////////////////////// - - // CURVE - - - //public float curvePoint(float a, float b, float c, float d, float t) - - - //public float curveTangent(float a, float b, float c, float d, float t) - - - /** Ignored (not needed) by this renderer. */ - @Override - public void curveDetail(int detail) { } - - - //public void curveTightness(float tightness) - - - //protected void curveInitCheck() - - - //protected void curveInit() - - - //public void curve(float x1, float y1, - // float x2, float y2, - // float x3, float y3, - // float x4, float y4) - - - //public void curve(float x1, float y1, float z1, - // float x2, float y2, float z2, - // float x3, float y3, float z3, - // float x4, float y4, float z4) - - - - ////////////////////////////////////////////////////////////// - - // SMOOTH - - -// @Override -// public void smooth() { -// smooth = true; -// -// if (quality == 0) { -// quality = 4; // change back to bicubic -// } -// } - - -// @Override -// public void smooth(int quality) { -//// this.quality = quality; -//// if (quality == 0) { -//// noSmooth(); -//// } else { -//// smooth(); -//// } -// showMissingWarning("smooth"); -// } -// -// -// @Override -// public void noSmooth() { -// showMissingWarning("noSmooth"); -// } - - - - ////////////////////////////////////////////////////////////// - - // IMAGE - - - //public void imageMode(int mode) - - - //public void image(PImage image, float x, float y) - - - //public void image(PImage image, float x, float y, float c, float d) - - - //public void image(PImage image, - // float a, float b, float c, float d, - // int u1, int v1, int u2, int v2) - - - /** - * Handle renderer-specific image drawing. - */ - @Override - protected void imageImpl(PImage who, - float x1, float y1, float x2, float y2, - int u1, int v1, int u2, int v2) { - // Image not ready yet, or an error - if (who.width <= 0 || who.height <= 0) return; - - ImageCache cash = (ImageCache) getCache(who); - - // Nuke the cache if the image was resized - if (cash != null) { - if (who.pixelWidth != cash.image.getWidth() || - who.pixelHeight != cash.image.getHeight()) { - cash = null; - } - } - - if (cash == null) { - //System.out.println("making new image cache"); - cash = new ImageCache(); //who); - setCache(who, cash); - who.updatePixels(); // mark the whole thing for update - who.setModified(); - } - - // If image previously was tinted, or the color changed - // or the image was tinted, and tint is now disabled - if ((tint && !cash.tinted) || - (tint && (cash.tintedColor != tintColor)) || - (!tint && cash.tinted)) { - // For tint change, mark all pixels as needing update. - who.updatePixels(); - } - - if (who.isModified()) { - if (who.pixels == null) { - // This might be a PGraphics that hasn't been drawn to yet. - // Can't just bail because the cache has been created above. - // https://github.com/processing/processing/issues/2208 - who.pixels = new int[who.pixelWidth * who.pixelHeight]; - } - cash.update(who, tint, tintColor); - who.setModified(false); - } - - u1 *= who.pixelDensity; - v1 *= who.pixelDensity; - u2 *= who.pixelDensity; - v2 *= who.pixelDensity; - - context.drawImage(((ImageCache) getCache(who)).image, - u1, v1, u2-u1, v2-v1, - x1, y1, x2-x1, y2-y1); - } - - - static class ImageCache { - boolean tinted; - int tintedColor; - int[] tintedTemp; // one row of tinted pixels - //BufferedImage image; - WritableImage image; - - /** - * Update the pixels of the cache image. Already determined that the tint - * has changed, or the pixels have changed, so should just go through - * with the update without further checks. - */ - public void update(PImage source, boolean tint, int tintColor) { - //int bufferType = BufferedImage.TYPE_INT_ARGB; - int targetType = ARGB; - boolean opaque = (tintColor & 0xFF000000) == 0xFF000000; - if (source.format == RGB) { - if (!tint || opaque) { - //bufferType = BufferedImage.TYPE_INT_RGB; - targetType = RGB; - } - } -// boolean wrongType = (image != null) && (image.getType() != bufferType); -// if ((image == null) || wrongType) { -// image = new BufferedImage(source.width, source.height, bufferType); -// } - // Must always use an ARGB image, otherwise will write zeros - // in the alpha channel when drawn to the screen. - // https://github.com/processing/processing/issues/2030 -// if (image == null) { -// image = new BufferedImage(source.width, source.height, -// BufferedImage.TYPE_INT_ARGB); -// } - if (image == null) { - image = new WritableImage(source.pixelWidth, source.pixelHeight); - } - - //WritableRaster wr = image.getRaster(); - PixelWriter pw = image.getPixelWriter(); - if (tint) { - if (tintedTemp == null || tintedTemp.length != source.pixelWidth) { - tintedTemp = new int[source.pixelWidth]; - } - int a2 = (tintColor >> 24) & 0xff; -// System.out.println("tint color is " + a2); -// System.out.println("source.pixels[0] alpha is " + (source.pixels[0] >>> 24)); - int r2 = (tintColor >> 16) & 0xff; - int g2 = (tintColor >> 8) & 0xff; - int b2 = (tintColor) & 0xff; - - //if (bufferType == BufferedImage.TYPE_INT_RGB) { - if (targetType == RGB) { - // The target image is opaque, meaning that the source image has no - // alpha (is not ARGB), and the tint has no alpha. - int index = 0; - for (int y = 0; y < source.pixelHeight; y++) { - for (int x = 0; x < source.pixelWidth; x++) { - int argb1 = source.pixels[index++]; - int r1 = (argb1 >> 16) & 0xff; - int g1 = (argb1 >> 8) & 0xff; - int b1 = (argb1) & 0xff; - - // Prior to 2.1, the alpha channel was commented out here, - // but can't remember why (just thought unnecessary b/c of RGB?) - // https://github.com/processing/processing/issues/2030 - tintedTemp[x] = 0xFF000000 | - (((r2 * r1) & 0xff00) << 8) | - ((g2 * g1) & 0xff00) | - (((b2 * b1) & 0xff00) >> 8); - } - //wr.setDataElements(0, y, source.width, 1, tintedTemp); - pw.setPixels(0, y, source.pixelWidth, 1, argbFormat, tintedTemp, 0, source.pixelWidth); - } - // could this be any slower? -// float[] scales = { tintR, tintG, tintB }; -// float[] offsets = new float[3]; -// RescaleOp op = new RescaleOp(scales, offsets, null); -// op.filter(image, image); - - } else { // targetType == ARGB - if (source.format == RGB && - (tintColor & 0xffffff) == 0xffffff) { - int hi = tintColor & 0xff000000; - int index = 0; - for (int y = 0; y < source.pixelHeight; y++) { - for (int x = 0; x < source.pixelWidth; x++) { - tintedTemp[x] = hi | (source.pixels[index++] & 0xFFFFFF); - } - //wr.setDataElements(0, y, source.width, 1, tintedTemp); - pw.setPixels(0, y, source.pixelWidth, 1, argbFormat, tintedTemp, 0, source.pixelWidth); - } - } else { - int index = 0; - for (int y = 0; y < source.pixelHeight; y++) { - if (source.format == RGB) { - int alpha = tintColor & 0xFF000000; - for (int x = 0; x < source.pixelWidth; x++) { - int argb1 = source.pixels[index++]; - int r1 = (argb1 >> 16) & 0xff; - int g1 = (argb1 >> 8) & 0xff; - int b1 = (argb1) & 0xff; - tintedTemp[x] = alpha | - (((r2 * r1) & 0xff00) << 8) | - ((g2 * g1) & 0xff00) | - (((b2 * b1) & 0xff00) >> 8); - } - } else if (source.format == ARGB) { - for (int x = 0; x < source.pixelWidth; x++) { - int argb1 = source.pixels[index++]; - int a1 = (argb1 >> 24) & 0xff; - int r1 = (argb1 >> 16) & 0xff; - int g1 = (argb1 >> 8) & 0xff; - int b1 = (argb1) & 0xff; - tintedTemp[x] = - (((a2 * a1) & 0xff00) << 16) | - (((r2 * r1) & 0xff00) << 8) | - ((g2 * g1) & 0xff00) | - (((b2 * b1) & 0xff00) >> 8); - } - } else if (source.format == ALPHA) { - int lower = tintColor & 0xFFFFFF; - for (int x = 0; x < source.pixelWidth; x++) { - int a1 = source.pixels[index++]; - tintedTemp[x] = - (((a2 * a1) & 0xff00) << 16) | lower; - } - } - //wr.setDataElements(0, y, source.width, 1, tintedTemp); - pw.setPixels(0, y, source.pixelWidth, 1, argbFormat, tintedTemp, 0, source.pixelWidth); - } - } - // Not sure why ARGB images take the scales in this order... -// float[] scales = { tintR, tintG, tintB, tintA }; -// float[] offsets = new float[4]; -// RescaleOp op = new RescaleOp(scales, offsets, null); -// op.filter(image, image); - } - } else { // !tint - if (targetType == RGB && (source.pixels[0] >> 24 == 0)) { - // If it's an RGB image and the high bits aren't set, need to set - // the high bits to opaque because we're drawing ARGB images. - source.filter(OPAQUE); - // Opting to just manipulate the image here, since it shouldn't - // affect anything else (and alpha(get(x, y)) should return 0xff). - // Wel also make no guarantees about the values of the pixels array - // in a PImage and how the high bits will be set. - } - // If no tint, just shove the pixels on in there verbatim - //wr.setDataElements(0, 0, source.width, source.height, source.pixels); - //System.out.println("moving the big one"); - pw.setPixels(0, 0, source.pixelWidth, source.pixelHeight, - argbFormat, source.pixels, 0, source.pixelWidth); - } - this.tinted = tint; - this.tintedColor = tintColor; - -// GraphicsConfiguration gc = parent.getGraphicsConfiguration(); -// compat = gc.createCompatibleImage(image.getWidth(), -// image.getHeight(), -// Transparency.TRANSLUCENT); -// -// Graphics2D g = compat.createGraphics(); -// g.drawImage(image, 0, 0, null); -// g.dispose(); - } - } - - - - ////////////////////////////////////////////////////////////// - - // SHAPE - - - //public void shapeMode(int mode) - - - //public void shape(PShape shape) - - - //public void shape(PShape shape, float x, float y) - - - //public void shape(PShape shape, float x, float y, float c, float d) - - - ////////////////////////////////////////////////////////////// - - // SHAPE I/O - - - @Override - public PShape loadShape(String filename) { - return loadShape(filename, null); - } - - - @Override - public PShape loadShape(String filename, String options) { - String extension = PApplet.getExtension(filename); - if (extension.equals("svg") || extension.equals("svgz")) { - return new PShapeSVG(parent.loadXML(filename)); - } - PGraphics.showWarning("Unsupported format: " + filename); - return null; - } - - - - ////////////////////////////////////////////////////////////// - - // TEXT ATTRIBUTES - - - protected FontCache fontCache = new FontCache(); - - // Is initialized when defaultFontOrDeath() is called - // and mirrors PGraphics.textFont field - protected FontInfo textFontInfo; - - - @Override - protected PFont createFont(String name, float size, - boolean smooth, char[] charset) { - PFont font = super.createFont(name, size, smooth, charset); - if (font.isStream()) { - fontCache.nameToFilename.put(font.getName(), name); - } - return font; - } - - - @Override - protected void defaultFontOrDeath(String method, float size) { - super.defaultFontOrDeath(method, size); - handleTextFont(textFont, size); - } - - - @Override - protected boolean textModeCheck(int mode) { - return mode == MODEL; - } - - - @Override - public float textAscent() { - if (textFont == null) { - defaultFontOrDeath("textAscent"); - } - if (textFontInfo.font == null) { - return super.textAscent(); - } - return textFontInfo.ascent; - } - - - @Override - public float textDescent() { - if (textFont == null) { - defaultFontOrDeath("textDescent"); - } - if (textFontInfo.font == null) { - return super.textDescent(); - } - return textFontInfo.descent; - } - - - static final class FontInfo { - // TODO: this should be based on memory consumption - // this should be enough e.g. for all grays and alpha combos - static final int MAX_CACHED_COLORS_PER_FONT = 1 << 16; - - // used only when there is native font - Font font; - float ascent; - float descent; - - // used only when there is no native font - // maps 32-bit color to the arrays of tinted glyph images - Map tintCache; - } - - - static final class FontCache { - static final int MAX_CACHE_SIZE = 512; - - // keeps track of filenames of fonts loaded from ttf and otf files - Map nameToFilename = new HashMap<>(); - - // keeps track of fonts which should be rendered as pictures - // so we don't go through native font search process every time - final HashSet nonNativeNames = new HashSet<>(); - - // keeps all created fonts for reuse up to MAX_CACHE_SIZE limit - // when the limit is reached, the least recently used font is removed - // TODO: this should be based on memory consumption - final LinkedHashMap cache = - new LinkedHashMap<>(16, 0.75f, true) { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > MAX_CACHE_SIZE; - } - }; - - // key for retrieving fonts from cache; don't use for insertion, - // every font has to have its own new Key instance - final Key retrievingKey = new Key(); - - // text node used for measuring sizes of text - final Text measuringText = new Text(); - - FontInfo get(String name, float size) { - if (nonNativeNames.contains(name)) { - // Don't have native font, using glyph images. - // Size is set to zero, because all sizes of this font - // should share one FontInfo with one tintCache. - size = 0; - } - retrievingKey.name = name; - retrievingKey.size = size; - return cache.get(retrievingKey); - } - - void put(String name, float size, FontInfo fontInfo) { - if (fontInfo.font == null) { - // Don't have native font, using glyph images. - // Size is set to zero, because all sizes of this font - // should share one FontInfo with one tintCache. - nonNativeNames.add(name); - size = 0; - } - Key key = new Key(); - key.name = name; - key.size = size; - cache.put(key, fontInfo); - } - - FontInfo createFontInfo(Font font) { - FontInfo result = new FontInfo(); - result.font = font; - if (font != null) { - // measure ascent and descent - measuringText.setFont(result.font); - measuringText.setText(" "); - float lineHeight = (float) measuringText.getLayoutBounds().getHeight(); - result.ascent = (float) measuringText.getBaselineOffset(); - result.descent = lineHeight - result.ascent; - } - return result; - } - - static final class Key { - String name; - float size; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Key that = (Key) o; - if (Float.compare(that.size, size) != 0) return false; - return name.equals(that.name); - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + (size != +0.0f ? Float.floatToIntBits(size) : 0); - return result; - } - } - } - - - /////////////////////////////////////////////////////////////// - - // TEXT - - // None of the variations of text() are overridden from PGraphics. - - - - ////////////////////////////////////////////////////////////// - - // TEXT IMPL - - - @Override - protected void textFontImpl(PFont which, float size) { - handleTextFont(which, size); - handleTextSize(size); - } - - - @Override - protected void textSizeImpl(float size) { - handleTextFont(textFont, size); - handleTextSize(size); - } - - - /** - * FX specific. When setting font or size, new font has to - * be created. Both textFontImpl and textSizeImpl call this one. - * @param which font to be set, not null - * @param size size to be set, greater than zero - */ - protected void handleTextFont(PFont which, float size) { - textFont = which; - - String fontName = which.getName(); - String fontPsName = which.getPostScriptName(); - - textFontInfo = fontCache.get(fontName, size); - if (textFontInfo == null) { - Font font = null; - - if (which.isStream()) { - // Load from ttf or otf file - String filename = fontCache.nameToFilename.get(fontName); - font = Font.loadFont(parent.createInput(filename), size); - } - - if (font == null) { - // Look up font name - font = new Font(fontName, size); - if (!fontName.equalsIgnoreCase(font.getName())) { - // Look up font postscript name - font = new Font(fontPsName, size); - if (!fontPsName.equalsIgnoreCase(font.getName())) { - font = null; // Done with it - } - } - } - - if (font == null && which.getNative() != null) { - // Ain't got nothing, but AWT has something, so glyph images are not - // going to be used for this font; go with the default font then - font = new Font(size); - } - - textFontInfo = fontCache.createFontInfo(font); - fontCache.put(fontName, size, textFontInfo); - } - - context.setFont(textFontInfo.font); - } - - - @Override - protected void textLineImpl(char[] buffer, int start, int stop, float x, float y) { - if (textFontInfo.font == null) { - super.textLineImpl(buffer, start, stop, x, y); - } else { - context.fillText(new String(buffer, start, stop - start), x, y); - } - } - - - protected PImage getTintedGlyphImage(PFont.Glyph glyph, int tintColor) { - if (textFontInfo.tintCache == null) { - textFontInfo.tintCache = new LinkedHashMap<>(16, 0.75f, true) { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > FontInfo.MAX_CACHED_COLORS_PER_FONT; - } - }; - } - PImage[] tintedGlyphs = textFontInfo.tintCache.get(tintColor); - int index = glyph.index; - if (tintedGlyphs == null || tintedGlyphs.length <= index) { - PImage[] newArray = new PImage[textFont.getGlyphCount()]; - if (tintedGlyphs != null) { - System.arraycopy(tintedGlyphs, 0, newArray, 0, tintedGlyphs.length); - } - tintedGlyphs = newArray; - textFontInfo.tintCache.put(tintColor, tintedGlyphs); - } - PImage tintedGlyph = tintedGlyphs[index]; - if (tintedGlyph == null) { - tintedGlyph = glyph.image.copy(); - tintedGlyphs[index] = tintedGlyph; - } - return tintedGlyph; - } - - - @Override - protected void textCharImpl(char ch, float x, float y) { //, float z) { - PFont.Glyph glyph = textFont.getGlyph(ch); - if (glyph != null) { - if (textMode == MODEL) { - float bitmapSize = textFont.getSize(); - float high = glyph.height / bitmapSize; - float wide = glyph.width / bitmapSize; - float leftExtent = glyph.leftExtent / bitmapSize; - float topExtent = glyph.topExtent / bitmapSize; - - float x1 = x + leftExtent * textSize; - float y1 = y - topExtent * textSize; - float x2 = x1 + wide * textSize; - float y2 = y1 + high * textSize; - - PImage glyphImage = (fillColor == 0xFFFFFFFF) ? - glyph.image : getTintedGlyphImage(glyph, fillColor); - - textCharModelImpl(glyphImage, - x1, y1, x2, y2, - glyph.width, glyph.height); - } - } else if (ch != ' ' && ch != 127) { - showWarning("No glyph found for the " + ch + - " (\\u" + PApplet.hex(ch, 4) + ") character"); - } - } - - - @Override - protected float textWidthImpl(char[] buffer, int start, int stop) { - if (textFont == null) { - defaultFontOrDeath("textWidth"); - } - - if (textFontInfo.font == null) { - return super.textWidthImpl(buffer, start, stop); - } - - fontCache.measuringText.setFont(textFontInfo.font); - fontCache.measuringText.setText(new String(buffer, start, stop - start)); - return (float) fontCache.measuringText.getLayoutBounds().getWidth(); - } - - - - ////////////////////////////////////////////////////////////// - - // MATRIX STACK - - - @Override - public void pushMatrix() { - if (transformCount == transformStack.length) { - throw new RuntimeException("pushMatrix() cannot use push more than " + - transformStack.length + " times"); - } - transformStack[transformCount] = context.getTransform(transformStack[transformCount]); - transformCount++; - } - - - @Override - public void popMatrix() { - if (transformCount == 0) { - throw new RuntimeException("missing a pushMatrix() " + - "to go with that popMatrix()"); - } - transformCount--; - context.setTransform(transformStack[transformCount]); - } - - - - ////////////////////////////////////////////////////////////// - - // MATRIX TRANSFORMS - - - @Override - public void translate(float tx, float ty) { - context.translate(tx, ty); - } - - - //public void translate(float tx, float ty, float tz) - - - @Override - public void rotate(float angle) { - context.rotate(PApplet.degrees(angle)); - } - - - @Override - public void rotateX(float angle) { - showDepthWarning("rotateX"); - } - - - @Override - public void rotateY(float angle) { - showDepthWarning("rotateY"); - } - - - @Override - public void rotateZ(float angle) { - showDepthWarning("rotateZ"); - } - - - @Override - public void rotate(float angle, float vx, float vy, float vz) { - showVariationWarning("rotate"); - } - - - @Override - public void scale(float s) { - context.scale(s, s); - } - - - @Override - public void scale(float sx, float sy) { - context.scale(sx, sy); - } - - - @Override - public void scale(float sx, float sy, float sz) { - showDepthWarningXYZ("scale"); - } - - - @Override - public void shearX(float angle) { - Affine temp = new Affine(); - temp.appendShear(Math.tan(angle), 0); - context.transform(temp); - } - - - @Override - public void shearY(float angle) { - Affine temp = new Affine(); - temp.appendShear(0, Math.tan(angle)); - context.transform(temp); - } - - - - ////////////////////////////////////////////////////////////// - - // MATRIX MORE - - - @Override - public void resetMatrix() { - context.setTransform(new Affine()); - } - - - //public void applyMatrix(PMatrix2D source) - - - @Override - public void applyMatrix(float n00, float n01, float n02, - float n10, float n11, float n12) { - context.transform(n00, n10, n01, n11, n02, n12); - } - - - //public void applyMatrix(PMatrix3D source) - - - @Override - public void applyMatrix(float n00, float n01, float n02, float n03, - float n10, float n11, float n12, float n13, - float n20, float n21, float n22, float n23, - float n30, float n31, float n32, float n33) { - showVariationWarning("applyMatrix"); - } - - - - ////////////////////////////////////////////////////////////// - - // MATRIX GET/SET - - - @Override - public PMatrix getMatrix() { - return getMatrix((PMatrix2D) null); - } - - - @Override - public PMatrix2D getMatrix(PMatrix2D target) { - if (target == null) { - target = new PMatrix2D(); - } - //double[] transform = new double[6]; - // TODO This is not tested; apparently Affine is a full 3x4 - Affine t = context.getTransform(); //.getMatrix(transform); -// target.set((float) transform[0], (float) transform[2], (float) transform[4], -// (float) transform[1], (float) transform[3], (float) transform[5]); - target.set((float) t.getMxx(), (float) t.getMxy(), (float) t.getTx(), - (float) t.getMyx(), (float) t.getMyy(), (float) t.getTy()); - return target; - } - - - @Override - public PMatrix3D getMatrix(PMatrix3D target) { - showVariationWarning("getMatrix"); - return target; - } - - - //public void setMatrix(PMatrix source) - - - @Override - public void setMatrix(PMatrix2D source) { - context.setTransform(source.m00, source.m10, - source.m01, source.m11, - source.m02, source.m12); - } - - - @Override - public void setMatrix(PMatrix3D source) { - showVariationWarning("setMatrix"); - } - - - @Override - public void printMatrix() { - getMatrix((PMatrix2D) null).print(); - } - - - -// ////////////////////////////////////////////////////////////// -// -// // CAMERA and PROJECTION -// -// // Inherit the plaintive warnings from PGraphics -// -// -// //public void beginCamera() -// //public void endCamera() -// //public void camera() -// //public void camera(float eyeX, float eyeY, float eyeZ, -// // float centerX, float centerY, float centerZ, -// // float upX, float upY, float upZ) -// //public void printCamera() -// -// //public void ortho() -// //public void ortho(float left, float right, -// // float bottom, float top, -// // float near, float far) -// //public void perspective() -// //public void perspective(float fov, float aspect, float near, float far) -// //public void frustum(float left, float right, -// // float bottom, float top, -// // float near, float far) -// //public void printProjection() - - - - ////////////////////////////////////////////////////////////// - - // SCREEN and MODEL transforms - - - @Override - public float screenX(float x, float y) { - return (float) context.getTransform().transform(x, y).getX(); - } - - - @Override - public float screenY(float x, float y) { - return (float) context.getTransform().transform(x, y).getY(); - } - - - @Override - public float screenX(float x, float y, float z) { - showDepthWarningXYZ("screenX"); - return 0; - } - - - @Override - public float screenY(float x, float y, float z) { - showDepthWarningXYZ("screenY"); - return 0; - } - - - @Override - public float screenZ(float x, float y, float z) { - showDepthWarningXYZ("screenZ"); - return 0; - } - - - //public float modelX(float x, float y, float z) - - - //public float modelY(float x, float y, float z) - - - //public float modelZ(float x, float y, float z) - - - -// ////////////////////////////////////////////////////////////// -// -// // STYLE -// -// // pushStyle(), popStyle(), style() and getStyle() inherited. - - - - ////////////////////////////////////////////////////////////// - - // STROKE CAP/JOIN/WEIGHT - - - @Override - public void strokeCap(int cap) { - super.strokeCap(cap); - if (strokeCap == ROUND) { - context.setLineCap(StrokeLineCap.ROUND); - } else if (strokeCap == PROJECT) { - context.setLineCap(StrokeLineCap.SQUARE); - } else { - context.setLineCap(StrokeLineCap.BUTT); - } - } - - - @Override - public void strokeJoin(int join) { - super.strokeJoin(join); - if (strokeJoin == MITER) { - context.setLineJoin(StrokeLineJoin.MITER); - } else if (strokeJoin == ROUND) { - context.setLineJoin(StrokeLineJoin.ROUND); - } else { - context.setLineJoin(StrokeLineJoin.BEVEL); - } - } - - - @Override - public void strokeWeight(float weight) { - super.strokeWeight(weight); - context.setLineWidth(weight); - } - - - - ////////////////////////////////////////////////////////////// - - // STROKE - - // noStroke() and stroke() inherited from PGraphics. - - - @Override - protected void strokeFromCalc() { - super.strokeFromCalc(); - context.setStroke(new Color(strokeR, strokeG, strokeB, strokeA)); - } - - - protected boolean drawingThinLines() { - // align strokes to pixel centers when drawing thin lines - return stroke && strokeWeight == 1; - } - - - - ////////////////////////////////////////////////////////////// - - // TINT - - // noTint() and tint() inherited from PGraphics. - - - - ////////////////////////////////////////////////////////////// - - // FILL - - // noFill() and fill() inherited from PGraphics. - - - @Override - protected void fillFromCalc() { - super.fillFromCalc(); - context.setFill(new Color(fillR, fillG, fillB, fillA)); - } - - - -// ////////////////////////////////////////////////////////////// -// -// // MATERIAL PROPERTIES -// -// -// //public void ambient(int rgb) -// //public void ambient(float gray) -// //public void ambient(float x, float y, float z) -// //protected void ambientFromCalc() -// //public void specular(int rgb) -// //public void specular(float gray) -// //public void specular(float x, float y, float z) -// //protected void specularFromCalc() -// //public void shininess(float shine) -// //public void emissive(int rgb) -// //public void emissive(float gray) -// //public void emissive(float x, float y, float z ) -// //protected void emissiveFromCalc() -// -// -// -// ////////////////////////////////////////////////////////////// -// -// // LIGHTS -// -// -// //public void lights() -// //public void noLights() -// //public void ambientLight(float red, float green, float blue) -// //public void ambientLight(float red, float green, float blue, -// // float x, float y, float z) -// //public void directionalLight(float red, float green, float blue, -// // float nx, float ny, float nz) -// //public void pointLight(float red, float green, float blue, -// // float x, float y, float z) -// //public void spotLight(float red, float green, float blue, -// // float x, float y, float z, -// // float nx, float ny, float nz, -// // float angle, float concentration) -// //public void lightFalloff(float constant, float linear, float quadratic) -// //public void lightSpecular(float x, float y, float z) -// //protected void lightPosition(int num, float x, float y, float z) -// //protected void lightDirection(int num, float x, float y, float z) - - - - ////////////////////////////////////////////////////////////// - - // BACKGROUND - - - @Override - public void backgroundImpl() { - - // if pixels are modified, we don't flush them (just mark them flushed) - // because they would be immediately overwritten by the background anyway - modified = false; - loaded = false; - - // Save drawing context (transform, fill, blend mode, etc.) - context.save(); - - // Reset transform to identity - context.setTransform(new Affine()); - - // This only takes into account cases where this is the primary surface. - // Not sure what we do with offscreen anyway. - context.setFill(new Color(backgroundR, backgroundG, backgroundB, backgroundA)); - context.setGlobalBlendMode(BlendMode.SRC_OVER); - context.fillRect(0, 0, width, height); - - // Restore drawing context (transform, fill, blend mode, etc.) - context.restore(); - } - - - -// ////////////////////////////////////////////////////////////// -// -// // COLOR MODE -// -// // All colorMode() variations are inherited from PGraphics. -// -// -// -// ////////////////////////////////////////////////////////////// -// -// // COLOR CALC -// -// // colorCalc() and colorCalcARGB() inherited from PGraphics. -// -// -// -// ////////////////////////////////////////////////////////////// -// -// // COLOR DATATYPE STUFFING -// -// // final color() variations inherited. -// -// -// -// ////////////////////////////////////////////////////////////// -// -// // COLOR DATATYPE EXTRACTION -// -// // final methods alpha, red, green, blue, -// // hue, saturation, and brightness all inherited. -// -// -// -// ////////////////////////////////////////////////////////////// -// -// // COLOR DATATYPE INTERPOLATION -// -// // both lerpColor variants inherited. -// -// -// -// ////////////////////////////////////////////////////////////// -// -// // BEGIN/END RAW -// -// -// @Override -// public void beginRaw(PGraphics recorderRaw) { -// showMethodWarning("beginRaw"); -// } -// -// -// @Override -// public void endRaw() { -// showMethodWarning("endRaw"); -// } -// -// -// -// ////////////////////////////////////////////////////////////// -// -// // WARNINGS and EXCEPTIONS -// -// // showWarning and showException inherited. -// -// -// -// ////////////////////////////////////////////////////////////// -// -// // RENDERER SUPPORT QUERIES -// -// -// //public boolean displayable() // true -// -// -// //public boolean is2D() // true -// -// -// //public boolean is3D() // false - - - - ////////////////////////////////////////////////////////////// - - // PIMAGE METHODS - - - @Override - public void loadPixels() { - if ((pixels == null) || (pixels.length != pixelWidth * pixelHeight)) { - pixels = new int[pixelWidth * pixelHeight]; - loaded = false; - } - - if (!loaded) { - if (snapshotImage == null || - snapshotImage.getWidth() != pixelWidth || - snapshotImage.getHeight() != pixelHeight) { - snapshotImage = new WritableImage(pixelWidth, pixelHeight); - } - - SnapshotParameters sp = null; - if (pixelDensity != 1) { - sp = new SnapshotParameters(); - sp.setTransform(Transform.scale(pixelDensity, pixelDensity)); - } - snapshotImage = ((PSurfaceFX) surface).canvas.snapshot(sp, snapshotImage); - PixelReader pr = snapshotImage.getPixelReader(); - pr.getPixels(0, 0, pixelWidth, pixelHeight, argbFormat, pixels, 0, pixelWidth); - - loaded = true; - modified = false; - } - } - - - - ////////////////////////////////////////////////////////////// - - // GET/SET PIXELS - - - @Override - public int get(int x, int y) { - loadPixels(); - return super.get(x, y); - } - - - @Override - protected void getImpl(int sourceX, int sourceY, - int sourceWidth, int sourceHeight, - PImage target, int targetX, int targetY) { - loadPixels(); - super.getImpl(sourceX, sourceY, sourceWidth, sourceHeight, - target, targetX, targetY); - } - - - @Override - public void set(int x, int y, int argb) { - loadPixels(); - super.set(x, y, argb); - } - - - @Override - protected void setImpl(PImage sourceImage, - int sourceX, int sourceY, - int sourceWidth, int sourceHeight, - int targetX, int targetY) { - sourceImage.loadPixels(); - - int sourceOffset = sourceX + sourceImage.pixelWidth * sourceY; - - PixelWriter pw = context.getPixelWriter(); - pw.setPixels(targetX, targetY, sourceWidth, sourceHeight, - argbFormat, - sourceImage.pixels, - sourceOffset, - sourceImage.pixelWidth); - - // Let's keep them loaded - if (loaded) { - int sourceStride = sourceImage.pixelWidth; - int targetStride = pixelWidth; - int targetOffset = targetX + targetY * targetStride; - for (int i = 0; i < sourceHeight; i++) { - System.arraycopy(sourceImage.pixels, sourceOffset + i * sourceStride, - pixels, targetOffset + i * targetStride, sourceWidth); - } - } - } - - - ////////////////////////////////////////////////////////////// - - // MASK - - - static final String MASK_WARNING = - "mask() cannot be used on the main drawing surface"; - - - @Override - public void mask(PImage alpha) { - showWarning(MASK_WARNING); - } - - - - ////////////////////////////////////////////////////////////// - - // FILTER - - // Because the PImage versions call loadPixels() and - // updatePixels(), no need to override anything here. - - - //public void filter(int kind) - - - //public void filter(int kind, float param) - - - - ////////////////////////////////////////////////////////////// - - // COPY - - -// @Override -// public void copy(int sx, int sy, int sw, int sh, -// int dx, int dy, int dw, int dh) { -// if ((sw != dw) || (sh != dh)) { -// g2.drawImage(image, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null); -// -// } else { -// dx = dx - sx; // java2d's "dx" is the delta, not dest -// dy = dy - sy; -// g2.copyArea(sx, sy, sw, sh, dx, dy); -// } -// } - - -// @Override -// public void copy(PImage src, -// int sx, int sy, int sw, int sh, -// int dx, int dy, int dw, int dh) { -// g2.drawImage((Image) src.getNative(), -// dx, dy, dx + dw, dy + dh, -// sx, sy, sx + sw, sy + sh, null); -// } - - - - ////////////////////////////////////////////////////////////// - - // BLEND - - - //static public int blendColor(int c1, int c2, int mode) - - - //public void blend(int sx, int sy, int sw, int sh, - // int dx, int dy, int dw, int dh, int mode) - - - //public void blend(PImage src, - // int sx, int sy, int sw, int sh, - // int dx, int dy, int dw, int dh, int mode) - - - - ////////////////////////////////////////////////////////////// - - // SAVE - - - //public void save(String filename) - - - - ////////////////////////////////////////////////////////////// - - /** - * Display a warning that the specified method is simply unavailable. - */ - static public void showTodoWarning(String method, int issue) { - showWarning(method + "() is not yet available: " + - "https://github.com/processing/processing/issues/" + issue); - } -} diff --git a/java/libraries/javafx/src/processing/javafx/PSurfaceFX.java b/java/libraries/javafx/src/processing/javafx/PSurfaceFX.java deleted file mode 100644 index 470ae71bc..000000000 --- a/java/libraries/javafx/src/processing/javafx/PSurfaceFX.java +++ /dev/null @@ -1,1090 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2015 The Processing Foundation - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation, version 2.1. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA -*/ - -package processing.javafx; - -import com.sun.glass.ui.Screen; - -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.Rectangle; -import java.io.File; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.SynchronousQueue; - -import javafx.animation.Animation; -import javafx.animation.KeyFrame; -import javafx.animation.Timeline; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.event.ActionEvent; -import javafx.event.EventHandler; -import javafx.event.EventType; -import javafx.scene.Cursor; -import javafx.scene.ImageCursor; -import javafx.scene.Scene; -import javafx.scene.SceneAntialiasing; -import javafx.scene.canvas.Canvas; -import javafx.scene.image.Image; -import javafx.scene.image.PixelFormat; -import javafx.scene.image.WritableImage; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyEvent; -import javafx.scene.input.MouseEvent; -import javafx.scene.input.ScrollEvent; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import javafx.stage.WindowEvent; -import javafx.util.Duration; -import processing.awt.ShimAWT; -import processing.core.*; - - -public class PSurfaceFX implements PSurface { - PApplet sketch; - - PGraphicsFX2D fx; - Stage stage; - Canvas canvas; - - final Animation animation; - float frameRate = 60; - - private SynchronousQueue drawExceptionQueue = new SynchronousQueue<>(); - - public PSurfaceFX(PGraphicsFX2D graphics) { - fx = graphics; - canvas = new ResizableCanvas(); - - // set up main drawing loop - KeyFrame keyFrame = new KeyFrame(Duration.millis(1000), - new EventHandler() { - public void handle(ActionEvent event) { - long startNanoTime = System.nanoTime(); - try { - sketch.handleDraw(); - } catch (Throwable e) { - // Let exception handler thread crash with our exception - drawExceptionQueue.offer(e); - // Stop animating right now so nothing runs afterwards - // and crash frame can be for example traced by println() - animation.stop(); - return; - } - long drawNanos = System.nanoTime() - startNanoTime; - - if (sketch.exitCalled()) { - // using Platform.runLater() didn't work -// Platform.runLater(new Runnable() { -// public void run() { - // instead of System.exit(), safely shut down JavaFX this way - Platform.exit(); -// } -// }); - } - if (sketch.frameCount > 5) { - animation.setRate(-PApplet.min(1e9f / drawNanos, frameRate)); - } - } - }); - animation = new Timeline(keyFrame); - animation.setCycleCount(Animation.INDEFINITE); - - // key frame has duration of 1 second, so the rate of the animation - // should be set to frames per second - - // setting rate to negative so that event fires at the start of - // the key frame and first frame is drawn immediately - animation.setRate(-frameRate); - } - - - public Object getNative() { - return canvas; - } - - - class ResizableCanvas extends Canvas { - - public ResizableCanvas() { - widthProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue value, - Number oldWidth, Number newWidth) { -// sketch.width = newWidth.intValue(); - sketch.setSize(newWidth.intValue(), sketch.height); -// draw(); - fx.setSize(sketch.width, sketch.height); - } - }); - heightProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue value, - Number oldHeight, Number newHeight) { -// sketch.height = newHeight.intValue(); - sketch.setSize(sketch.width, newHeight.intValue()); -// draw(); - fx.setSize(sketch.width, sketch.height); - } - }); - - //addEventHandler(eventType, eventHandler); - - EventHandler mouseHandler = new EventHandler<>() { - public void handle(MouseEvent e) { - fxMouseEvent(e); - } - }; - - setOnMousePressed(mouseHandler); - setOnMouseReleased(mouseHandler); - setOnMouseClicked(mouseHandler); - setOnMouseEntered(mouseHandler); - setOnMouseExited(mouseHandler); - - setOnMouseDragged(mouseHandler); - setOnMouseMoved(mouseHandler); - - setOnScroll(new EventHandler() { - public void handle(ScrollEvent e) { - fxScrollEvent(e); - } - }); - - EventHandler keyHandler = new EventHandler<>() { - public void handle(KeyEvent e) { - fxKeyEvent(e); - } - }; - - setOnKeyPressed(keyHandler); - setOnKeyReleased(keyHandler); - setOnKeyTyped(keyHandler); - - setFocusTraversable(false); // prevent tab from de-focusing - - focusedProperty().addListener(new ChangeListener() { - public void changed(ObservableValue value, - Boolean oldValue, Boolean newValue) { - if (newValue.booleanValue()) { - sketch.focused = true; - sketch.focusGained(); - } else { - sketch.focused = false; - sketch.focusLost(); - } - } - }); - } - - public Stage getStage() { - return stage; - } - - @Override - public boolean isResizable() { - return true; - } - - @Override - public double prefWidth(double height) { - return getWidth(); - } - - @Override - public double prefHeight(double width) { - return getHeight(); - } - } - - - // TODO rewrite before 4.0 release - public PImage loadImage(String path, Object... args) { - return ShimAWT.loadImage(sketch, path, args); - } - - - @Override - public void selectInput(String prompt, String callbackMethod, - File file, Object callbackObject) { - ShimAWT.selectInput(prompt, callbackMethod, file, callbackObject); - } - - - @Override - public void selectOutput(String prompt, String callbackMethod, - File file, Object callbackObject) { - ShimAWT.selectOutput(prompt, callbackMethod, file, callbackObject); - } - - - @Override - public void selectFolder(String prompt, String callbackMethod, - File file, Object callbackObject) { - ShimAWT.selectFolder(prompt, callbackMethod, file, callbackObject); - } - - - public void initOffscreen(PApplet sketch) { - } - - - static public class PApplicationFX extends Application { - static public PSurfaceFX surface; -// static String title; // title set at launch -// static boolean resizable; // set at launch - - public PApplicationFX() { } - - @Override - public void start(final Stage stage) { -// if (title != null) { -// stage.setTitle(title); -// } - - PApplet sketch = surface.sketch; - - // See JEP 263 - float renderScale = Screen.getMainScreen().getRecommendedOutputScaleX(); - if (PApplet.platform == PConstants.MACOS) { - for (Screen s : Screen.getScreens()) { - renderScale = Math.max(renderScale, s.getRecommendedOutputScaleX()); - } - } - if (sketch.pixelDensity == 2 && renderScale < 2) { - sketch.pixelDensity = 1; - sketch.g.pixelDensity = 1; - System.err.println("pixelDensity(2) is not available for this display"); - } - - // Use AWT display code, because FX orders screens in different way - GraphicsDevice displayDevice = null; - - GraphicsEnvironment environment = - GraphicsEnvironment.getLocalGraphicsEnvironment(); - - int displayNum = sketch.sketchDisplay(); - if (displayNum > 0) { // if -1, use the default device - GraphicsDevice[] devices = environment.getScreenDevices(); - if (displayNum <= devices.length) { - displayDevice = devices[displayNum - 1]; - } else { - System.err.format("Display %d does not exist, " + - "using the default display instead.%n", displayNum); - for (int i = 0; i < devices.length; i++) { - System.err.format("Display %d is %s%n", (i+1), devices[i]); - } - } - } - if (displayDevice == null) { - displayDevice = environment.getDefaultScreenDevice(); - } - - boolean fullScreen = sketch.sketchFullScreen(); - boolean spanDisplays = sketch.sketchDisplay() == PConstants.SPAN; - - Rectangle primaryScreenRect = displayDevice.getDefaultConfiguration().getBounds(); - Rectangle screenRect = primaryScreenRect; - if (fullScreen || spanDisplays) { - double minX = screenRect.getMinX(); - double maxX = screenRect.getMaxX(); - double minY = screenRect.getMinY(); - double maxY = screenRect.getMaxY(); - if (spanDisplays) { - for (GraphicsDevice s : environment.getScreenDevices()) { - Rectangle bounds = s.getDefaultConfiguration().getBounds(); - minX = Math.min(minX, bounds.getMinX()); - maxX = Math.max(maxX, bounds.getMaxX()); - minY = Math.min(minY, bounds.getMinY()); - maxY = Math.max(maxY, bounds.getMaxY()); - } - } - screenRect = new Rectangle((int) minX, (int) minY, - (int) (maxX - minX), (int) (maxY - minY)); - } - - // Set the displayWidth/Height variables inside PApplet, so that they're - // usable and can even be returned by the sketchWidth()/Height() methods. - sketch.displayWidth = (int) screenRect.getWidth(); - sketch.displayHeight = (int) screenRect.getHeight(); - - int sketchWidth = sketch.sketchWidth(); - int sketchHeight = sketch.sketchHeight(); - - if (fullScreen || spanDisplays) { - sketchWidth = (int) screenRect.getWidth(); - sketchHeight = (int) screenRect.getHeight(); - - stage.initStyle(StageStyle.UNDECORATED); - stage.setX(screenRect.getMinX()); - stage.setY(screenRect.getMinY()); - stage.setWidth(screenRect.getWidth()); - stage.setHeight(screenRect.getHeight()); - } - - Canvas canvas = surface.canvas; - surface.fx.context = canvas.getGraphicsContext2D(); - StackPane stackPane = new StackPane(); - stackPane.getChildren().add(canvas); - canvas.widthProperty().bind(stackPane.widthProperty()); - canvas.heightProperty().bind(stackPane.heightProperty()); - - int width = sketchWidth; - int height = sketchHeight; - int smooth = sketch.sketchSmooth(); - - // Workaround for https://bugs.openjdk.java.net/browse/JDK-8136495 - // https://github.com/processing/processing/issues/3823 - if ((PApplet.platform == PConstants.MACOS || - PApplet.platform == PConstants.LINUX) && - PApplet.javaVersionName.compareTo("1.8.0_60") >= 0 && - PApplet.javaVersionName.compareTo("1.8.0_72") < 0) { - System.err.println("smooth() disabled for JavaFX with this Java version due to Oracle bug"); - System.err.println("https://github.com/processing/processing/issues/3795"); - smooth = 0; - } - - SceneAntialiasing sceneAntialiasing = (smooth == 0) ? - SceneAntialiasing.DISABLED : SceneAntialiasing.BALANCED; - - stage.setScene(new Scene(stackPane, width, height, false, sceneAntialiasing)); - - // initFrame in different thread is waiting for - // the stage, assign it only when it is all set up - surface.stage = stage; - } - - @Override - public void stop() throws Exception { - surface.sketch.dispose(); - } - } - - - //public Frame initFrame(PApplet sketch, java.awt.Color backgroundColor, - public void initFrame(PApplet sketch) {/*, int backgroundColor, - int deviceIndex, boolean fullScreen, - boolean spanDisplays) {*/ - this.sketch = sketch; - PApplicationFX.surface = this; - //Frame frame = new DummyFrame(); - new Thread(new Runnable() { - public void run() { - Application.launch(PApplicationFX.class); - } - }).start(); - - // wait for stage to be initialized on its own thread before continuing - while (stage == null) { - try { - //System.out.println("waiting for launch"); - Thread.sleep(5); - } catch (InterruptedException e) { } - } - - startExceptionHandlerThread(); - - setProcessingIcon(stage); - } - - - private void startExceptionHandlerThread() { - Thread exceptionHandlerThread = new Thread(() -> { - Throwable drawException; - try { - drawException = drawExceptionQueue.take(); - } catch (InterruptedException e) { - return; - } - // Adapted from PSurfaceJOGL - if (drawException != null) { - if (drawException instanceof ThreadDeath) { -// System.out.println("caught ThreadDeath"); -// throw (ThreadDeath)cause; - } else if (drawException instanceof RuntimeException) { - throw (RuntimeException) drawException; - } else if (drawException instanceof UnsatisfiedLinkError) { - throw new UnsatisfiedLinkError(drawException.getMessage()); - } else { - throw new RuntimeException(drawException); - } - } - }); - exceptionHandlerThread.setDaemon(true); - exceptionHandlerThread.setName("Processing-FX-ExceptionHandler"); - exceptionHandlerThread.start(); - } - - - /** Set the window (and dock, or whatever necessary) title. */ - public void setTitle(String title) { -// PApplicationFX.title = title; // store this in case the stage still null -// if (stage != null) { - stage.setTitle(title); -// } - } - - - /** Show or hide the window. */ - @Override - public void setVisible(final boolean visible) { - Platform.runLater(new Runnable() { - public void run() { - if (visible) { - stage.show(); - canvas.requestFocus(); - } else { - stage.hide(); - } - } - }); - } - - - /** Set true if we want to resize things (default is not resizable) */ - public void setResizable(boolean resizable) { -// PApplicationFX.resizable = resizable; -// if (stage != null) { - stage.setResizable(resizable); -// } - } - - - public void setIcon(PImage icon) { - int w = icon.pixelWidth; - int h = icon.pixelHeight; - WritableImage im = new WritableImage(w, h); - im.getPixelWriter().setPixels(0, 0, w, h, - PixelFormat.getIntArgbInstance(), - icon.pixels, - 0, w); - - Stage stage = (Stage) canvas.getScene().getWindow(); - stage.getIcons().clear(); - stage.getIcons().add(im); - } - - - List iconImages; - - protected void setProcessingIcon(Stage stage) { - // Adapted from PSurfaceAWT - // Note: FX chooses wrong icon size, should be fixed in Java 9, see: - // https://bugs.openjdk.java.net/browse/JDK-8091186 - // Removing smaller sizes helps a bit, but big ones are downsized - try { - if (iconImages == null) { - iconImages = new ArrayList<>(); - final int[] sizes = { 48, 64, 128, 256, 512 }; - - for (int sz : sizes) { - URL url = PApplet.class.getResource("/icon/icon-" + sz + ".png"); - Image image = new Image(url.toString()); - iconImages.add(image); - } - } - List icons = stage.getIcons(); - icons.clear(); - icons.addAll(iconImages); - } catch (Exception e) { } // harmless; keep this to ourselves - } - - - @Override - public void setAlwaysOnTop(boolean always) { - stage.setAlwaysOnTop(always); - } - - - /* - @Override - public void placeWindow(int[] location) { - //setFrameSize(); - - if (location != null) { - // a specific location was received from the Runner - // (applet has been run more than once, user placed window) - stage.setX(location[0]); - stage.setY(location[1]); - - } else { // just center on screen - // Can't use frame.setLocationRelativeTo(null) because it sends the - // frame to the main display, which undermines the --display setting. -// frame.setLocation(screenRect.x + (screenRect.width - sketchWidth) / 2, -// screenRect.y + (screenRect.height - sketchHeight) / 2); - } - if (stage.getY() < 0) { - // Windows actually allows you to place frames where they can't be - // closed. Awesome. http://dev.processing.org/bugs/show_bug.cgi?id=1508 - //frame.setLocation(frameLoc.x, 30); - stage.setY(30); - } - - //setCanvasSize(); - - // TODO add window closing behavior -// frame.addWindowListener(new WindowAdapter() { -// @Override -// public void windowClosing(WindowEvent e) { -// System.exit(0); -// } -// }); - - // TODO handle frame resizing events -// setupFrameResizeListener(); - - if (sketch.getGraphics().displayable()) { - setVisible(true); - } - } - */ - - - @Override - public void placeWindow(int[] location, int[] editorLocation) { - if (sketch.sketchFullScreen()) { - PApplet.hideMenuBar(); - return; - } - - int wide = sketch.width; // stage.getWidth() is NaN here - //int high = sketch.height; // stage.getHeight() - - if (location != null) { - // a specific location was received from the Runner - // (applet has been run more than once, user placed window) - stage.setX(location[0]); - stage.setY(location[1]); - - } else if (editorLocation != null) { - int locationX = editorLocation[0] - 20; - int locationY = editorLocation[1]; - - if (locationX - wide > 10) { - // if it fits to the left of the window - stage.setX(locationX - wide); - stage.setY(locationY); - - } else { // doesn't fit - stage.centerOnScreen(); - } - } else { // just center on screen - stage.centerOnScreen(); - } - } - - - // http://download.java.net/jdk8/jfxdocs/javafx/stage/Stage.html#setFullScreenExitHint-java.lang.String- - // http://download.java.net/jdk8/jfxdocs/javafx/stage/Stage.html#setFullScreenExitKeyCombination-javafx.scene.input.KeyCombination- - public void placePresent(int stopColor) { - // TODO Auto-generated method stub - PApplet.hideMenuBar(); - } - - - @Override - public void setupExternalMessages() { - stage.xProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue value, - Number oldX, Number newX) { - sketch.frameMoved(newX.intValue(), stage.yProperty().intValue()); - } - }); - - stage.yProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue value, - Number oldY, Number newY) { - sketch.frameMoved(stage.xProperty().intValue(), newY.intValue()); - } - }); - - stage.setOnCloseRequest(new EventHandler() { - public void handle(WindowEvent we) { - sketch.exit(); - } - }); - } - - - public void setLocation(int x, int y) { - stage.setX(x); - stage.setY(y); - } - - - public void setSize(int wide, int high) { - // When the surface is set to resizable via surface.setResizable(true), - // a crash may occur if the user sets the window to size zero. - // https://github.com/processing/processing/issues/5052 - if (high <= 0) { - high = 1; - } - if (wide <= 0) { - wide = 1; - } - - //System.out.format("%s.setSize(%d, %d)%n", getClass().getSimpleName(), width, height); - Scene scene = stage.getScene(); - double decorH = stage.getWidth() - scene.getWidth(); - double decorV = stage.getHeight() - scene.getHeight(); - stage.setWidth(wide + decorH); - stage.setHeight(high + decorV); - fx.setSize(wide, high); - } - - -// public Component getComponent() { -// return null; -// } - - - public void setSmooth(int level) { - // TODO Auto-generated method stub - - } - - - public void setFrameRate(float fps) { - // setting rate to negative so that event fires at the start of - // the key frame and first frame is drawn immediately - if (fps > 0) { - frameRate = fps; - animation.setRate(-frameRate); - } - } - - -// @Override -// public void requestFocus() { -// canvas.requestFocus(); -// } - - Cursor lastCursor = Cursor.DEFAULT; - - public void setCursor(int kind) { - Cursor c; - switch (kind) { - case PConstants.ARROW: c = Cursor.DEFAULT; break; - case PConstants.CROSS: c = Cursor.CROSSHAIR; break; - case PConstants.HAND: c = Cursor.HAND; break; - case PConstants.MOVE: c = Cursor.MOVE; break; - case PConstants.TEXT: c = Cursor.TEXT; break; - case PConstants.WAIT: c = Cursor.WAIT; break; - default: c = Cursor.DEFAULT; break; - } - lastCursor = c; - canvas.getScene().setCursor(c); - } - - - public void setCursor(PImage image, int hotspotX, int hotspotY) { - int w = image.pixelWidth; - int h = image.pixelHeight; - WritableImage im = new WritableImage(w, h); - im.getPixelWriter().setPixels(0, 0, w, h, - PixelFormat.getIntArgbInstance(), - image.pixels, - 0, w); - ImageCursor c = new ImageCursor(im, hotspotX, hotspotY); - lastCursor = c; - canvas.getScene().setCursor(c); - } - - - public void showCursor() { - canvas.getScene().setCursor(lastCursor); - } - - - public void hideCursor() { - canvas.getScene().setCursor(Cursor.NONE); - } - - - public boolean openLink(String url) { - return ShimAWT.openLink(url); - } - - - public void startThread() { - animation.play(); - } - - - public void pauseThread() { - animation.pause(); - } - - - public void resumeThread() { - animation.play(); - } - - - public boolean stopThread() { - animation.stop(); - return true; - } - - - public boolean isStopped() { - return animation.getStatus() == Animation.Status.STOPPED; - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - /* - protected void addListeners() { - - canvas.addMouseListener(new MouseListener() { - - public void mousePressed(java.awt.event.MouseEvent e) { - nativeMouseEvent(e); - } - - public void mouseReleased(java.awt.event.MouseEvent e) { - nativeMouseEvent(e); - } - - public void mouseClicked(java.awt.event.MouseEvent e) { - nativeMouseEvent(e); - } - - public void mouseEntered(java.awt.event.MouseEvent e) { - nativeMouseEvent(e); - } - - public void mouseExited(java.awt.event.MouseEvent e) { - nativeMouseEvent(e); - } - }); - - canvas.addMouseMotionListener(new MouseMotionListener() { - - public void mouseDragged(java.awt.event.MouseEvent e) { - nativeMouseEvent(e); - } - - public void mouseMoved(java.awt.event.MouseEvent e) { - nativeMouseEvent(e); - } - }); - - canvas.addMouseWheelListener(new MouseWheelListener() { - - public void mouseWheelMoved(MouseWheelEvent e) { - nativeMouseEvent(e); - } - }); - - canvas.addKeyListener(new KeyListener() { - - public void keyPressed(java.awt.event.KeyEvent e) { - nativeKeyEvent(e); - } - - - public void keyReleased(java.awt.event.KeyEvent e) { - nativeKeyEvent(e); - } - - - public void keyTyped(java.awt.event.KeyEvent e) { - nativeKeyEvent(e); - } - }); - - canvas.addFocusListener(new FocusListener() { - - public void focusGained(FocusEvent e) { - sketch.focused = true; - sketch.focusGained(); - } - - public void focusLost(FocusEvent e) { - sketch.focused = false; - sketch.focusLost(); - } - }); - } - */ - - - static Map, Integer> mouseMap = - new HashMap<>(); - static { - mouseMap.put(MouseEvent.MOUSE_PRESSED, processing.event.MouseEvent.PRESS); - mouseMap.put(MouseEvent.MOUSE_RELEASED, processing.event.MouseEvent.RELEASE); - mouseMap.put(MouseEvent.MOUSE_CLICKED, processing.event.MouseEvent.CLICK); - mouseMap.put(MouseEvent.MOUSE_DRAGGED, processing.event.MouseEvent.DRAG); - mouseMap.put(MouseEvent.MOUSE_MOVED, processing.event.MouseEvent.MOVE); - mouseMap.put(MouseEvent.MOUSE_ENTERED, processing.event.MouseEvent.ENTER); - mouseMap.put(MouseEvent.MOUSE_EXITED, processing.event.MouseEvent.EXIT); - } - - protected void fxMouseEvent(MouseEvent fxEvent) { - // the 'amount' is the number of button clicks for a click event, - // or the number of steps/clicks on the wheel for a mouse wheel event. - int count = fxEvent.getClickCount(); - - int action = mouseMap.get(fxEvent.getEventType()); - - int modifiers = 0; - if (fxEvent.isShiftDown()) { - modifiers |= processing.event.Event.SHIFT; - } - if (fxEvent.isControlDown()) { - modifiers |= processing.event.Event.CTRL; - } - if (fxEvent.isMetaDown()) { - modifiers |= processing.event.Event.META; - } - if (fxEvent.isAltDown()) { - modifiers |= processing.event.Event.ALT; - } - - int button = 0; - switch (fxEvent.getButton()) { - case PRIMARY: - button = PConstants.LEFT; - break; - case SECONDARY: - button = PConstants.RIGHT; - break; - case MIDDLE: - button = PConstants.CENTER; - break; - case NONE: - case BACK: - case FORWARD: - // not currently handled - break; - } - - //long when = nativeEvent.getWhen(); // from AWT - long when = System.currentTimeMillis(); - int x = (int) fxEvent.getX(); // getSceneX()? - int y = (int) fxEvent.getY(); - - sketch.postEvent(new processing.event.MouseEvent(fxEvent, when, - action, modifiers, - x, y, button, count)); - } - - // https://docs.oracle.com/javase/8/javafx/api/javafx/scene/input/ScrollEvent.html - protected void fxScrollEvent(ScrollEvent fxEvent) { - // the number of steps/clicks on the wheel for a mouse wheel event. - int count = (int) -(fxEvent.getDeltaY() / fxEvent.getMultiplierY()); - - int action = processing.event.MouseEvent.WHEEL; - - int modifiers = 0; - if (fxEvent.isShiftDown()) { - modifiers |= processing.event.Event.SHIFT; - } - if (fxEvent.isControlDown()) { - modifiers |= processing.event.Event.CTRL; - } - if (fxEvent.isMetaDown()) { - modifiers |= processing.event.Event.META; - } - if (fxEvent.isAltDown()) { - modifiers |= processing.event.Event.ALT; - } - - // FX does not supply button info - int button = 0; - - long when = System.currentTimeMillis(); - int x = (int) fxEvent.getX(); // getSceneX()? - int y = (int) fxEvent.getY(); - - sketch.postEvent(new processing.event.MouseEvent(fxEvent, when, - action, modifiers, - x, y, button, count)); - } - - - protected void fxKeyEvent(javafx.scene.input.KeyEvent fxEvent) { - int action = 0; - EventType et = fxEvent.getEventType(); - if (et == KeyEvent.KEY_PRESSED) { - action = processing.event.KeyEvent.PRESS; - } else if (et == KeyEvent.KEY_RELEASED) { - action = processing.event.KeyEvent.RELEASE; - } else if (et == KeyEvent.KEY_TYPED) { - action = processing.event.KeyEvent.TYPE; - } - - int modifiers = 0; - if (fxEvent.isShiftDown()) { - modifiers |= processing.event.Event.SHIFT; - } - if (fxEvent.isControlDown()) { - modifiers |= processing.event.Event.CTRL; - } - if (fxEvent.isMetaDown()) { - modifiers |= processing.event.Event.META; - } - if (fxEvent.isAltDown()) { - modifiers |= processing.event.Event.ALT; - } - - long when = System.currentTimeMillis(); - - char keyChar = getKeyChar(fxEvent); - int keyCode = getKeyCode(fxEvent); - sketch.postEvent(new processing.event.KeyEvent(fxEvent, when, - action, modifiers, - keyChar, keyCode)); - } - - - private int getKeyCode(KeyEvent fxEvent) { - if (fxEvent.getEventType() == KeyEvent.KEY_TYPED) { - return 0; - } - - KeyCode kc = fxEvent.getCode(); - switch (kc) { - case ALT_GRAPH: - return PConstants.ALT; - default: - break; - } - return kc.getCode(); - } - - - private char getKeyChar(KeyEvent fxEvent) { - KeyCode kc = fxEvent.getCode(); - - // Overriding chars for some - // KEY_PRESSED and KEY_RELEASED events - switch (kc) { - case UP: - case KP_UP: - case DOWN: - case KP_DOWN: - case LEFT: - case KP_LEFT: - case RIGHT: - case KP_RIGHT: - case ALT: - case ALT_GRAPH: - case CONTROL: - case SHIFT: - case CAPS: - case META: - case WINDOWS: - case CONTEXT_MENU: - case HOME: - case PAGE_UP: - case PAGE_DOWN: - case END: - case PAUSE: - case PRINTSCREEN: - case INSERT: - case NUM_LOCK: - case SCROLL_LOCK: - case F1: - case F2: - case F3: - case F4: - case F5: - case F6: - case F7: - case F8: - case F9: - case F10: - case F11: - case F12: - return PConstants.CODED; - case ENTER: - return '\n'; - case DIVIDE: - return '/'; - case MULTIPLY: - return '*'; - case SUBTRACT: - return '-'; - case ADD: - return '+'; - case NUMPAD0: - return '0'; - case NUMPAD1: - return '1'; - case NUMPAD2: - return '2'; - case NUMPAD3: - return '3'; - case NUMPAD4: - return '4'; - case NUMPAD5: - return '5'; - case NUMPAD6: - return '6'; - case NUMPAD7: - return '7'; - case NUMPAD8: - return '8'; - case NUMPAD9: - return '9'; - case DECIMAL: - // KEY_TYPED does not go through here and will produce - // dot or comma based on the keyboard layout. - // For KEY_PRESSED and KEY_RELEASED, let's just go with - // the dot. Users can detect the key by its keyCode. - return '.'; - case UNDEFINED: - // KEY_TYPED has KeyCode: UNDEFINED - // and falls through here - break; - default: - break; - } - - // Just go with what FX gives us for the rest of - // KEY_PRESSED and KEY_RELEASED and all of KEY_TYPED - String ch; - if (fxEvent.getEventType() == KeyEvent.KEY_TYPED) { - ch = fxEvent.getCharacter(); - } else { - ch = kc.getChar(); - } - - if (ch.length() < 1) return PConstants.CODED; - if (ch.startsWith("\r")) return '\n'; // normalize enter key - return ch.charAt(0); - } -}