propane 2.8.0.pre-java → 2.9.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mvn/extensions.xml +1 -1
- data/CHANGELOG.md +4 -2
- data/README.md +9 -11
- data/Rakefile +2 -2
- data/lib/propane/app.rb +3 -3
- data/lib/propane/version.rb +1 -1
- data/library/control_panel/control_panel.rb +2 -3
- data/library/slider/slider.rb +2 -1
- data/license.txt +508 -0
- data/pom.rb +92 -81
- data/pom.xml +42 -30
- data/propane.gemspec +3 -3
- data/src/main/java/processing/awt/PSurfaceAWT.java +36 -36
- data/src/main/java/processing/core/PApplet.java +45 -46
- data/src/main/java/processing/core/ThinkDifferent.java +96 -0
- data/src/main/java/processing/javafx/PGraphicsFX2D.java +2330 -0
- data/src/main/java/processing/javafx/PSurfaceFX.java +1069 -0
- data/vendors/Rakefile +20 -5
- metadata +12 -9
- data/.travis.yml +0 -10
@@ -0,0 +1,96 @@
|
|
1
|
+
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
2
|
+
|
3
|
+
/*
|
4
|
+
Part of the Processing project - http://processing.org
|
5
|
+
|
6
|
+
Copyright (c) 2012-2014 The Processing Foundation
|
7
|
+
Copyright (c) 2007-2012 Ben Fry and Casey Reas
|
8
|
+
|
9
|
+
This program is free software; you can redistribute it and/or
|
10
|
+
modify it under the terms of the GNU General Public License
|
11
|
+
version 2, as published by the Free Software Foundation.
|
12
|
+
|
13
|
+
This program is distributed in the hope that it will be useful,
|
14
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
GNU General Public License for more details.
|
17
|
+
|
18
|
+
You should have received a copy of the GNU General Public License
|
19
|
+
along with this program; if not, write to the Free Software Foundation,
|
20
|
+
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
21
|
+
*/
|
22
|
+
|
23
|
+
package processing.core;
|
24
|
+
|
25
|
+
import java.awt.Image;
|
26
|
+
|
27
|
+
import com.apple.eawt.AppEvent.QuitEvent;
|
28
|
+
import com.apple.eawt.Application;
|
29
|
+
import com.apple.eawt.QuitHandler;
|
30
|
+
import com.apple.eawt.QuitResponse;
|
31
|
+
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Deal with issues related to thinking differently.
|
35
|
+
*
|
36
|
+
* We have to register a quit handler to safely shut down the sketch,
|
37
|
+
* otherwise OS X will just kill the sketch when a user hits Cmd-Q.
|
38
|
+
* In addition, we have a method to set the dock icon image so we look more
|
39
|
+
* like a native application.
|
40
|
+
*
|
41
|
+
* This is a stripped-down version of what's in processing.app.platform to fix
|
42
|
+
* <a href="https://github.com/processing/processing/issues/3301">3301</a>.
|
43
|
+
*/
|
44
|
+
public class ThinkDifferent {
|
45
|
+
|
46
|
+
// http://developer.apple.com/documentation/Java/Reference/1.4.2/appledoc/api/com/apple/eawt/Application.html
|
47
|
+
private static Application application;
|
48
|
+
|
49
|
+
// True if user has tried to quit once. Prevents us from canceling the quit
|
50
|
+
// call if the sketch is held up for some reason, like an exception that's
|
51
|
+
// managed to put the sketch in a bad state.
|
52
|
+
static boolean attemptedQuit;
|
53
|
+
|
54
|
+
|
55
|
+
static public void init(final PApplet sketch) {
|
56
|
+
if (application == null) {
|
57
|
+
application = Application.getApplication();
|
58
|
+
}
|
59
|
+
|
60
|
+
application.setQuitHandler(new QuitHandler() {
|
61
|
+
public void handleQuitRequestWith(QuitEvent event, QuitResponse response) {
|
62
|
+
sketch.exit();
|
63
|
+
if (PApplet.uncaughtThrowable == null && // no known crash
|
64
|
+
!attemptedQuit) { // haven't tried yet
|
65
|
+
response.cancelQuit(); // tell OS X we'll handle this
|
66
|
+
attemptedQuit = true;
|
67
|
+
} else {
|
68
|
+
response.performQuit(); // just force it this time
|
69
|
+
}
|
70
|
+
}
|
71
|
+
});
|
72
|
+
}
|
73
|
+
|
74
|
+
static public void cleanup() {
|
75
|
+
if (application == null) {
|
76
|
+
application = Application.getApplication();
|
77
|
+
}
|
78
|
+
application.setQuitHandler(null);
|
79
|
+
}
|
80
|
+
|
81
|
+
// Called via reflection from PSurfaceAWT and others
|
82
|
+
static public void setIconImage(Image image) {
|
83
|
+
// When already set, is a sun.awt.image.MultiResolutionCachedImage on OS X
|
84
|
+
// Image current = application.getDockIconImage();
|
85
|
+
// System.out.println("current dock icon image is " + current);
|
86
|
+
// System.out.println("changing to " + image);
|
87
|
+
|
88
|
+
application.setDockIconImage(image);
|
89
|
+
}
|
90
|
+
|
91
|
+
|
92
|
+
// Instead, just use Application.getApplication() inside your app
|
93
|
+
// static public Application getApplication() {
|
94
|
+
// return application;
|
95
|
+
// }
|
96
|
+
}
|
@@ -0,0 +1,2330 @@
|
|
1
|
+
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
2
|
+
|
3
|
+
/*
|
4
|
+
Part of the Processing project - http://processing.org
|
5
|
+
|
6
|
+
Copyright (c) 2015 The Processing Foundation
|
7
|
+
|
8
|
+
This library is free software; you can redistribute it and/or
|
9
|
+
modify it under the terms of the GNU Lesser General Public
|
10
|
+
License as published by the Free Software Foundation, version 2.1.
|
11
|
+
|
12
|
+
This library is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
Lesser General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU Lesser General
|
18
|
+
Public License along with this library; if not, write to the
|
19
|
+
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
20
|
+
Boston, MA 02111-1307 USA
|
21
|
+
*/
|
22
|
+
|
23
|
+
package processing.javafx;
|
24
|
+
|
25
|
+
import com.sun.javafx.geom.Path2D;
|
26
|
+
import com.sun.javafx.geom.PathIterator;
|
27
|
+
import com.sun.javafx.geom.Shape;
|
28
|
+
|
29
|
+
import java.nio.IntBuffer;
|
30
|
+
import java.util.HashMap;
|
31
|
+
import java.util.HashSet;
|
32
|
+
import java.util.LinkedHashMap;
|
33
|
+
import java.util.Map;
|
34
|
+
|
35
|
+
import javafx.scene.SnapshotParameters;
|
36
|
+
import javafx.scene.canvas.GraphicsContext;
|
37
|
+
import javafx.scene.effect.BlendMode;
|
38
|
+
import javafx.scene.image.PixelFormat;
|
39
|
+
import javafx.scene.image.PixelReader;
|
40
|
+
import javafx.scene.image.PixelWriter;
|
41
|
+
import javafx.scene.image.WritableImage;
|
42
|
+
import javafx.scene.image.WritablePixelFormat;
|
43
|
+
import javafx.scene.paint.Color;
|
44
|
+
import javafx.scene.shape.ArcType;
|
45
|
+
import javafx.scene.shape.StrokeLineCap;
|
46
|
+
import javafx.scene.shape.StrokeLineJoin;
|
47
|
+
import javafx.scene.text.Font;
|
48
|
+
import javafx.scene.text.Text;
|
49
|
+
import javafx.scene.transform.Affine;
|
50
|
+
import javafx.scene.transform.Transform;
|
51
|
+
|
52
|
+
import processing.core.*;
|
53
|
+
|
54
|
+
|
55
|
+
public class PGraphicsFX2D extends PGraphics {
|
56
|
+
GraphicsContext context;
|
57
|
+
|
58
|
+
static final WritablePixelFormat<IntBuffer> argbFormat =
|
59
|
+
PixelFormat.getIntArgbInstance();
|
60
|
+
|
61
|
+
WritableImage snapshotImage;
|
62
|
+
|
63
|
+
Path2D workPath = new Path2D();
|
64
|
+
Path2D auxPath = new Path2D();
|
65
|
+
boolean openContour;
|
66
|
+
boolean adjustedForThinLines;
|
67
|
+
/// break the shape at the next vertex (next vertex() call is a moveto())
|
68
|
+
boolean breakShape;
|
69
|
+
|
70
|
+
private float pathCoordsBuffer[] = new float[6];
|
71
|
+
|
72
|
+
/// coordinates for internal curve calculation
|
73
|
+
float[] curveCoordX;
|
74
|
+
float[] curveCoordY;
|
75
|
+
float[] curveDrawX;
|
76
|
+
float[] curveDrawY;
|
77
|
+
|
78
|
+
int transformCount;
|
79
|
+
Affine transformStack[] = new Affine[MATRIX_STACK_DEPTH];
|
80
|
+
|
81
|
+
// Line2D.Float line = new Line2D.Float();
|
82
|
+
// Ellipse2D.Float ellipse = new Ellipse2D.Float();
|
83
|
+
// Rectangle2D.Float rect = new Rectangle2D.Float();
|
84
|
+
// Arc2D.Float arc = new Arc2D.Float();
|
85
|
+
//
|
86
|
+
// protected Color tintColorObject;
|
87
|
+
//
|
88
|
+
// protected Color fillColorObject;
|
89
|
+
// public boolean fillGradient;
|
90
|
+
// public Paint fillGradientObject;
|
91
|
+
//
|
92
|
+
// protected Color strokeColorObject;
|
93
|
+
// public boolean strokeGradient;
|
94
|
+
// public Paint strokeGradientObject;
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
//////////////////////////////////////////////////////////////
|
99
|
+
|
100
|
+
// INTERNAL
|
101
|
+
|
102
|
+
|
103
|
+
public PGraphicsFX2D() { }
|
104
|
+
|
105
|
+
|
106
|
+
//public void setParent(PApplet parent)
|
107
|
+
|
108
|
+
|
109
|
+
//public void setPrimary(boolean primary)
|
110
|
+
|
111
|
+
|
112
|
+
//public void setPath(String path)
|
113
|
+
|
114
|
+
|
115
|
+
//public void setSize(int width, int height)
|
116
|
+
|
117
|
+
|
118
|
+
//public void dispose()
|
119
|
+
|
120
|
+
|
121
|
+
@Override
|
122
|
+
public PSurface createSurface() {
|
123
|
+
return surface = new PSurfaceFX(this);
|
124
|
+
}
|
125
|
+
|
126
|
+
|
127
|
+
/** Returns the javafx.scene.canvas.GraphicsContext used by this renderer. */
|
128
|
+
@Override
|
129
|
+
public Object getNative() {
|
130
|
+
return context;
|
131
|
+
}
|
132
|
+
|
133
|
+
|
134
|
+
//////////////////////////////////////////////////////////////
|
135
|
+
|
136
|
+
// FRAME
|
137
|
+
|
138
|
+
|
139
|
+
// @Override
|
140
|
+
// public boolean canDraw() {
|
141
|
+
// return true;
|
142
|
+
// }
|
143
|
+
|
144
|
+
|
145
|
+
@Override
|
146
|
+
public void beginDraw() {
|
147
|
+
checkSettings();
|
148
|
+
resetMatrix(); // reset model matrix
|
149
|
+
vertexCount = 0;
|
150
|
+
}
|
151
|
+
|
152
|
+
|
153
|
+
@Override
|
154
|
+
public void endDraw() {
|
155
|
+
flush();
|
156
|
+
|
157
|
+
if (!primaryGraphics) {
|
158
|
+
// TODO this is probably overkill for most tasks...
|
159
|
+
loadPixels();
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
//////////////////////////////////////////////////////////////
|
166
|
+
|
167
|
+
// SETTINGS
|
168
|
+
|
169
|
+
|
170
|
+
//protected void checkSettings()
|
171
|
+
|
172
|
+
|
173
|
+
//protected void defaultSettings()
|
174
|
+
|
175
|
+
|
176
|
+
//protected void reapplySettings()
|
177
|
+
|
178
|
+
|
179
|
+
|
180
|
+
//////////////////////////////////////////////////////////////
|
181
|
+
|
182
|
+
// HINT
|
183
|
+
|
184
|
+
|
185
|
+
//public void hint(int which)
|
186
|
+
|
187
|
+
|
188
|
+
|
189
|
+
//////////////////////////////////////////////////////////////
|
190
|
+
|
191
|
+
// SHAPE CREATION
|
192
|
+
|
193
|
+
|
194
|
+
//protected PShape createShapeFamily(int type)
|
195
|
+
|
196
|
+
|
197
|
+
//protected PShape createShapePrimitive(int kind, float... p)
|
198
|
+
|
199
|
+
|
200
|
+
|
201
|
+
//////////////////////////////////////////////////////////////
|
202
|
+
|
203
|
+
// SHAPE
|
204
|
+
|
205
|
+
|
206
|
+
@Override
|
207
|
+
public void beginShape(int kind) {
|
208
|
+
shape = kind;
|
209
|
+
vertexCount = 0;
|
210
|
+
curveVertexCount = 0;
|
211
|
+
|
212
|
+
workPath.reset();
|
213
|
+
auxPath.reset();
|
214
|
+
|
215
|
+
flushPixels();
|
216
|
+
|
217
|
+
if (drawingThinLines()) {
|
218
|
+
adjustedForThinLines = true;
|
219
|
+
translate(0.5f, 0.5f);
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
|
224
|
+
//public boolean edge(boolean e)
|
225
|
+
|
226
|
+
|
227
|
+
//public void normal(float nx, float ny, float nz) {
|
228
|
+
|
229
|
+
|
230
|
+
//public void textureMode(int mode)
|
231
|
+
|
232
|
+
|
233
|
+
@Override
|
234
|
+
public void texture(PImage image) {
|
235
|
+
showMethodWarning("texture");
|
236
|
+
}
|
237
|
+
|
238
|
+
|
239
|
+
@Override
|
240
|
+
public void vertex(float x, float y) {
|
241
|
+
if (vertexCount == vertices.length) {
|
242
|
+
float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
|
243
|
+
System.arraycopy(vertices, 0, temp, 0, vertexCount);
|
244
|
+
vertices = temp;
|
245
|
+
//message(CHATTER, "allocating more vertices " + vertices.length);
|
246
|
+
}
|
247
|
+
// not everyone needs this, but just easier to store rather
|
248
|
+
// than adding another moving part to the code...
|
249
|
+
vertices[vertexCount][X] = x;
|
250
|
+
vertices[vertexCount][Y] = y;
|
251
|
+
vertexCount++;
|
252
|
+
|
253
|
+
switch (shape) {
|
254
|
+
|
255
|
+
case POINTS:
|
256
|
+
point(x, y);
|
257
|
+
break;
|
258
|
+
|
259
|
+
case LINES:
|
260
|
+
if ((vertexCount % 2) == 0) {
|
261
|
+
line(vertices[vertexCount-2][X],
|
262
|
+
vertices[vertexCount-2][Y], x, y);
|
263
|
+
}
|
264
|
+
break;
|
265
|
+
|
266
|
+
case TRIANGLES:
|
267
|
+
if ((vertexCount % 3) == 0) {
|
268
|
+
triangle(vertices[vertexCount - 3][X],
|
269
|
+
vertices[vertexCount - 3][Y],
|
270
|
+
vertices[vertexCount - 2][X],
|
271
|
+
vertices[vertexCount - 2][Y],
|
272
|
+
x, y);
|
273
|
+
}
|
274
|
+
break;
|
275
|
+
|
276
|
+
case TRIANGLE_STRIP:
|
277
|
+
if (vertexCount >= 3) {
|
278
|
+
triangle(vertices[vertexCount - 2][X],
|
279
|
+
vertices[vertexCount - 2][Y],
|
280
|
+
vertices[vertexCount - 1][X],
|
281
|
+
vertices[vertexCount - 1][Y],
|
282
|
+
vertices[vertexCount - 3][X],
|
283
|
+
vertices[vertexCount - 3][Y]);
|
284
|
+
}
|
285
|
+
break;
|
286
|
+
|
287
|
+
case TRIANGLE_FAN:
|
288
|
+
if (vertexCount >= 3) {
|
289
|
+
// This is an unfortunate implementation because the stroke for an
|
290
|
+
// adjacent triangle will be repeated. However, if the stroke is not
|
291
|
+
// redrawn, it will replace the adjacent line (when it lines up
|
292
|
+
// perfectly) or show a faint line (when off by a small amount).
|
293
|
+
// The alternative would be to wait, then draw the shape as a
|
294
|
+
// polygon fill, followed by a series of vertices. But that's a
|
295
|
+
// poor method when used with PDF, DXF, or other recording objects,
|
296
|
+
// since discrete triangles would likely be preferred.
|
297
|
+
triangle(vertices[0][X],
|
298
|
+
vertices[0][Y],
|
299
|
+
vertices[vertexCount - 2][X],
|
300
|
+
vertices[vertexCount - 2][Y],
|
301
|
+
x, y);
|
302
|
+
}
|
303
|
+
break;
|
304
|
+
|
305
|
+
case QUAD:
|
306
|
+
case QUADS:
|
307
|
+
if ((vertexCount % 4) == 0) {
|
308
|
+
quad(vertices[vertexCount - 4][X],
|
309
|
+
vertices[vertexCount - 4][Y],
|
310
|
+
vertices[vertexCount - 3][X],
|
311
|
+
vertices[vertexCount - 3][Y],
|
312
|
+
vertices[vertexCount - 2][X],
|
313
|
+
vertices[vertexCount - 2][Y],
|
314
|
+
x, y);
|
315
|
+
}
|
316
|
+
break;
|
317
|
+
|
318
|
+
case QUAD_STRIP:
|
319
|
+
// 0---2---4
|
320
|
+
// | | |
|
321
|
+
// 1---3---5
|
322
|
+
if ((vertexCount >= 4) && ((vertexCount % 2) == 0)) {
|
323
|
+
quad(vertices[vertexCount - 4][X],
|
324
|
+
vertices[vertexCount - 4][Y],
|
325
|
+
vertices[vertexCount - 2][X],
|
326
|
+
vertices[vertexCount - 2][Y],
|
327
|
+
x, y,
|
328
|
+
vertices[vertexCount - 3][X],
|
329
|
+
vertices[vertexCount - 3][Y]);
|
330
|
+
}
|
331
|
+
break;
|
332
|
+
|
333
|
+
case POLYGON:
|
334
|
+
if (workPath.getNumCommands() == 0 || breakShape) {
|
335
|
+
workPath.moveTo(x, y);
|
336
|
+
breakShape = false;
|
337
|
+
} else {
|
338
|
+
workPath.lineTo(x, y);
|
339
|
+
}
|
340
|
+
break;
|
341
|
+
}
|
342
|
+
}
|
343
|
+
|
344
|
+
|
345
|
+
@Override
|
346
|
+
public void vertex(float x, float y, float z) {
|
347
|
+
showDepthWarningXYZ("vertex");
|
348
|
+
}
|
349
|
+
|
350
|
+
|
351
|
+
@Override
|
352
|
+
public void vertex(float[] v) {
|
353
|
+
vertex(v[X], v[Y]);
|
354
|
+
}
|
355
|
+
|
356
|
+
|
357
|
+
@Override
|
358
|
+
public void vertex(float x, float y, float u, float v) {
|
359
|
+
showVariationWarning("vertex(x, y, u, v)");
|
360
|
+
}
|
361
|
+
|
362
|
+
|
363
|
+
@Override
|
364
|
+
public void vertex(float x, float y, float z, float u, float v) {
|
365
|
+
showDepthWarningXYZ("vertex");
|
366
|
+
}
|
367
|
+
|
368
|
+
|
369
|
+
@Override
|
370
|
+
public void beginContour() {
|
371
|
+
if (openContour) {
|
372
|
+
PGraphics.showWarning("Already called beginContour()");
|
373
|
+
return;
|
374
|
+
}
|
375
|
+
|
376
|
+
// draw contours to auxiliary path so main path can be closed later
|
377
|
+
Path2D contourPath = auxPath;
|
378
|
+
auxPath = workPath;
|
379
|
+
workPath = contourPath;
|
380
|
+
|
381
|
+
if (contourPath.getNumCommands() > 0) { // first contour does not break
|
382
|
+
breakShape = true;
|
383
|
+
}
|
384
|
+
|
385
|
+
openContour = true;
|
386
|
+
}
|
387
|
+
|
388
|
+
|
389
|
+
@Override
|
390
|
+
public void endContour() {
|
391
|
+
if (!openContour) {
|
392
|
+
PGraphics.showWarning("Need to call beginContour() first");
|
393
|
+
return;
|
394
|
+
}
|
395
|
+
|
396
|
+
if (workPath.getNumCommands() > 0) workPath.closePath();
|
397
|
+
|
398
|
+
Path2D temp = workPath;
|
399
|
+
workPath = auxPath;
|
400
|
+
auxPath = temp;
|
401
|
+
|
402
|
+
openContour = false;
|
403
|
+
}
|
404
|
+
|
405
|
+
|
406
|
+
@Override
|
407
|
+
public void endShape(int mode) {
|
408
|
+
if (openContour) { // correct automagically, notify user
|
409
|
+
endContour();
|
410
|
+
PGraphics.showWarning("Missing endContour() before endShape()");
|
411
|
+
}
|
412
|
+
if (workPath.getNumCommands() > 0) {
|
413
|
+
if (shape == POLYGON) {
|
414
|
+
if (mode == CLOSE) {
|
415
|
+
workPath.closePath();
|
416
|
+
}
|
417
|
+
if (auxPath.getNumCommands() > 0) {
|
418
|
+
workPath.append(auxPath, false);
|
419
|
+
}
|
420
|
+
drawShape(workPath);
|
421
|
+
}
|
422
|
+
}
|
423
|
+
shape = 0;
|
424
|
+
if (adjustedForThinLines) {
|
425
|
+
adjustedForThinLines = false;
|
426
|
+
translate(-0.5f, -0.5f);
|
427
|
+
}
|
428
|
+
loaded = false;
|
429
|
+
}
|
430
|
+
|
431
|
+
|
432
|
+
private void drawShape(Shape s) {
|
433
|
+
context.beginPath();
|
434
|
+
PathIterator pi = s.getPathIterator(null);
|
435
|
+
while (!pi.isDone()) {
|
436
|
+
int pitype = pi.currentSegment(pathCoordsBuffer);
|
437
|
+
switch (pitype) {
|
438
|
+
case PathIterator.SEG_MOVETO:
|
439
|
+
context.moveTo(pathCoordsBuffer[0], pathCoordsBuffer[1]);
|
440
|
+
break;
|
441
|
+
case PathIterator.SEG_LINETO:
|
442
|
+
context.lineTo(pathCoordsBuffer[0], pathCoordsBuffer[1]);
|
443
|
+
break;
|
444
|
+
case PathIterator.SEG_QUADTO:
|
445
|
+
context.quadraticCurveTo(pathCoordsBuffer[0], pathCoordsBuffer[1],
|
446
|
+
pathCoordsBuffer[2], pathCoordsBuffer[3]);
|
447
|
+
break;
|
448
|
+
case PathIterator.SEG_CUBICTO:
|
449
|
+
context.bezierCurveTo(pathCoordsBuffer[0], pathCoordsBuffer[1],
|
450
|
+
pathCoordsBuffer[2], pathCoordsBuffer[3],
|
451
|
+
pathCoordsBuffer[4], pathCoordsBuffer[5]);
|
452
|
+
break;
|
453
|
+
case PathIterator.SEG_CLOSE:
|
454
|
+
context.closePath();
|
455
|
+
break;
|
456
|
+
default:
|
457
|
+
showWarning("Unknown segment type " + pitype);
|
458
|
+
}
|
459
|
+
pi.next();
|
460
|
+
}
|
461
|
+
if (fill) context.fill();
|
462
|
+
if (stroke) context.stroke();
|
463
|
+
}
|
464
|
+
|
465
|
+
|
466
|
+
|
467
|
+
//////////////////////////////////////////////////////////////
|
468
|
+
|
469
|
+
// CLIPPING
|
470
|
+
|
471
|
+
|
472
|
+
@Override
|
473
|
+
protected void clipImpl(float x1, float y1, float x2, float y2) {
|
474
|
+
//g2.setClip(new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1));
|
475
|
+
showTodoWarning("clip()", 3274);
|
476
|
+
}
|
477
|
+
|
478
|
+
|
479
|
+
@Override
|
480
|
+
public void noClip() {
|
481
|
+
//g2.setClip(null);
|
482
|
+
showTodoWarning("noClip()", 3274);
|
483
|
+
}
|
484
|
+
|
485
|
+
|
486
|
+
|
487
|
+
//////////////////////////////////////////////////////////////
|
488
|
+
|
489
|
+
// BLEND
|
490
|
+
|
491
|
+
|
492
|
+
@Override
|
493
|
+
protected void blendModeImpl() {
|
494
|
+
BlendMode mode = BlendMode.SRC_OVER;
|
495
|
+
switch (blendMode) {
|
496
|
+
case REPLACE: showWarning("blendMode(REPLACE) is not supported"); break;
|
497
|
+
case BLEND: break; // this is SRC_OVER, the default
|
498
|
+
case ADD: mode = BlendMode.ADD; break; // everyone's favorite
|
499
|
+
case SUBTRACT: showWarning("blendMode(SUBTRACT) is not supported"); break;
|
500
|
+
case LIGHTEST: mode = BlendMode.LIGHTEN; break;
|
501
|
+
case DARKEST: mode = BlendMode.DARKEN; break;
|
502
|
+
case DIFFERENCE: mode = BlendMode.DIFFERENCE; break;
|
503
|
+
case EXCLUSION: mode = BlendMode.EXCLUSION; break;
|
504
|
+
case MULTIPLY: mode = BlendMode.MULTIPLY; break;
|
505
|
+
case SCREEN: mode = BlendMode.SCREEN; break;
|
506
|
+
case OVERLAY: mode = BlendMode.OVERLAY; break;
|
507
|
+
case HARD_LIGHT: mode = BlendMode.HARD_LIGHT; break;
|
508
|
+
case SOFT_LIGHT: mode = BlendMode.SOFT_LIGHT; break;
|
509
|
+
case DODGE: mode = BlendMode.COLOR_DODGE; break;
|
510
|
+
case BURN: mode = BlendMode.COLOR_BURN; break;
|
511
|
+
}
|
512
|
+
context.setGlobalBlendMode(mode);
|
513
|
+
}
|
514
|
+
|
515
|
+
|
516
|
+
|
517
|
+
//////////////////////////////////////////////////////////////
|
518
|
+
|
519
|
+
// BEZIER VERTICES
|
520
|
+
|
521
|
+
|
522
|
+
@Override
|
523
|
+
protected void bezierVertexCheck() {
|
524
|
+
if (shape == 0 || shape != POLYGON) {
|
525
|
+
throw new RuntimeException("beginShape() or beginShape(POLYGON) " +
|
526
|
+
"must be used before bezierVertex() or quadraticVertex()");
|
527
|
+
}
|
528
|
+
if (workPath.getNumCommands() == 0) {
|
529
|
+
throw new RuntimeException("vertex() must be used at least once " +
|
530
|
+
"before bezierVertex() or quadraticVertex()");
|
531
|
+
}
|
532
|
+
}
|
533
|
+
|
534
|
+
@Override
|
535
|
+
public void bezierVertex(float x1, float y1,
|
536
|
+
float x2, float y2,
|
537
|
+
float x3, float y3) {
|
538
|
+
bezierVertexCheck();
|
539
|
+
workPath.curveTo(x1, y1, x2, y2, x3, y3);
|
540
|
+
}
|
541
|
+
|
542
|
+
|
543
|
+
@Override
|
544
|
+
public void bezierVertex(float x2, float y2, float z2,
|
545
|
+
float x3, float y3, float z3,
|
546
|
+
float x4, float y4, float z4) {
|
547
|
+
showDepthWarningXYZ("bezierVertex");
|
548
|
+
}
|
549
|
+
|
550
|
+
|
551
|
+
|
552
|
+
//////////////////////////////////////////////////////////////
|
553
|
+
|
554
|
+
// QUADRATIC BEZIER VERTICES
|
555
|
+
|
556
|
+
|
557
|
+
@Override
|
558
|
+
public void quadraticVertex(float ctrlX, float ctrlY,
|
559
|
+
float endX, float endY) {
|
560
|
+
bezierVertexCheck();
|
561
|
+
workPath.quadTo(ctrlX, ctrlY, endX, endY);
|
562
|
+
}
|
563
|
+
|
564
|
+
|
565
|
+
@Override
|
566
|
+
public void quadraticVertex(float x2, float y2, float z2,
|
567
|
+
float x4, float y4, float z4) {
|
568
|
+
showDepthWarningXYZ("quadVertex");
|
569
|
+
}
|
570
|
+
|
571
|
+
|
572
|
+
|
573
|
+
//////////////////////////////////////////////////////////////
|
574
|
+
|
575
|
+
// CURVE VERTICES
|
576
|
+
|
577
|
+
|
578
|
+
@Override
|
579
|
+
protected void curveVertexSegment(float x1, float y1,
|
580
|
+
float x2, float y2,
|
581
|
+
float x3, float y3,
|
582
|
+
float x4, float y4) {
|
583
|
+
if (curveCoordX == null) {
|
584
|
+
curveCoordX = new float[4];
|
585
|
+
curveCoordY = new float[4];
|
586
|
+
curveDrawX = new float[4];
|
587
|
+
curveDrawY = new float[4];
|
588
|
+
}
|
589
|
+
|
590
|
+
curveCoordX[0] = x1;
|
591
|
+
curveCoordY[0] = y1;
|
592
|
+
|
593
|
+
curveCoordX[1] = x2;
|
594
|
+
curveCoordY[1] = y2;
|
595
|
+
|
596
|
+
curveCoordX[2] = x3;
|
597
|
+
curveCoordY[2] = y3;
|
598
|
+
|
599
|
+
curveCoordX[3] = x4;
|
600
|
+
curveCoordY[3] = y4;
|
601
|
+
|
602
|
+
curveToBezierMatrix.mult(curveCoordX, curveDrawX);
|
603
|
+
curveToBezierMatrix.mult(curveCoordY, curveDrawY);
|
604
|
+
|
605
|
+
// since the paths are continuous,
|
606
|
+
// only the first point needs the actual moveto
|
607
|
+
if (workPath.getNumCommands() == 0) {
|
608
|
+
workPath.moveTo(curveDrawX[0], curveDrawY[0]);
|
609
|
+
breakShape = false;
|
610
|
+
}
|
611
|
+
|
612
|
+
workPath.curveTo(curveDrawX[1], curveDrawY[1],
|
613
|
+
curveDrawX[2], curveDrawY[2],
|
614
|
+
curveDrawX[3], curveDrawY[3]);
|
615
|
+
}
|
616
|
+
|
617
|
+
|
618
|
+
@Override
|
619
|
+
public void curveVertex(float x, float y, float z) {
|
620
|
+
showDepthWarningXYZ("curveVertex");
|
621
|
+
}
|
622
|
+
|
623
|
+
|
624
|
+
|
625
|
+
//////////////////////////////////////////////////////////////
|
626
|
+
|
627
|
+
// RENDERER
|
628
|
+
|
629
|
+
@Override
|
630
|
+
public void flush() {
|
631
|
+
flushPixels();
|
632
|
+
}
|
633
|
+
|
634
|
+
|
635
|
+
protected void flushPixels() {
|
636
|
+
boolean hasPixels = modified && pixels != null;
|
637
|
+
if (hasPixels) {
|
638
|
+
// If the user has been manipulating individual pixels,
|
639
|
+
// the changes need to be copied to the screen before
|
640
|
+
// drawing any new geometry.
|
641
|
+
int mx1 = getModifiedX1();
|
642
|
+
int mx2 = getModifiedX2();
|
643
|
+
int my1 = getModifiedY1();
|
644
|
+
int my2 = getModifiedY2();
|
645
|
+
int mw = mx2 - mx1;
|
646
|
+
int mh = my2 - my1;
|
647
|
+
|
648
|
+
if (pixelDensity == 1) {
|
649
|
+
PixelWriter pw = context.getPixelWriter();
|
650
|
+
pw.setPixels(mx1, my1, mw, mh, argbFormat, pixels,
|
651
|
+
mx1 + my1 * pixelWidth, pixelWidth);
|
652
|
+
} else {
|
653
|
+
// The only way to push all the pixels is to draw a scaled-down image
|
654
|
+
if (snapshotImage == null ||
|
655
|
+
snapshotImage.getWidth() != pixelWidth ||
|
656
|
+
snapshotImage.getHeight() != pixelHeight) {
|
657
|
+
snapshotImage = new WritableImage(pixelWidth, pixelHeight);
|
658
|
+
}
|
659
|
+
|
660
|
+
PixelWriter pw = snapshotImage.getPixelWriter();
|
661
|
+
pw.setPixels(mx1, my1, mw, mh, argbFormat, pixels,
|
662
|
+
mx1 + my1 * pixelWidth, pixelWidth);
|
663
|
+
context.save();
|
664
|
+
resetMatrix();
|
665
|
+
context.scale(1d / pixelDensity, 1d / pixelDensity);
|
666
|
+
context.drawImage(snapshotImage, mx1, my1, mw, mh, mx1, my1, mw, mh);
|
667
|
+
context.restore();
|
668
|
+
}
|
669
|
+
}
|
670
|
+
|
671
|
+
modified = false;
|
672
|
+
}
|
673
|
+
|
674
|
+
|
675
|
+
protected void beforeContextDraw() {
|
676
|
+
flushPixels();
|
677
|
+
loaded = false;
|
678
|
+
}
|
679
|
+
|
680
|
+
|
681
|
+
//////////////////////////////////////////////////////////////
|
682
|
+
|
683
|
+
// POINT, LINE, TRIANGLE, QUAD
|
684
|
+
|
685
|
+
|
686
|
+
@Override
|
687
|
+
public void point(float x, float y) {
|
688
|
+
if (stroke) {
|
689
|
+
// if (strokeWeight > 1) {
|
690
|
+
line(x, y, x + EPSILON, y + EPSILON);
|
691
|
+
// } else {
|
692
|
+
// set((int) screenX(x, y), (int) screenY(x, y), strokeColor);
|
693
|
+
// }
|
694
|
+
}
|
695
|
+
}
|
696
|
+
|
697
|
+
|
698
|
+
@Override
|
699
|
+
public void line(float x1, float y1, float x2, float y2) {
|
700
|
+
beforeContextDraw();
|
701
|
+
if (drawingThinLines()) {
|
702
|
+
x1 += 0.5f;
|
703
|
+
x2 += 0.5f;
|
704
|
+
y1 += 0.5f;
|
705
|
+
y2 += 0.5f;
|
706
|
+
}
|
707
|
+
context.strokeLine(x1, y1, x2, y2);
|
708
|
+
}
|
709
|
+
|
710
|
+
|
711
|
+
@Override
|
712
|
+
public void triangle(float x1, float y1, float x2, float y2,
|
713
|
+
float x3, float y3) {
|
714
|
+
beforeContextDraw();
|
715
|
+
if (drawingThinLines()) {
|
716
|
+
x1 += 0.5f;
|
717
|
+
x2 += 0.5f;
|
718
|
+
x3 += 0.5f;
|
719
|
+
y1 += 0.5f;
|
720
|
+
y2 += 0.5f;
|
721
|
+
y3 += 0.5f;
|
722
|
+
}
|
723
|
+
context.beginPath();
|
724
|
+
context.moveTo(x1, y1);
|
725
|
+
context.lineTo(x2, y2);
|
726
|
+
context.lineTo(x3, y3);
|
727
|
+
context.closePath();
|
728
|
+
if (fill) context.fill();
|
729
|
+
if (stroke) context.stroke();
|
730
|
+
}
|
731
|
+
|
732
|
+
|
733
|
+
@Override
|
734
|
+
public void quad(float x1, float y1, float x2, float y2,
|
735
|
+
float x3, float y3, float x4, float y4) {
|
736
|
+
beforeContextDraw();
|
737
|
+
if (drawingThinLines()) {
|
738
|
+
x1 += 0.5f;
|
739
|
+
x2 += 0.5f;
|
740
|
+
x3 += 0.5f;
|
741
|
+
x4 += 0.5f;
|
742
|
+
y1 += 0.5f;
|
743
|
+
y2 += 0.5f;
|
744
|
+
y3 += 0.5f;
|
745
|
+
y4 += 0.5f;
|
746
|
+
}
|
747
|
+
context.beginPath();
|
748
|
+
context.moveTo(x1, y1);
|
749
|
+
context.lineTo(x2, y2);
|
750
|
+
context.lineTo(x3, y3);
|
751
|
+
context.lineTo(x4, y4);
|
752
|
+
context.closePath();
|
753
|
+
if (fill) context.fill();
|
754
|
+
if (stroke) context.stroke();
|
755
|
+
}
|
756
|
+
|
757
|
+
|
758
|
+
|
759
|
+
//////////////////////////////////////////////////////////////
|
760
|
+
|
761
|
+
// RECT
|
762
|
+
|
763
|
+
|
764
|
+
//public void rectMode(int mode)
|
765
|
+
|
766
|
+
|
767
|
+
//public void rect(float a, float b, float c, float d)
|
768
|
+
|
769
|
+
|
770
|
+
@Override
|
771
|
+
protected void rectImpl(float x1, float y1, float x2, float y2) {
|
772
|
+
beforeContextDraw();
|
773
|
+
if (drawingThinLines()) {
|
774
|
+
x1 += 0.5f;
|
775
|
+
x2 += 0.5f;
|
776
|
+
y1 += 0.5f;
|
777
|
+
y2 += 0.5f;
|
778
|
+
}
|
779
|
+
if (fill) context.fillRect(x1, y1, x2 - x1, y2 - y1);
|
780
|
+
if (stroke) context.strokeRect(x1, y1, x2 - x1, y2 - y1);
|
781
|
+
}
|
782
|
+
|
783
|
+
|
784
|
+
|
785
|
+
//////////////////////////////////////////////////////////////
|
786
|
+
|
787
|
+
// ELLIPSE
|
788
|
+
|
789
|
+
|
790
|
+
//public void ellipseMode(int mode)
|
791
|
+
|
792
|
+
|
793
|
+
//public void ellipse(float a, float b, float c, float d)
|
794
|
+
|
795
|
+
|
796
|
+
@Override
|
797
|
+
protected void ellipseImpl(float x, float y, float w, float h) {
|
798
|
+
beforeContextDraw();
|
799
|
+
if (drawingThinLines()) {
|
800
|
+
x += 0.5f;
|
801
|
+
y += 0.5f;
|
802
|
+
}
|
803
|
+
if (fill) context.fillOval(x, y, w, h);
|
804
|
+
if (stroke) context.strokeOval(x, y, w, h);
|
805
|
+
}
|
806
|
+
|
807
|
+
|
808
|
+
|
809
|
+
//////////////////////////////////////////////////////////////
|
810
|
+
|
811
|
+
// ARC
|
812
|
+
|
813
|
+
|
814
|
+
//public void arc(float a, float b, float c, float d,
|
815
|
+
// float start, float stop)
|
816
|
+
|
817
|
+
|
818
|
+
@Override
|
819
|
+
protected void arcImpl(float x, float y, float w, float h,
|
820
|
+
float start, float stop, int mode) {
|
821
|
+
beforeContextDraw();
|
822
|
+
|
823
|
+
if (drawingThinLines()) {
|
824
|
+
x += 0.5f;
|
825
|
+
y += 0.5f;
|
826
|
+
}
|
827
|
+
|
828
|
+
// 0 to 90 in java would be 0 to -90 for p5 renderer
|
829
|
+
// but that won't work, so -90 to 0?
|
830
|
+
start = -start;
|
831
|
+
stop = -stop;
|
832
|
+
|
833
|
+
float sweep = stop - start;
|
834
|
+
|
835
|
+
// The defaults, before 2.0b7, were to stroke as Arc2D.OPEN, and then fill
|
836
|
+
// using Arc2D.PIE. That's a little wonky, but it's here for compatability.
|
837
|
+
ArcType fillMode = ArcType.ROUND; // Arc2D.PIE
|
838
|
+
ArcType strokeMode = ArcType.OPEN;
|
839
|
+
|
840
|
+
if (mode == OPEN) {
|
841
|
+
fillMode = ArcType.OPEN;
|
842
|
+
|
843
|
+
} else if (mode == PIE) {
|
844
|
+
strokeMode = ArcType.ROUND; // PIE
|
845
|
+
|
846
|
+
} else if (mode == CHORD) {
|
847
|
+
fillMode = ArcType.CHORD;
|
848
|
+
strokeMode = ArcType.CHORD;
|
849
|
+
}
|
850
|
+
|
851
|
+
if (fill) {
|
852
|
+
context.fillArc(x, y, w, h, PApplet.degrees(start), PApplet.degrees(sweep), fillMode);
|
853
|
+
}
|
854
|
+
if (stroke) {
|
855
|
+
context.strokeArc(x, y, w, h, PApplet.degrees(start), PApplet.degrees(sweep), strokeMode);
|
856
|
+
}
|
857
|
+
}
|
858
|
+
|
859
|
+
|
860
|
+
|
861
|
+
//////////////////////////////////////////////////////////////
|
862
|
+
|
863
|
+
// BOX
|
864
|
+
|
865
|
+
|
866
|
+
//public void box(float size)
|
867
|
+
|
868
|
+
|
869
|
+
@Override
|
870
|
+
public void box(float w, float h, float d) {
|
871
|
+
showMethodWarning("box");
|
872
|
+
}
|
873
|
+
|
874
|
+
|
875
|
+
|
876
|
+
//////////////////////////////////////////////////////////////
|
877
|
+
|
878
|
+
// SPHERE
|
879
|
+
|
880
|
+
|
881
|
+
//public void sphereDetail(int res)
|
882
|
+
|
883
|
+
|
884
|
+
//public void sphereDetail(int ures, int vres)
|
885
|
+
|
886
|
+
|
887
|
+
@Override
|
888
|
+
public void sphere(float r) {
|
889
|
+
showMethodWarning("sphere");
|
890
|
+
}
|
891
|
+
|
892
|
+
|
893
|
+
|
894
|
+
//////////////////////////////////////////////////////////////
|
895
|
+
|
896
|
+
// BEZIER
|
897
|
+
|
898
|
+
|
899
|
+
//public float bezierPoint(float a, float b, float c, float d, float t)
|
900
|
+
|
901
|
+
|
902
|
+
//public float bezierTangent(float a, float b, float c, float d, float t)
|
903
|
+
|
904
|
+
|
905
|
+
//protected void bezierInitCheck()
|
906
|
+
|
907
|
+
|
908
|
+
//protected void bezierInit()
|
909
|
+
|
910
|
+
|
911
|
+
/** Ignored (not needed) by this renderer. */
|
912
|
+
@Override
|
913
|
+
public void bezierDetail(int detail) { }
|
914
|
+
|
915
|
+
|
916
|
+
//public void bezier(float x1, float y1,
|
917
|
+
// float x2, float y2,
|
918
|
+
// float x3, float y3,
|
919
|
+
// float x4, float y4)
|
920
|
+
|
921
|
+
|
922
|
+
//public void bezier(float x1, float y1, float z1,
|
923
|
+
// float x2, float y2, float z2,
|
924
|
+
// float x3, float y3, float z3,
|
925
|
+
// float x4, float y4, float z4)
|
926
|
+
|
927
|
+
|
928
|
+
|
929
|
+
//////////////////////////////////////////////////////////////
|
930
|
+
|
931
|
+
// CURVE
|
932
|
+
|
933
|
+
|
934
|
+
//public float curvePoint(float a, float b, float c, float d, float t)
|
935
|
+
|
936
|
+
|
937
|
+
//public float curveTangent(float a, float b, float c, float d, float t)
|
938
|
+
|
939
|
+
|
940
|
+
/** Ignored (not needed) by this renderer. */
|
941
|
+
@Override
|
942
|
+
public void curveDetail(int detail) { }
|
943
|
+
|
944
|
+
|
945
|
+
//public void curveTightness(float tightness)
|
946
|
+
|
947
|
+
|
948
|
+
//protected void curveInitCheck()
|
949
|
+
|
950
|
+
|
951
|
+
//protected void curveInit()
|
952
|
+
|
953
|
+
|
954
|
+
//public void curve(float x1, float y1,
|
955
|
+
// float x2, float y2,
|
956
|
+
// float x3, float y3,
|
957
|
+
// float x4, float y4)
|
958
|
+
|
959
|
+
|
960
|
+
//public void curve(float x1, float y1, float z1,
|
961
|
+
// float x2, float y2, float z2,
|
962
|
+
// float x3, float y3, float z3,
|
963
|
+
// float x4, float y4, float z4)
|
964
|
+
|
965
|
+
|
966
|
+
|
967
|
+
//////////////////////////////////////////////////////////////
|
968
|
+
|
969
|
+
// SMOOTH
|
970
|
+
|
971
|
+
|
972
|
+
// @Override
|
973
|
+
// public void smooth() {
|
974
|
+
// smooth = true;
|
975
|
+
//
|
976
|
+
// if (quality == 0) {
|
977
|
+
// quality = 4; // change back to bicubic
|
978
|
+
// }
|
979
|
+
// }
|
980
|
+
|
981
|
+
|
982
|
+
// @Override
|
983
|
+
// public void smooth(int quality) {
|
984
|
+
//// this.quality = quality;
|
985
|
+
//// if (quality == 0) {
|
986
|
+
//// noSmooth();
|
987
|
+
//// } else {
|
988
|
+
//// smooth();
|
989
|
+
//// }
|
990
|
+
// showMissingWarning("smooth");
|
991
|
+
// }
|
992
|
+
//
|
993
|
+
//
|
994
|
+
// @Override
|
995
|
+
// public void noSmooth() {
|
996
|
+
// showMissingWarning("noSmooth");
|
997
|
+
// }
|
998
|
+
|
999
|
+
|
1000
|
+
|
1001
|
+
//////////////////////////////////////////////////////////////
|
1002
|
+
|
1003
|
+
// IMAGE
|
1004
|
+
|
1005
|
+
|
1006
|
+
//public void imageMode(int mode)
|
1007
|
+
|
1008
|
+
|
1009
|
+
//public void image(PImage image, float x, float y)
|
1010
|
+
|
1011
|
+
|
1012
|
+
//public void image(PImage image, float x, float y, float c, float d)
|
1013
|
+
|
1014
|
+
|
1015
|
+
//public void image(PImage image,
|
1016
|
+
// float a, float b, float c, float d,
|
1017
|
+
// int u1, int v1, int u2, int v2)
|
1018
|
+
|
1019
|
+
|
1020
|
+
/**
|
1021
|
+
* Handle renderer-specific image drawing.
|
1022
|
+
*/
|
1023
|
+
@Override
|
1024
|
+
protected void imageImpl(PImage who,
|
1025
|
+
float x1, float y1, float x2, float y2,
|
1026
|
+
int u1, int v1, int u2, int v2) {
|
1027
|
+
// Image not ready yet, or an error
|
1028
|
+
if (who.width <= 0 || who.height <= 0) return;
|
1029
|
+
|
1030
|
+
ImageCache cash = (ImageCache) getCache(who);
|
1031
|
+
|
1032
|
+
// Nuke the cache if the image was resized
|
1033
|
+
if (cash != null) {
|
1034
|
+
if (who.pixelWidth != cash.image.getWidth() ||
|
1035
|
+
who.pixelHeight != cash.image.getHeight()) {
|
1036
|
+
cash = null;
|
1037
|
+
}
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
if (cash == null) {
|
1041
|
+
//System.out.println("making new image cache");
|
1042
|
+
cash = new ImageCache(); //who);
|
1043
|
+
setCache(who, cash);
|
1044
|
+
who.updatePixels(); // mark the whole thing for update
|
1045
|
+
who.setModified();
|
1046
|
+
}
|
1047
|
+
|
1048
|
+
// If image previously was tinted, or the color changed
|
1049
|
+
// or the image was tinted, and tint is now disabled
|
1050
|
+
if ((tint && !cash.tinted) ||
|
1051
|
+
(tint && (cash.tintedColor != tintColor)) ||
|
1052
|
+
(!tint && cash.tinted)) {
|
1053
|
+
// For tint change, mark all pixels as needing update.
|
1054
|
+
who.updatePixels();
|
1055
|
+
}
|
1056
|
+
|
1057
|
+
if (who.isModified()) {
|
1058
|
+
if (who.pixels == null) {
|
1059
|
+
// This might be a PGraphics that hasn't been drawn to yet.
|
1060
|
+
// Can't just bail because the cache has been created above.
|
1061
|
+
// https://github.com/processing/processing/issues/2208
|
1062
|
+
who.pixels = new int[who.pixelWidth * who.pixelHeight];
|
1063
|
+
}
|
1064
|
+
cash.update(who, tint, tintColor);
|
1065
|
+
who.setModified(false);
|
1066
|
+
}
|
1067
|
+
|
1068
|
+
u1 *= who.pixelDensity;
|
1069
|
+
v1 *= who.pixelDensity;
|
1070
|
+
u2 *= who.pixelDensity;
|
1071
|
+
v2 *= who.pixelDensity;
|
1072
|
+
|
1073
|
+
context.drawImage(((ImageCache) getCache(who)).image,
|
1074
|
+
u1, v1, u2-u1, v2-v1,
|
1075
|
+
x1, y1, x2-x1, y2-y1);
|
1076
|
+
}
|
1077
|
+
|
1078
|
+
|
1079
|
+
static class ImageCache {
|
1080
|
+
boolean tinted;
|
1081
|
+
int tintedColor;
|
1082
|
+
int[] tintedTemp; // one row of tinted pixels
|
1083
|
+
//BufferedImage image;
|
1084
|
+
WritableImage image;
|
1085
|
+
|
1086
|
+
/**
|
1087
|
+
* Update the pixels of the cache image. Already determined that the tint
|
1088
|
+
* has changed, or the pixels have changed, so should just go through
|
1089
|
+
* with the update without further checks.
|
1090
|
+
*/
|
1091
|
+
public void update(PImage source, boolean tint, int tintColor) {
|
1092
|
+
//int bufferType = BufferedImage.TYPE_INT_ARGB;
|
1093
|
+
int targetType = ARGB;
|
1094
|
+
boolean opaque = (tintColor & 0xFF000000) == 0xFF000000;
|
1095
|
+
if (source.format == RGB) {
|
1096
|
+
if (!tint || (tint && opaque)) {
|
1097
|
+
//bufferType = BufferedImage.TYPE_INT_RGB;
|
1098
|
+
targetType = RGB;
|
1099
|
+
}
|
1100
|
+
}
|
1101
|
+
// boolean wrongType = (image != null) && (image.getType() != bufferType);
|
1102
|
+
// if ((image == null) || wrongType) {
|
1103
|
+
// image = new BufferedImage(source.width, source.height, bufferType);
|
1104
|
+
// }
|
1105
|
+
// Must always use an ARGB image, otherwise will write zeros
|
1106
|
+
// in the alpha channel when drawn to the screen.
|
1107
|
+
// https://github.com/processing/processing/issues/2030
|
1108
|
+
// if (image == null) {
|
1109
|
+
// image = new BufferedImage(source.width, source.height,
|
1110
|
+
// BufferedImage.TYPE_INT_ARGB);
|
1111
|
+
// }
|
1112
|
+
if (image == null) {
|
1113
|
+
image = new WritableImage(source.pixelWidth, source.pixelHeight);
|
1114
|
+
}
|
1115
|
+
|
1116
|
+
//WritableRaster wr = image.getRaster();
|
1117
|
+
PixelWriter pw = image.getPixelWriter();
|
1118
|
+
if (tint) {
|
1119
|
+
if (tintedTemp == null || tintedTemp.length != source.pixelWidth) {
|
1120
|
+
tintedTemp = new int[source.pixelWidth];
|
1121
|
+
}
|
1122
|
+
int a2 = (tintColor >> 24) & 0xff;
|
1123
|
+
// System.out.println("tint color is " + a2);
|
1124
|
+
// System.out.println("source.pixels[0] alpha is " + (source.pixels[0] >>> 24));
|
1125
|
+
int r2 = (tintColor >> 16) & 0xff;
|
1126
|
+
int g2 = (tintColor >> 8) & 0xff;
|
1127
|
+
int b2 = (tintColor) & 0xff;
|
1128
|
+
|
1129
|
+
//if (bufferType == BufferedImage.TYPE_INT_RGB) {
|
1130
|
+
if (targetType == RGB) {
|
1131
|
+
// The target image is opaque, meaning that the source image has no
|
1132
|
+
// alpha (is not ARGB), and the tint has no alpha.
|
1133
|
+
int index = 0;
|
1134
|
+
for (int y = 0; y < source.pixelHeight; y++) {
|
1135
|
+
for (int x = 0; x < source.pixelWidth; x++) {
|
1136
|
+
int argb1 = source.pixels[index++];
|
1137
|
+
int r1 = (argb1 >> 16) & 0xff;
|
1138
|
+
int g1 = (argb1 >> 8) & 0xff;
|
1139
|
+
int b1 = (argb1) & 0xff;
|
1140
|
+
|
1141
|
+
// Prior to 2.1, the alpha channel was commented out here,
|
1142
|
+
// but can't remember why (just thought unnecessary b/c of RGB?)
|
1143
|
+
// https://github.com/processing/processing/issues/2030
|
1144
|
+
tintedTemp[x] = 0xFF000000 |
|
1145
|
+
(((r2 * r1) & 0xff00) << 8) |
|
1146
|
+
((g2 * g1) & 0xff00) |
|
1147
|
+
(((b2 * b1) & 0xff00) >> 8);
|
1148
|
+
}
|
1149
|
+
//wr.setDataElements(0, y, source.width, 1, tintedTemp);
|
1150
|
+
pw.setPixels(0, y, source.pixelWidth, 1, argbFormat, tintedTemp, 0, source.pixelWidth);
|
1151
|
+
}
|
1152
|
+
// could this be any slower?
|
1153
|
+
// float[] scales = { tintR, tintG, tintB };
|
1154
|
+
// float[] offsets = new float[3];
|
1155
|
+
// RescaleOp op = new RescaleOp(scales, offsets, null);
|
1156
|
+
// op.filter(image, image);
|
1157
|
+
|
1158
|
+
//} else if (bufferType == BufferedImage.TYPE_INT_ARGB) {
|
1159
|
+
} else if (targetType == ARGB) {
|
1160
|
+
if (source.format == RGB &&
|
1161
|
+
(tintColor & 0xffffff) == 0xffffff) {
|
1162
|
+
int hi = tintColor & 0xff000000;
|
1163
|
+
int index = 0;
|
1164
|
+
for (int y = 0; y < source.pixelHeight; y++) {
|
1165
|
+
for (int x = 0; x < source.pixelWidth; x++) {
|
1166
|
+
tintedTemp[x] = hi | (source.pixels[index++] & 0xFFFFFF);
|
1167
|
+
}
|
1168
|
+
//wr.setDataElements(0, y, source.width, 1, tintedTemp);
|
1169
|
+
pw.setPixels(0, y, source.pixelWidth, 1, argbFormat, tintedTemp, 0, source.pixelWidth);
|
1170
|
+
}
|
1171
|
+
} else {
|
1172
|
+
int index = 0;
|
1173
|
+
for (int y = 0; y < source.pixelHeight; y++) {
|
1174
|
+
if (source.format == RGB) {
|
1175
|
+
int alpha = tintColor & 0xFF000000;
|
1176
|
+
for (int x = 0; x < source.pixelWidth; x++) {
|
1177
|
+
int argb1 = source.pixels[index++];
|
1178
|
+
int r1 = (argb1 >> 16) & 0xff;
|
1179
|
+
int g1 = (argb1 >> 8) & 0xff;
|
1180
|
+
int b1 = (argb1) & 0xff;
|
1181
|
+
tintedTemp[x] = alpha |
|
1182
|
+
(((r2 * r1) & 0xff00) << 8) |
|
1183
|
+
((g2 * g1) & 0xff00) |
|
1184
|
+
(((b2 * b1) & 0xff00) >> 8);
|
1185
|
+
}
|
1186
|
+
} else if (source.format == ARGB) {
|
1187
|
+
for (int x = 0; x < source.pixelWidth; x++) {
|
1188
|
+
int argb1 = source.pixels[index++];
|
1189
|
+
int a1 = (argb1 >> 24) & 0xff;
|
1190
|
+
int r1 = (argb1 >> 16) & 0xff;
|
1191
|
+
int g1 = (argb1 >> 8) & 0xff;
|
1192
|
+
int b1 = (argb1) & 0xff;
|
1193
|
+
tintedTemp[x] =
|
1194
|
+
(((a2 * a1) & 0xff00) << 16) |
|
1195
|
+
(((r2 * r1) & 0xff00) << 8) |
|
1196
|
+
((g2 * g1) & 0xff00) |
|
1197
|
+
(((b2 * b1) & 0xff00) >> 8);
|
1198
|
+
}
|
1199
|
+
} else if (source.format == ALPHA) {
|
1200
|
+
int lower = tintColor & 0xFFFFFF;
|
1201
|
+
for (int x = 0; x < source.pixelWidth; x++) {
|
1202
|
+
int a1 = source.pixels[index++];
|
1203
|
+
tintedTemp[x] =
|
1204
|
+
(((a2 * a1) & 0xff00) << 16) | lower;
|
1205
|
+
}
|
1206
|
+
}
|
1207
|
+
//wr.setDataElements(0, y, source.width, 1, tintedTemp);
|
1208
|
+
pw.setPixels(0, y, source.pixelWidth, 1, argbFormat, tintedTemp, 0, source.pixelWidth);
|
1209
|
+
}
|
1210
|
+
}
|
1211
|
+
// Not sure why ARGB images take the scales in this order...
|
1212
|
+
// float[] scales = { tintR, tintG, tintB, tintA };
|
1213
|
+
// float[] offsets = new float[4];
|
1214
|
+
// RescaleOp op = new RescaleOp(scales, offsets, null);
|
1215
|
+
// op.filter(image, image);
|
1216
|
+
}
|
1217
|
+
} else { // !tint
|
1218
|
+
if (targetType == RGB && (source.pixels[0] >> 24 == 0)) {
|
1219
|
+
// If it's an RGB image and the high bits aren't set, need to set
|
1220
|
+
// the high bits to opaque because we're drawing ARGB images.
|
1221
|
+
source.filter(OPAQUE);
|
1222
|
+
// Opting to just manipulate the image here, since it shouldn't
|
1223
|
+
// affect anything else (and alpha(get(x, y)) should return 0xff).
|
1224
|
+
// Wel also make no guarantees about the values of the pixels array
|
1225
|
+
// in a PImage and how the high bits will be set.
|
1226
|
+
}
|
1227
|
+
// If no tint, just shove the pixels on in there verbatim
|
1228
|
+
//wr.setDataElements(0, 0, source.width, source.height, source.pixels);
|
1229
|
+
//System.out.println("moving the big one");
|
1230
|
+
pw.setPixels(0, 0, source.pixelWidth, source.pixelHeight,
|
1231
|
+
argbFormat, source.pixels, 0, source.pixelWidth);
|
1232
|
+
}
|
1233
|
+
this.tinted = tint;
|
1234
|
+
this.tintedColor = tintColor;
|
1235
|
+
|
1236
|
+
// GraphicsConfiguration gc = parent.getGraphicsConfiguration();
|
1237
|
+
// compat = gc.createCompatibleImage(image.getWidth(),
|
1238
|
+
// image.getHeight(),
|
1239
|
+
// Transparency.TRANSLUCENT);
|
1240
|
+
//
|
1241
|
+
// Graphics2D g = compat.createGraphics();
|
1242
|
+
// g.drawImage(image, 0, 0, null);
|
1243
|
+
// g.dispose();
|
1244
|
+
}
|
1245
|
+
}
|
1246
|
+
|
1247
|
+
|
1248
|
+
|
1249
|
+
//////////////////////////////////////////////////////////////
|
1250
|
+
|
1251
|
+
// SHAPE
|
1252
|
+
|
1253
|
+
|
1254
|
+
//public void shapeMode(int mode)
|
1255
|
+
|
1256
|
+
|
1257
|
+
//public void shape(PShape shape)
|
1258
|
+
|
1259
|
+
|
1260
|
+
//public void shape(PShape shape, float x, float y)
|
1261
|
+
|
1262
|
+
|
1263
|
+
//public void shape(PShape shape, float x, float y, float c, float d)
|
1264
|
+
|
1265
|
+
|
1266
|
+
//////////////////////////////////////////////////////////////
|
1267
|
+
|
1268
|
+
// SHAPE I/O
|
1269
|
+
|
1270
|
+
|
1271
|
+
@Override
|
1272
|
+
public PShape loadShape(String filename) {
|
1273
|
+
return loadShape(filename, null);
|
1274
|
+
}
|
1275
|
+
|
1276
|
+
|
1277
|
+
@Override
|
1278
|
+
public PShape loadShape(String filename, String options) {
|
1279
|
+
String extension = PApplet.getExtension(filename);
|
1280
|
+
if (extension.equals("svg") || extension.equals("svgz")) {
|
1281
|
+
return new PShapeSVG(parent.loadXML(filename));
|
1282
|
+
}
|
1283
|
+
PGraphics.showWarning("Unsupported format: " + filename);
|
1284
|
+
return null;
|
1285
|
+
}
|
1286
|
+
|
1287
|
+
|
1288
|
+
|
1289
|
+
//////////////////////////////////////////////////////////////
|
1290
|
+
|
1291
|
+
// TEXT ATTRIBTUES
|
1292
|
+
|
1293
|
+
|
1294
|
+
protected FontCache fontCache = new FontCache();
|
1295
|
+
|
1296
|
+
// Is initialized when defaultFontOrDeath() is called
|
1297
|
+
// and mirrors PGraphics.textFont field
|
1298
|
+
protected FontInfo textFontInfo;
|
1299
|
+
|
1300
|
+
|
1301
|
+
@Override
|
1302
|
+
protected PFont createFont(String name, float size,
|
1303
|
+
boolean smooth, char[] charset) {
|
1304
|
+
PFont font = super.createFont(name, size, smooth, charset);
|
1305
|
+
if (font.isStream()) {
|
1306
|
+
fontCache.nameToFilename.put(font.getName(), name);
|
1307
|
+
}
|
1308
|
+
return font;
|
1309
|
+
}
|
1310
|
+
|
1311
|
+
|
1312
|
+
@Override
|
1313
|
+
protected void defaultFontOrDeath(String method, float size) {
|
1314
|
+
super.defaultFontOrDeath(method, size);
|
1315
|
+
handleTextFont(textFont, size);
|
1316
|
+
}
|
1317
|
+
|
1318
|
+
|
1319
|
+
@Override
|
1320
|
+
protected boolean textModeCheck(int mode) {
|
1321
|
+
return mode == MODEL;
|
1322
|
+
}
|
1323
|
+
|
1324
|
+
|
1325
|
+
@Override
|
1326
|
+
public float textAscent() {
|
1327
|
+
if (textFont == null) {
|
1328
|
+
defaultFontOrDeath("textAscent");
|
1329
|
+
}
|
1330
|
+
if (textFontInfo.font == null) {
|
1331
|
+
return super.textAscent();
|
1332
|
+
}
|
1333
|
+
return textFontInfo.ascent;
|
1334
|
+
}
|
1335
|
+
|
1336
|
+
|
1337
|
+
@Override
|
1338
|
+
public float textDescent() {
|
1339
|
+
if (textFont == null) {
|
1340
|
+
defaultFontOrDeath("textDescent");
|
1341
|
+
}
|
1342
|
+
if (textFontInfo.font == null) {
|
1343
|
+
return super.textDescent();
|
1344
|
+
}
|
1345
|
+
return textFontInfo.descent;
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
|
1349
|
+
static final class FontInfo {
|
1350
|
+
// TODO: this should be based on memory consumption
|
1351
|
+
// this should be enough e.g. for all grays and alpha combos
|
1352
|
+
static final int MAX_CACHED_COLORS_PER_FONT = 1 << 16;
|
1353
|
+
|
1354
|
+
// used only when there is native font
|
1355
|
+
Font font;
|
1356
|
+
float ascent;
|
1357
|
+
float descent;
|
1358
|
+
|
1359
|
+
// used only when there is no native font
|
1360
|
+
// maps 32-bit color to the arrays of tinted glyph images
|
1361
|
+
Map<Integer, PImage[]> tintCache;
|
1362
|
+
}
|
1363
|
+
|
1364
|
+
|
1365
|
+
static final class FontCache {
|
1366
|
+
static final int MAX_CACHE_SIZE = 512;
|
1367
|
+
|
1368
|
+
// keeps track of filenames of fonts loaded from ttf and otf files
|
1369
|
+
Map<String, String> nameToFilename = new HashMap<>();
|
1370
|
+
|
1371
|
+
// keeps track of fonts which should be rendered as pictures
|
1372
|
+
// so we don't go through native font search process every time
|
1373
|
+
final HashSet<String> nonNativeNames = new HashSet<>();
|
1374
|
+
|
1375
|
+
// keeps all created fonts for reuse up to MAX_CACHE_SIZE limit
|
1376
|
+
// when the limit is reached, the least recently used font is removed
|
1377
|
+
// TODO: this should be based on memory consumtion
|
1378
|
+
final LinkedHashMap<Key, FontInfo> cache =
|
1379
|
+
new LinkedHashMap<Key, FontInfo>(16, 0.75f, true) {
|
1380
|
+
@Override
|
1381
|
+
protected boolean removeEldestEntry(Map.Entry<Key, FontInfo> eldest) {
|
1382
|
+
return size() > MAX_CACHE_SIZE;
|
1383
|
+
}
|
1384
|
+
};
|
1385
|
+
|
1386
|
+
// key for retrieving fonts from cache; don't use for insertion,
|
1387
|
+
// every font has to have its own new Key instance
|
1388
|
+
final Key retrievingKey = new Key();
|
1389
|
+
|
1390
|
+
// text node used for measuring sizes of text
|
1391
|
+
final Text measuringText = new Text();
|
1392
|
+
|
1393
|
+
FontInfo get(String name, float size) {
|
1394
|
+
if (nonNativeNames.contains(name)) {
|
1395
|
+
// Don't have native font, using glyph images.
|
1396
|
+
// Size is set to zero, because all sizes of this font
|
1397
|
+
// should share one FontInfo with one tintCache.
|
1398
|
+
size = 0;
|
1399
|
+
}
|
1400
|
+
retrievingKey.name = name;
|
1401
|
+
retrievingKey.size = size;
|
1402
|
+
return cache.get(retrievingKey);
|
1403
|
+
}
|
1404
|
+
|
1405
|
+
void put(String name, float size, FontInfo fontInfo) {
|
1406
|
+
if (fontInfo.font == null) {
|
1407
|
+
// Don't have native font, using glyph images.
|
1408
|
+
// Size is set to zero, because all sizes of this font
|
1409
|
+
// should share one FontInfo with one tintCache.
|
1410
|
+
nonNativeNames.add(name);
|
1411
|
+
size = 0;
|
1412
|
+
}
|
1413
|
+
Key key = new Key();
|
1414
|
+
key.name = name;
|
1415
|
+
key.size = size;
|
1416
|
+
cache.put(key, fontInfo);
|
1417
|
+
}
|
1418
|
+
|
1419
|
+
FontInfo createFontInfo(Font font) {
|
1420
|
+
FontInfo result = new FontInfo();
|
1421
|
+
result.font = font;
|
1422
|
+
if (font != null) {
|
1423
|
+
// measure ascent and descent
|
1424
|
+
measuringText.setFont(result.font);
|
1425
|
+
measuringText.setText(" ");
|
1426
|
+
float lineHeight = (float) measuringText.getLayoutBounds().getHeight();
|
1427
|
+
result.ascent = (float) measuringText.getBaselineOffset();
|
1428
|
+
result.descent = lineHeight - result.ascent;
|
1429
|
+
}
|
1430
|
+
return result;
|
1431
|
+
}
|
1432
|
+
|
1433
|
+
static final class Key {
|
1434
|
+
String name;
|
1435
|
+
float size;
|
1436
|
+
|
1437
|
+
@Override
|
1438
|
+
public boolean equals(Object o) {
|
1439
|
+
if (this == o) return true;
|
1440
|
+
if (o == null || getClass() != o.getClass()) return false;
|
1441
|
+
Key that = (Key) o;
|
1442
|
+
if (Float.compare(that.size, size) != 0) return false;
|
1443
|
+
return name.equals(that.name);
|
1444
|
+
}
|
1445
|
+
|
1446
|
+
@Override
|
1447
|
+
public int hashCode() {
|
1448
|
+
int result = name.hashCode();
|
1449
|
+
result = 31 * result + (size != +0.0f ? Float.floatToIntBits(size) : 0);
|
1450
|
+
return result;
|
1451
|
+
}
|
1452
|
+
}
|
1453
|
+
}
|
1454
|
+
|
1455
|
+
|
1456
|
+
///////////////////////////////////////////////////////////////
|
1457
|
+
|
1458
|
+
// TEXT
|
1459
|
+
|
1460
|
+
// None of the variations of text() are overridden from PGraphics.
|
1461
|
+
|
1462
|
+
|
1463
|
+
|
1464
|
+
//////////////////////////////////////////////////////////////
|
1465
|
+
|
1466
|
+
// TEXT IMPL
|
1467
|
+
|
1468
|
+
|
1469
|
+
@Override
|
1470
|
+
protected void textFontImpl(PFont which, float size) {
|
1471
|
+
handleTextFont(which, size);
|
1472
|
+
handleTextSize(size);
|
1473
|
+
}
|
1474
|
+
|
1475
|
+
|
1476
|
+
@Override
|
1477
|
+
protected void textSizeImpl(float size) {
|
1478
|
+
handleTextFont(textFont, size);
|
1479
|
+
handleTextSize(size);
|
1480
|
+
}
|
1481
|
+
|
1482
|
+
|
1483
|
+
/**
|
1484
|
+
* FX specific. When setting font or size, new font has to
|
1485
|
+
* be created. Both textFontImpl and textSizeImpl call this one.
|
1486
|
+
* @param which font to be set, not null
|
1487
|
+
* @param size size to be set, greater than zero
|
1488
|
+
*/
|
1489
|
+
protected void handleTextFont(PFont which, float size) {
|
1490
|
+
textFont = which;
|
1491
|
+
|
1492
|
+
String fontName = which.getName();
|
1493
|
+
String fontPsName = which.getPostScriptName();
|
1494
|
+
|
1495
|
+
textFontInfo = fontCache.get(fontName, size);
|
1496
|
+
if (textFontInfo == null) {
|
1497
|
+
Font font = null;
|
1498
|
+
|
1499
|
+
if (which.isStream()) {
|
1500
|
+
// Load from ttf or otf file
|
1501
|
+
String filename = fontCache.nameToFilename.get(fontName);
|
1502
|
+
font = Font.loadFont(parent.createInput(filename), size);
|
1503
|
+
}
|
1504
|
+
|
1505
|
+
if (font == null) {
|
1506
|
+
// Look up font name
|
1507
|
+
font = new Font(fontName, size);
|
1508
|
+
if (!fontName.equalsIgnoreCase(font.getName())) {
|
1509
|
+
// Look up font postscript name
|
1510
|
+
font = new Font(fontPsName, size);
|
1511
|
+
if (!fontPsName.equalsIgnoreCase(font.getName())) {
|
1512
|
+
font = null; // Done with it
|
1513
|
+
}
|
1514
|
+
}
|
1515
|
+
}
|
1516
|
+
|
1517
|
+
if (font == null && which.getNative() != null) {
|
1518
|
+
// Ain't got nothing, but AWT has something, so glyph images are not
|
1519
|
+
// going to be used for this font; go with the default font then
|
1520
|
+
font = new Font(size);
|
1521
|
+
}
|
1522
|
+
|
1523
|
+
textFontInfo = fontCache.createFontInfo(font);
|
1524
|
+
fontCache.put(fontName, size, textFontInfo);
|
1525
|
+
}
|
1526
|
+
|
1527
|
+
context.setFont(textFontInfo.font);
|
1528
|
+
}
|
1529
|
+
|
1530
|
+
|
1531
|
+
@Override
|
1532
|
+
protected void textLineImpl(char[] buffer, int start, int stop, float x, float y) {
|
1533
|
+
if (textFontInfo.font == null) {
|
1534
|
+
super.textLineImpl(buffer, start, stop, x, y);
|
1535
|
+
} else {
|
1536
|
+
context.fillText(new String(buffer, start, stop - start), x, y);
|
1537
|
+
}
|
1538
|
+
}
|
1539
|
+
|
1540
|
+
|
1541
|
+
protected PImage getTintedGlyphImage(PFont.Glyph glyph, int tintColor) {
|
1542
|
+
if (textFontInfo.tintCache == null) {
|
1543
|
+
textFontInfo.tintCache = new LinkedHashMap<Integer, PImage[]>(16, 0.75f, true) {
|
1544
|
+
@Override
|
1545
|
+
protected boolean removeEldestEntry(Map.Entry<Integer, PImage[]> eldest) {
|
1546
|
+
return size() > FontInfo.MAX_CACHED_COLORS_PER_FONT;
|
1547
|
+
}
|
1548
|
+
};
|
1549
|
+
}
|
1550
|
+
PImage[] tintedGlyphs = textFontInfo.tintCache.get(tintColor);
|
1551
|
+
int index = glyph.index;
|
1552
|
+
if (tintedGlyphs == null || tintedGlyphs.length <= index) {
|
1553
|
+
PImage[] newArray = new PImage[textFont.getGlyphCount()];
|
1554
|
+
if (tintedGlyphs != null) {
|
1555
|
+
System.arraycopy(tintedGlyphs, 0, newArray, 0, tintedGlyphs.length);
|
1556
|
+
}
|
1557
|
+
tintedGlyphs = newArray;
|
1558
|
+
textFontInfo.tintCache.put(tintColor, tintedGlyphs);
|
1559
|
+
}
|
1560
|
+
PImage tintedGlyph = tintedGlyphs[index];
|
1561
|
+
if (tintedGlyph == null) {
|
1562
|
+
tintedGlyph = glyph.image.copy();
|
1563
|
+
tintedGlyphs[index] = tintedGlyph;
|
1564
|
+
}
|
1565
|
+
return tintedGlyph;
|
1566
|
+
}
|
1567
|
+
|
1568
|
+
|
1569
|
+
@Override
|
1570
|
+
protected void textCharImpl(char ch, float x, float y) { //, float z) {
|
1571
|
+
PFont.Glyph glyph = textFont.getGlyph(ch);
|
1572
|
+
if (glyph != null) {
|
1573
|
+
if (textMode == MODEL) {
|
1574
|
+
float high = glyph.height / (float) textFont.getSize();
|
1575
|
+
float bwidth = glyph.width / (float) textFont.getSize();
|
1576
|
+
float lextent = glyph.leftExtent / (float) textFont.getSize();
|
1577
|
+
float textent = glyph.topExtent / (float) textFont.getSize();
|
1578
|
+
|
1579
|
+
float x1 = x + lextent * textSize;
|
1580
|
+
float y1 = y - textent * textSize;
|
1581
|
+
float x2 = x1 + bwidth * textSize;
|
1582
|
+
float y2 = y1 + high * textSize;
|
1583
|
+
|
1584
|
+
PImage glyphImage = (fillColor == 0xFFFFFFFF) ?
|
1585
|
+
glyph.image : getTintedGlyphImage(glyph, fillColor);
|
1586
|
+
|
1587
|
+
textCharModelImpl(glyphImage,
|
1588
|
+
x1, y1, x2, y2,
|
1589
|
+
glyph.width, glyph.height);
|
1590
|
+
}
|
1591
|
+
} else if (ch != ' ' && ch != 127) {
|
1592
|
+
showWarning("No glyph found for the " + ch +
|
1593
|
+
" (\\u" + PApplet.hex(ch, 4) + ") character");
|
1594
|
+
}
|
1595
|
+
}
|
1596
|
+
|
1597
|
+
|
1598
|
+
@Override
|
1599
|
+
protected float textWidthImpl(char[] buffer, int start, int stop) {
|
1600
|
+
if (textFont == null) {
|
1601
|
+
defaultFontOrDeath("textWidth");
|
1602
|
+
}
|
1603
|
+
|
1604
|
+
if (textFontInfo.font == null) {
|
1605
|
+
return super.textWidthImpl(buffer, start, stop);
|
1606
|
+
}
|
1607
|
+
|
1608
|
+
fontCache.measuringText.setFont(textFontInfo.font);
|
1609
|
+
fontCache.measuringText.setText(new String(buffer, start, stop - start));
|
1610
|
+
return (float) fontCache.measuringText.getLayoutBounds().getWidth();
|
1611
|
+
}
|
1612
|
+
|
1613
|
+
|
1614
|
+
|
1615
|
+
//////////////////////////////////////////////////////////////
|
1616
|
+
|
1617
|
+
// MATRIX STACK
|
1618
|
+
|
1619
|
+
|
1620
|
+
@Override
|
1621
|
+
public void pushMatrix() {
|
1622
|
+
if (transformCount == transformStack.length) {
|
1623
|
+
throw new RuntimeException("pushMatrix() cannot use push more than " +
|
1624
|
+
transformStack.length + " times");
|
1625
|
+
}
|
1626
|
+
transformStack[transformCount] = context.getTransform(transformStack[transformCount]);
|
1627
|
+
transformCount++;
|
1628
|
+
}
|
1629
|
+
|
1630
|
+
|
1631
|
+
@Override
|
1632
|
+
public void popMatrix() {
|
1633
|
+
if (transformCount == 0) {
|
1634
|
+
throw new RuntimeException("missing a pushMatrix() " +
|
1635
|
+
"to go with that popMatrix()");
|
1636
|
+
}
|
1637
|
+
transformCount--;
|
1638
|
+
context.setTransform(transformStack[transformCount]);
|
1639
|
+
}
|
1640
|
+
|
1641
|
+
|
1642
|
+
|
1643
|
+
//////////////////////////////////////////////////////////////
|
1644
|
+
|
1645
|
+
// MATRIX TRANSFORMS
|
1646
|
+
|
1647
|
+
|
1648
|
+
@Override
|
1649
|
+
public void translate(float tx, float ty) {
|
1650
|
+
context.translate(tx, ty);
|
1651
|
+
}
|
1652
|
+
|
1653
|
+
|
1654
|
+
//public void translate(float tx, float ty, float tz)
|
1655
|
+
|
1656
|
+
|
1657
|
+
@Override
|
1658
|
+
public void rotate(float angle) {
|
1659
|
+
context.rotate(PApplet.degrees(angle));
|
1660
|
+
}
|
1661
|
+
|
1662
|
+
|
1663
|
+
@Override
|
1664
|
+
public void rotateX(float angle) {
|
1665
|
+
showDepthWarning("rotateX");
|
1666
|
+
}
|
1667
|
+
|
1668
|
+
|
1669
|
+
@Override
|
1670
|
+
public void rotateY(float angle) {
|
1671
|
+
showDepthWarning("rotateY");
|
1672
|
+
}
|
1673
|
+
|
1674
|
+
|
1675
|
+
@Override
|
1676
|
+
public void rotateZ(float angle) {
|
1677
|
+
showDepthWarning("rotateZ");
|
1678
|
+
}
|
1679
|
+
|
1680
|
+
|
1681
|
+
@Override
|
1682
|
+
public void rotate(float angle, float vx, float vy, float vz) {
|
1683
|
+
showVariationWarning("rotate");
|
1684
|
+
}
|
1685
|
+
|
1686
|
+
|
1687
|
+
@Override
|
1688
|
+
public void scale(float s) {
|
1689
|
+
context.scale(s, s);
|
1690
|
+
}
|
1691
|
+
|
1692
|
+
|
1693
|
+
@Override
|
1694
|
+
public void scale(float sx, float sy) {
|
1695
|
+
context.scale(sx, sy);
|
1696
|
+
}
|
1697
|
+
|
1698
|
+
|
1699
|
+
@Override
|
1700
|
+
public void scale(float sx, float sy, float sz) {
|
1701
|
+
showDepthWarningXYZ("scale");
|
1702
|
+
}
|
1703
|
+
|
1704
|
+
|
1705
|
+
@Override
|
1706
|
+
public void shearX(float angle) {
|
1707
|
+
Affine temp = new Affine();
|
1708
|
+
temp.appendShear(Math.tan(angle), 0);
|
1709
|
+
context.transform(temp);
|
1710
|
+
}
|
1711
|
+
|
1712
|
+
|
1713
|
+
@Override
|
1714
|
+
public void shearY(float angle) {
|
1715
|
+
Affine temp = new Affine();
|
1716
|
+
temp.appendShear(0, Math.tan(angle));
|
1717
|
+
context.transform(temp);
|
1718
|
+
}
|
1719
|
+
|
1720
|
+
|
1721
|
+
|
1722
|
+
//////////////////////////////////////////////////////////////
|
1723
|
+
|
1724
|
+
// MATRIX MORE
|
1725
|
+
|
1726
|
+
|
1727
|
+
@Override
|
1728
|
+
public void resetMatrix() {
|
1729
|
+
context.setTransform(new Affine());
|
1730
|
+
}
|
1731
|
+
|
1732
|
+
|
1733
|
+
//public void applyMatrix(PMatrix2D source)
|
1734
|
+
|
1735
|
+
|
1736
|
+
@Override
|
1737
|
+
public void applyMatrix(float n00, float n01, float n02,
|
1738
|
+
float n10, float n11, float n12) {
|
1739
|
+
context.transform(n00, n10, n01, n11, n02, n12);
|
1740
|
+
}
|
1741
|
+
|
1742
|
+
|
1743
|
+
//public void applyMatrix(PMatrix3D source)
|
1744
|
+
|
1745
|
+
|
1746
|
+
@Override
|
1747
|
+
public void applyMatrix(float n00, float n01, float n02, float n03,
|
1748
|
+
float n10, float n11, float n12, float n13,
|
1749
|
+
float n20, float n21, float n22, float n23,
|
1750
|
+
float n30, float n31, float n32, float n33) {
|
1751
|
+
showVariationWarning("applyMatrix");
|
1752
|
+
}
|
1753
|
+
|
1754
|
+
|
1755
|
+
|
1756
|
+
//////////////////////////////////////////////////////////////
|
1757
|
+
|
1758
|
+
// MATRIX GET/SET
|
1759
|
+
|
1760
|
+
|
1761
|
+
@Override
|
1762
|
+
public PMatrix getMatrix() {
|
1763
|
+
return getMatrix((PMatrix2D) null);
|
1764
|
+
}
|
1765
|
+
|
1766
|
+
|
1767
|
+
@Override
|
1768
|
+
public PMatrix2D getMatrix(PMatrix2D target) {
|
1769
|
+
if (target == null) {
|
1770
|
+
target = new PMatrix2D();
|
1771
|
+
}
|
1772
|
+
//double[] transform = new double[6];
|
1773
|
+
// TODO This is not tested; apparently Affine is a full 3x4
|
1774
|
+
Affine t = context.getTransform(); //.getMatrix(transform);
|
1775
|
+
// target.set((float) transform[0], (float) transform[2], (float) transform[4],
|
1776
|
+
// (float) transform[1], (float) transform[3], (float) transform[5]);
|
1777
|
+
target.set((float) t.getMxx(), (float) t.getMxy(), (float) t.getTx(),
|
1778
|
+
(float) t.getMyx(), (float) t.getMyy(), (float) t.getTy());
|
1779
|
+
return target;
|
1780
|
+
}
|
1781
|
+
|
1782
|
+
|
1783
|
+
@Override
|
1784
|
+
public PMatrix3D getMatrix(PMatrix3D target) {
|
1785
|
+
showVariationWarning("getMatrix");
|
1786
|
+
return target;
|
1787
|
+
}
|
1788
|
+
|
1789
|
+
|
1790
|
+
//public void setMatrix(PMatrix source)
|
1791
|
+
|
1792
|
+
|
1793
|
+
@Override
|
1794
|
+
public void setMatrix(PMatrix2D source) {
|
1795
|
+
context.setTransform(source.m00, source.m10,
|
1796
|
+
source.m01, source.m11,
|
1797
|
+
source.m02, source.m12);
|
1798
|
+
}
|
1799
|
+
|
1800
|
+
|
1801
|
+
@Override
|
1802
|
+
public void setMatrix(PMatrix3D source) {
|
1803
|
+
showVariationWarning("setMatrix");
|
1804
|
+
}
|
1805
|
+
|
1806
|
+
|
1807
|
+
@Override
|
1808
|
+
public void printMatrix() {
|
1809
|
+
getMatrix((PMatrix2D) null).print();
|
1810
|
+
}
|
1811
|
+
|
1812
|
+
|
1813
|
+
|
1814
|
+
// //////////////////////////////////////////////////////////////
|
1815
|
+
//
|
1816
|
+
// // CAMERA and PROJECTION
|
1817
|
+
//
|
1818
|
+
// // Inherit the plaintive warnings from PGraphics
|
1819
|
+
//
|
1820
|
+
//
|
1821
|
+
// //public void beginCamera()
|
1822
|
+
// //public void endCamera()
|
1823
|
+
// //public void camera()
|
1824
|
+
// //public void camera(float eyeX, float eyeY, float eyeZ,
|
1825
|
+
// // float centerX, float centerY, float centerZ,
|
1826
|
+
// // float upX, float upY, float upZ)
|
1827
|
+
// //public void printCamera()
|
1828
|
+
//
|
1829
|
+
// //public void ortho()
|
1830
|
+
// //public void ortho(float left, float right,
|
1831
|
+
// // float bottom, float top,
|
1832
|
+
// // float near, float far)
|
1833
|
+
// //public void perspective()
|
1834
|
+
// //public void perspective(float fov, float aspect, float near, float far)
|
1835
|
+
// //public void frustum(float left, float right,
|
1836
|
+
// // float bottom, float top,
|
1837
|
+
// // float near, float far)
|
1838
|
+
// //public void printProjection()
|
1839
|
+
|
1840
|
+
|
1841
|
+
|
1842
|
+
//////////////////////////////////////////////////////////////
|
1843
|
+
|
1844
|
+
// SCREEN and MODEL transforms
|
1845
|
+
|
1846
|
+
|
1847
|
+
@Override
|
1848
|
+
public float screenX(float x, float y) {
|
1849
|
+
return (float) context.getTransform().transform(x, y).getX();
|
1850
|
+
}
|
1851
|
+
|
1852
|
+
|
1853
|
+
@Override
|
1854
|
+
public float screenY(float x, float y) {
|
1855
|
+
return (float) context.getTransform().transform(x, y).getY();
|
1856
|
+
}
|
1857
|
+
|
1858
|
+
|
1859
|
+
@Override
|
1860
|
+
public float screenX(float x, float y, float z) {
|
1861
|
+
showDepthWarningXYZ("screenX");
|
1862
|
+
return 0;
|
1863
|
+
}
|
1864
|
+
|
1865
|
+
|
1866
|
+
@Override
|
1867
|
+
public float screenY(float x, float y, float z) {
|
1868
|
+
showDepthWarningXYZ("screenY");
|
1869
|
+
return 0;
|
1870
|
+
}
|
1871
|
+
|
1872
|
+
|
1873
|
+
@Override
|
1874
|
+
public float screenZ(float x, float y, float z) {
|
1875
|
+
showDepthWarningXYZ("screenZ");
|
1876
|
+
return 0;
|
1877
|
+
}
|
1878
|
+
|
1879
|
+
|
1880
|
+
//public float modelX(float x, float y, float z)
|
1881
|
+
|
1882
|
+
|
1883
|
+
//public float modelY(float x, float y, float z)
|
1884
|
+
|
1885
|
+
|
1886
|
+
//public float modelZ(float x, float y, float z)
|
1887
|
+
|
1888
|
+
|
1889
|
+
|
1890
|
+
// //////////////////////////////////////////////////////////////
|
1891
|
+
//
|
1892
|
+
// // STYLE
|
1893
|
+
//
|
1894
|
+
// // pushStyle(), popStyle(), style() and getStyle() inherited.
|
1895
|
+
|
1896
|
+
|
1897
|
+
|
1898
|
+
//////////////////////////////////////////////////////////////
|
1899
|
+
|
1900
|
+
// STROKE CAP/JOIN/WEIGHT
|
1901
|
+
|
1902
|
+
|
1903
|
+
@Override
|
1904
|
+
public void strokeCap(int cap) {
|
1905
|
+
super.strokeCap(cap);
|
1906
|
+
if (strokeCap == ROUND) {
|
1907
|
+
context.setLineCap(StrokeLineCap.ROUND);
|
1908
|
+
} else if (strokeCap == PROJECT) {
|
1909
|
+
context.setLineCap(StrokeLineCap.SQUARE);
|
1910
|
+
} else {
|
1911
|
+
context.setLineCap(StrokeLineCap.BUTT);
|
1912
|
+
}
|
1913
|
+
}
|
1914
|
+
|
1915
|
+
|
1916
|
+
@Override
|
1917
|
+
public void strokeJoin(int join) {
|
1918
|
+
super.strokeJoin(join);
|
1919
|
+
if (strokeJoin == MITER) {
|
1920
|
+
context.setLineJoin(StrokeLineJoin.MITER);
|
1921
|
+
} else if (strokeJoin == ROUND) {
|
1922
|
+
context.setLineJoin(StrokeLineJoin.ROUND);
|
1923
|
+
} else {
|
1924
|
+
context.setLineJoin(StrokeLineJoin.BEVEL);
|
1925
|
+
}
|
1926
|
+
}
|
1927
|
+
|
1928
|
+
|
1929
|
+
@Override
|
1930
|
+
public void strokeWeight(float weight) {
|
1931
|
+
super.strokeWeight(weight);
|
1932
|
+
context.setLineWidth(weight);
|
1933
|
+
}
|
1934
|
+
|
1935
|
+
|
1936
|
+
|
1937
|
+
//////////////////////////////////////////////////////////////
|
1938
|
+
|
1939
|
+
// STROKE
|
1940
|
+
|
1941
|
+
// noStroke() and stroke() inherited from PGraphics.
|
1942
|
+
|
1943
|
+
|
1944
|
+
@Override
|
1945
|
+
protected void strokeFromCalc() {
|
1946
|
+
super.strokeFromCalc();
|
1947
|
+
context.setStroke(new Color(strokeR, strokeG, strokeB, strokeA));
|
1948
|
+
}
|
1949
|
+
|
1950
|
+
|
1951
|
+
protected boolean drawingThinLines() {
|
1952
|
+
// align strokes to pixel centers when drawing thin lines
|
1953
|
+
return stroke && strokeWeight == 1;
|
1954
|
+
}
|
1955
|
+
|
1956
|
+
|
1957
|
+
|
1958
|
+
//////////////////////////////////////////////////////////////
|
1959
|
+
|
1960
|
+
// TINT
|
1961
|
+
|
1962
|
+
// noTint() and tint() inherited from PGraphics.
|
1963
|
+
|
1964
|
+
|
1965
|
+
|
1966
|
+
//////////////////////////////////////////////////////////////
|
1967
|
+
|
1968
|
+
// FILL
|
1969
|
+
|
1970
|
+
// noFill() and fill() inherited from PGraphics.
|
1971
|
+
|
1972
|
+
|
1973
|
+
@Override
|
1974
|
+
protected void fillFromCalc() {
|
1975
|
+
super.fillFromCalc();
|
1976
|
+
context.setFill(new Color(fillR, fillG, fillB, fillA));
|
1977
|
+
}
|
1978
|
+
|
1979
|
+
|
1980
|
+
|
1981
|
+
// //////////////////////////////////////////////////////////////
|
1982
|
+
//
|
1983
|
+
// // MATERIAL PROPERTIES
|
1984
|
+
//
|
1985
|
+
//
|
1986
|
+
// //public void ambient(int rgb)
|
1987
|
+
// //public void ambient(float gray)
|
1988
|
+
// //public void ambient(float x, float y, float z)
|
1989
|
+
// //protected void ambientFromCalc()
|
1990
|
+
// //public void specular(int rgb)
|
1991
|
+
// //public void specular(float gray)
|
1992
|
+
// //public void specular(float x, float y, float z)
|
1993
|
+
// //protected void specularFromCalc()
|
1994
|
+
// //public void shininess(float shine)
|
1995
|
+
// //public void emissive(int rgb)
|
1996
|
+
// //public void emissive(float gray)
|
1997
|
+
// //public void emissive(float x, float y, float z )
|
1998
|
+
// //protected void emissiveFromCalc()
|
1999
|
+
//
|
2000
|
+
//
|
2001
|
+
//
|
2002
|
+
// //////////////////////////////////////////////////////////////
|
2003
|
+
//
|
2004
|
+
// // LIGHTS
|
2005
|
+
//
|
2006
|
+
//
|
2007
|
+
// //public void lights()
|
2008
|
+
// //public void noLights()
|
2009
|
+
// //public void ambientLight(float red, float green, float blue)
|
2010
|
+
// //public void ambientLight(float red, float green, float blue,
|
2011
|
+
// // float x, float y, float z)
|
2012
|
+
// //public void directionalLight(float red, float green, float blue,
|
2013
|
+
// // float nx, float ny, float nz)
|
2014
|
+
// //public void pointLight(float red, float green, float blue,
|
2015
|
+
// // float x, float y, float z)
|
2016
|
+
// //public void spotLight(float red, float green, float blue,
|
2017
|
+
// // float x, float y, float z,
|
2018
|
+
// // float nx, float ny, float nz,
|
2019
|
+
// // float angle, float concentration)
|
2020
|
+
// //public void lightFalloff(float constant, float linear, float quadratic)
|
2021
|
+
// //public void lightSpecular(float x, float y, float z)
|
2022
|
+
// //protected void lightPosition(int num, float x, float y, float z)
|
2023
|
+
// //protected void lightDirection(int num, float x, float y, float z)
|
2024
|
+
|
2025
|
+
|
2026
|
+
|
2027
|
+
//////////////////////////////////////////////////////////////
|
2028
|
+
|
2029
|
+
// BACKGROUND
|
2030
|
+
|
2031
|
+
|
2032
|
+
@Override
|
2033
|
+
public void backgroundImpl() {
|
2034
|
+
|
2035
|
+
// if pixels are modified, we don't flush them (just mark them flushed)
|
2036
|
+
// because they would be immediatelly overwritten by the background anyway
|
2037
|
+
modified = false;
|
2038
|
+
loaded = false;
|
2039
|
+
|
2040
|
+
// Save drawing context (transform, fill, blend mode, etc.)
|
2041
|
+
context.save();
|
2042
|
+
|
2043
|
+
// Reset transform to identity
|
2044
|
+
context.setTransform(new Affine());
|
2045
|
+
|
2046
|
+
// This only takes into account cases where this is the primary surface.
|
2047
|
+
// Not sure what we do with offscreen anyway.
|
2048
|
+
context.setFill(new Color(backgroundR, backgroundG, backgroundB, backgroundA));
|
2049
|
+
context.setGlobalBlendMode(BlendMode.SRC_OVER);
|
2050
|
+
context.fillRect(0, 0, width, height);
|
2051
|
+
|
2052
|
+
// Restore drawing context (transform, fill, blend mode, etc.)
|
2053
|
+
context.restore();
|
2054
|
+
}
|
2055
|
+
|
2056
|
+
|
2057
|
+
|
2058
|
+
// //////////////////////////////////////////////////////////////
|
2059
|
+
//
|
2060
|
+
// // COLOR MODE
|
2061
|
+
//
|
2062
|
+
// // All colorMode() variations are inherited from PGraphics.
|
2063
|
+
//
|
2064
|
+
//
|
2065
|
+
//
|
2066
|
+
// //////////////////////////////////////////////////////////////
|
2067
|
+
//
|
2068
|
+
// // COLOR CALC
|
2069
|
+
//
|
2070
|
+
// // colorCalc() and colorCalcARGB() inherited from PGraphics.
|
2071
|
+
//
|
2072
|
+
//
|
2073
|
+
//
|
2074
|
+
// //////////////////////////////////////////////////////////////
|
2075
|
+
//
|
2076
|
+
// // COLOR DATATYPE STUFFING
|
2077
|
+
//
|
2078
|
+
// // final color() variations inherited.
|
2079
|
+
//
|
2080
|
+
//
|
2081
|
+
//
|
2082
|
+
// //////////////////////////////////////////////////////////////
|
2083
|
+
//
|
2084
|
+
// // COLOR DATATYPE EXTRACTION
|
2085
|
+
//
|
2086
|
+
// // final methods alpha, red, green, blue,
|
2087
|
+
// // hue, saturation, and brightness all inherited.
|
2088
|
+
//
|
2089
|
+
//
|
2090
|
+
//
|
2091
|
+
// //////////////////////////////////////////////////////////////
|
2092
|
+
//
|
2093
|
+
// // COLOR DATATYPE INTERPOLATION
|
2094
|
+
//
|
2095
|
+
// // both lerpColor variants inherited.
|
2096
|
+
//
|
2097
|
+
//
|
2098
|
+
//
|
2099
|
+
// //////////////////////////////////////////////////////////////
|
2100
|
+
//
|
2101
|
+
// // BEGIN/END RAW
|
2102
|
+
//
|
2103
|
+
//
|
2104
|
+
// @Override
|
2105
|
+
// public void beginRaw(PGraphics recorderRaw) {
|
2106
|
+
// showMethodWarning("beginRaw");
|
2107
|
+
// }
|
2108
|
+
//
|
2109
|
+
//
|
2110
|
+
// @Override
|
2111
|
+
// public void endRaw() {
|
2112
|
+
// showMethodWarning("endRaw");
|
2113
|
+
// }
|
2114
|
+
//
|
2115
|
+
//
|
2116
|
+
//
|
2117
|
+
// //////////////////////////////////////////////////////////////
|
2118
|
+
//
|
2119
|
+
// // WARNINGS and EXCEPTIONS
|
2120
|
+
//
|
2121
|
+
// // showWarning and showException inherited.
|
2122
|
+
//
|
2123
|
+
//
|
2124
|
+
//
|
2125
|
+
// //////////////////////////////////////////////////////////////
|
2126
|
+
//
|
2127
|
+
// // RENDERER SUPPORT QUERIES
|
2128
|
+
//
|
2129
|
+
//
|
2130
|
+
// //public boolean displayable() // true
|
2131
|
+
//
|
2132
|
+
//
|
2133
|
+
// //public boolean is2D() // true
|
2134
|
+
//
|
2135
|
+
//
|
2136
|
+
// //public boolean is3D() // false
|
2137
|
+
|
2138
|
+
|
2139
|
+
|
2140
|
+
//////////////////////////////////////////////////////////////
|
2141
|
+
|
2142
|
+
// PIMAGE METHODS
|
2143
|
+
|
2144
|
+
|
2145
|
+
@Override
|
2146
|
+
public void loadPixels() {
|
2147
|
+
if ((pixels == null) || (pixels.length != pixelWidth * pixelHeight)) {
|
2148
|
+
pixels = new int[pixelWidth * pixelHeight];
|
2149
|
+
loaded = false;
|
2150
|
+
}
|
2151
|
+
|
2152
|
+
if (!loaded) {
|
2153
|
+
if (snapshotImage == null ||
|
2154
|
+
snapshotImage.getWidth() != pixelWidth ||
|
2155
|
+
snapshotImage.getHeight() != pixelHeight) {
|
2156
|
+
snapshotImage = new WritableImage(pixelWidth, pixelHeight);
|
2157
|
+
}
|
2158
|
+
|
2159
|
+
SnapshotParameters sp = null;
|
2160
|
+
if (pixelDensity != 1) {
|
2161
|
+
sp = new SnapshotParameters();
|
2162
|
+
sp.setTransform(Transform.scale(pixelDensity, pixelDensity));
|
2163
|
+
}
|
2164
|
+
snapshotImage = ((PSurfaceFX) surface).canvas.snapshot(sp, snapshotImage);
|
2165
|
+
PixelReader pr = snapshotImage.getPixelReader();
|
2166
|
+
pr.getPixels(0, 0, pixelWidth, pixelHeight, argbFormat, pixels, 0, pixelWidth);
|
2167
|
+
|
2168
|
+
loaded = true;
|
2169
|
+
modified = false;
|
2170
|
+
}
|
2171
|
+
}
|
2172
|
+
|
2173
|
+
|
2174
|
+
|
2175
|
+
//////////////////////////////////////////////////////////////
|
2176
|
+
|
2177
|
+
// GET/SET PIXELS
|
2178
|
+
|
2179
|
+
|
2180
|
+
@Override
|
2181
|
+
public int get(int x, int y) {
|
2182
|
+
loadPixels();
|
2183
|
+
return super.get(x, y);
|
2184
|
+
}
|
2185
|
+
|
2186
|
+
|
2187
|
+
@Override
|
2188
|
+
protected void getImpl(int sourceX, int sourceY,
|
2189
|
+
int sourceWidth, int sourceHeight,
|
2190
|
+
PImage target, int targetX, int targetY) {
|
2191
|
+
loadPixels();
|
2192
|
+
super.getImpl(sourceX, sourceY, sourceWidth, sourceHeight,
|
2193
|
+
target, targetX, targetY);
|
2194
|
+
}
|
2195
|
+
|
2196
|
+
|
2197
|
+
@Override
|
2198
|
+
public void set(int x, int y, int argb) {
|
2199
|
+
loadPixels();
|
2200
|
+
super.set(x, y, argb);
|
2201
|
+
}
|
2202
|
+
|
2203
|
+
|
2204
|
+
@Override
|
2205
|
+
protected void setImpl(PImage sourceImage,
|
2206
|
+
int sourceX, int sourceY,
|
2207
|
+
int sourceWidth, int sourceHeight,
|
2208
|
+
int targetX, int targetY) {
|
2209
|
+
sourceImage.loadPixels();
|
2210
|
+
|
2211
|
+
int sourceOffset = sourceX + sourceImage.pixelWidth * sourceY;
|
2212
|
+
|
2213
|
+
PixelWriter pw = context.getPixelWriter();
|
2214
|
+
pw.setPixels(targetX, targetY, sourceWidth, sourceHeight,
|
2215
|
+
argbFormat,
|
2216
|
+
sourceImage.pixels,
|
2217
|
+
sourceOffset,
|
2218
|
+
sourceImage.pixelWidth);
|
2219
|
+
|
2220
|
+
// Let's keep them loaded
|
2221
|
+
if (loaded) {
|
2222
|
+
int sourceStride = sourceImage.pixelWidth;
|
2223
|
+
int targetStride = pixelWidth;
|
2224
|
+
int targetOffset = targetX + targetY * targetStride;
|
2225
|
+
for (int i = 0; i < sourceHeight; i++) {
|
2226
|
+
System.arraycopy(sourceImage.pixels, sourceOffset + i * sourceStride,
|
2227
|
+
pixels, targetOffset + i * targetStride, sourceWidth);
|
2228
|
+
}
|
2229
|
+
}
|
2230
|
+
}
|
2231
|
+
|
2232
|
+
|
2233
|
+
//////////////////////////////////////////////////////////////
|
2234
|
+
|
2235
|
+
// MASK
|
2236
|
+
|
2237
|
+
|
2238
|
+
static final String MASK_WARNING =
|
2239
|
+
"mask() cannot be used on the main drawing surface";
|
2240
|
+
|
2241
|
+
|
2242
|
+
@Override
|
2243
|
+
public void mask(PImage alpha) {
|
2244
|
+
showWarning(MASK_WARNING);
|
2245
|
+
}
|
2246
|
+
|
2247
|
+
|
2248
|
+
|
2249
|
+
//////////////////////////////////////////////////////////////
|
2250
|
+
|
2251
|
+
// FILTER
|
2252
|
+
|
2253
|
+
// Because the PImage versions call loadPixels() and
|
2254
|
+
// updatePixels(), no need to override anything here.
|
2255
|
+
|
2256
|
+
|
2257
|
+
//public void filter(int kind)
|
2258
|
+
|
2259
|
+
|
2260
|
+
//public void filter(int kind, float param)
|
2261
|
+
|
2262
|
+
|
2263
|
+
|
2264
|
+
//////////////////////////////////////////////////////////////
|
2265
|
+
|
2266
|
+
// COPY
|
2267
|
+
|
2268
|
+
|
2269
|
+
// @Override
|
2270
|
+
// public void copy(int sx, int sy, int sw, int sh,
|
2271
|
+
// int dx, int dy, int dw, int dh) {
|
2272
|
+
// if ((sw != dw) || (sh != dh)) {
|
2273
|
+
// g2.drawImage(image, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
|
2274
|
+
//
|
2275
|
+
// } else {
|
2276
|
+
// dx = dx - sx; // java2d's "dx" is the delta, not dest
|
2277
|
+
// dy = dy - sy;
|
2278
|
+
// g2.copyArea(sx, sy, sw, sh, dx, dy);
|
2279
|
+
// }
|
2280
|
+
// }
|
2281
|
+
|
2282
|
+
|
2283
|
+
// @Override
|
2284
|
+
// public void copy(PImage src,
|
2285
|
+
// int sx, int sy, int sw, int sh,
|
2286
|
+
// int dx, int dy, int dw, int dh) {
|
2287
|
+
// g2.drawImage((Image) src.getNative(),
|
2288
|
+
// dx, dy, dx + dw, dy + dh,
|
2289
|
+
// sx, sy, sx + sw, sy + sh, null);
|
2290
|
+
// }
|
2291
|
+
|
2292
|
+
|
2293
|
+
|
2294
|
+
//////////////////////////////////////////////////////////////
|
2295
|
+
|
2296
|
+
// BLEND
|
2297
|
+
|
2298
|
+
|
2299
|
+
//static public int blendColor(int c1, int c2, int mode)
|
2300
|
+
|
2301
|
+
|
2302
|
+
//public void blend(int sx, int sy, int sw, int sh,
|
2303
|
+
// int dx, int dy, int dw, int dh, int mode)
|
2304
|
+
|
2305
|
+
|
2306
|
+
//public void blend(PImage src,
|
2307
|
+
// int sx, int sy, int sw, int sh,
|
2308
|
+
// int dx, int dy, int dw, int dh, int mode)
|
2309
|
+
|
2310
|
+
|
2311
|
+
|
2312
|
+
//////////////////////////////////////////////////////////////
|
2313
|
+
|
2314
|
+
// SAVE
|
2315
|
+
|
2316
|
+
|
2317
|
+
//public void save(String filename)
|
2318
|
+
|
2319
|
+
|
2320
|
+
|
2321
|
+
//////////////////////////////////////////////////////////////
|
2322
|
+
|
2323
|
+
/**
|
2324
|
+
* Display a warning that the specified method is simply unavailable.
|
2325
|
+
*/
|
2326
|
+
static public void showTodoWarning(String method, int issue) {
|
2327
|
+
showWarning(method + "() is not yet available: " +
|
2328
|
+
"https://github.com/processing/processing/issues/" + issue);
|
2329
|
+
}
|
2330
|
+
}
|