propane 2.8.0.pre-java → 2.9.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }