1
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
4
Part of the Processing project - http://processing.org
6
Copyright (c) 2006-08 Ben Fry and Casey Reas
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; either
11
version 2.1 of the License, or (at your option) any later version.
13
This library 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 GNU
16
Lesser General Public License for more details.
18
You should have received a copy of the GNU Lesser General
19
Public License along with this library; if not, write to the
20
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
21
Boston, MA 02111-1307 USA
24
package processing.core;
26
import java.awt.Toolkit;
27
import java.awt.image.DirectColorModel;
28
import java.awt.image.MemoryImageSource;
29
import java.util.Arrays;
33
* Subclass of PGraphics that handles fast 2D rendering using a
34
* MemoryImageSource. The renderer found in this class is not as accurate as
35
* PGraphicsJava2D, but offers certain speed tradeoffs, particular when
36
* messing with the pixels array, or displaying image or video data.
38
public class PGraphics2D extends PGraphics {
40
PMatrix2D ctm = new PMatrix2D();
42
//PPolygon polygon; // general polygon to use for shape
43
PPolygon fpolygon; // used to fill polys for tri or quad strips
44
PPolygon spolygon; // stroke/line polygon
45
float svertices[][]; // temp vertices used for stroking end of poly
52
float[][] matrixStack = new float[MATRIX_STACK_DEPTH][6];
56
MemoryImageSource mis;
59
//////////////////////////////////////////////////////////////
62
public PGraphics2D() { }
65
//public void setParent(PApplet parent)
68
//public void setPrimary(boolean primary)
71
//public void setPath(String path)
74
//public void setSize(int iwidth, int iheight)
77
protected void allocate() {
78
pixelCount = width * height;
79
pixels = new int[pixelCount];
82
cm = new DirectColorModel(32, 0x00ff0000, 0x0000ff00, 0x000000ff);;
83
mis = new MemoryImageSource(width, height, pixels, 0, width);
84
mis.setFullBufferUpdates(true);
85
mis.setAnimated(true);
86
image = Toolkit.getDefaultToolkit().createImage(mis);
91
//public void dispose()
95
//////////////////////////////////////////////////////////////
98
public boolean canDraw() {
103
public void beginDraw() {
104
// need to call defaults(), but can only be done when it's ok to draw
105
// (i.e. for OpenGL, no drawing can be done outside beginDraw/endDraw).
106
if (!settingsInited) {
109
// polygon = new PPolygon(this);
110
fpolygon = new PPolygon(this);
111
spolygon = new PPolygon(this);
112
spolygon.vertexCount = 4;
113
svertices = new float[2][];
116
resetMatrix(); // reset model matrix
123
public void endDraw() {
125
mis.newPixels(pixels, cm, 0, width);
127
// mark pixels as having been updated, so that they'll work properly
128
// when this PGraphics is drawn using image().
133
// public void flush()
137
//////////////////////////////////////////////////////////////
140
//protected void checkSettings()
143
//protected void defaultSettings()
146
//protected void reapplySettings()
150
//////////////////////////////////////////////////////////////
153
//public void hint(int which)
157
//////////////////////////////////////////////////////////////
160
//public void beginShape()
163
public void beginShape(int kind) {
166
curveVertexCount = 0;
173
// polygon.interpUV = false;
177
//public void edge(boolean e)
180
//public void normal(float nx, float ny, float nz)
183
//public void textureMode(int mode)
186
//public void texture(PImage image)
190
public void vertex(float x, float y) {
191
if (shape == POINTS) {
200
public void vertex(float x, float y, float z) {
201
showDepthWarningXYZ("vertex");
205
//public void vertex(float x, float y, float u, float v)
208
public void vertex(float x, float y, float z, float u, float v) {
209
showDepthWarningXYZ("vertex");
213
//protected void vertexTexture(float u, float v);
216
public void breakShape() {
217
showWarning("This renderer cannot handle concave shapes " +
218
"or shapes with holes.");
222
//public void endShape()
225
public void endShape(int mode) {
226
if (ctm.isIdentity()) {
227
for (int i = 0; i < vertexCount; i++) {
228
vertices[i][TX] = vertices[i][X];
229
vertices[i][TY] = vertices[i][Y];
232
for (int i = 0; i < vertexCount; i++) {
233
vertices[i][TX] = ctm.multX(vertices[i][X], vertices[i][Y]);
234
vertices[i][TY] = ctm.multY(vertices[i][X], vertices[i][Y]);
238
// ------------------------------------------------------------------
241
fpolygon.texture(textureImage);
243
// ------------------------------------------------------------------
245
// calculate RGB for each vertex
247
spolygon.interpARGB = true; //strokeChanged; //false;
248
fpolygon.interpARGB = true; //fillChanged; //false;
250
// all the values for r, g, b have been set with calls to vertex()
251
// (no need to re-calculate anything here)
253
// ------------------------------------------------------------------
260
// stroke cannot change inside beginShape(POINTS);
262
if ((ctm.m00 == ctm.m11) && (strokeWeight == 1)) {
263
for (int i = 0; i < vertexCount; i++) {
264
thin_point(vertices[i][TX], vertices[i][TY], strokeColor);
267
for (int i = 0; i < vertexCount; i++) {
268
float[] v = vertices[i];
269
thick_point(v[TX], v[TY], v[TZ], v[SR], v[SG], v[SB], v[SA]);
277
// increment by two for individual lines
278
increment = (shape == LINES) ? 2 : 1;
279
draw_lines(vertices, vertexCount-1, 1, increment, 0);
284
// do fill and stroke separately because otherwise
285
// the lines will be stroked more than necessary
286
if (fill || textureImage != null) {
287
fpolygon.vertexCount = 3;
289
for (int i = 1; i < vertexCount-1; i++) {
290
// System.out.println(i + " of " + vertexCount);
292
fpolygon.vertices[2][R] = vertices[0][R];
293
fpolygon.vertices[2][G] = vertices[0][G];
294
fpolygon.vertices[2][B] = vertices[0][B];
295
fpolygon.vertices[2][A] = vertices[0][A];
297
fpolygon.vertices[2][TX] = vertices[0][TX];
298
fpolygon.vertices[2][TY] = vertices[0][TY];
300
if (textureImage != null) {
301
fpolygon.vertices[2][U] = vertices[0][U];
302
fpolygon.vertices[2][V] = vertices[0][V];
304
// System.out.println(fpolygon.vertices[2][TX] + " " + fpolygon.vertices[2][TY]);
306
for (int j = 0; j < 2; j++) {
307
fpolygon.vertices[j][R] = vertices[i+j][R];
308
fpolygon.vertices[j][G] = vertices[i+j][G];
309
fpolygon.vertices[j][B] = vertices[i+j][B];
310
fpolygon.vertices[j][A] = vertices[i+j][A];
312
fpolygon.vertices[j][TX] = vertices[i+j][TX];
313
fpolygon.vertices[j][TY] = vertices[i+j][TY];
315
// System.out.println(fpolygon.vertices[j][TX] + " " + fpolygon.vertices[j][TY]);
317
if (textureImage != null) {
318
fpolygon.vertices[j][U] = vertices[i+j][U];
319
fpolygon.vertices[j][V] = vertices[i+j][V];
322
// System.out.println();
327
// draw internal lines
328
for (int i = 1; i < vertexCount; i++) {
329
draw_line(vertices[0], vertices[i]);
331
// draw a ring around the outside
332
for (int i = 1; i < vertexCount-1; i++) {
333
draw_line(vertices[i], vertices[i+1]);
336
draw_line(vertices[vertexCount-1], vertices[1]);
342
increment = (shape == TRIANGLES) ? 3 : 1;
343
// do fill and stroke separately because otherwise
344
// the lines will be stroked more than necessary
345
if (fill || textureImage != null) {
346
fpolygon.vertexCount = 3;
347
for (int i = 0; i < vertexCount-2; i += increment) {
348
for (int j = 0; j < 3; j++) {
349
fpolygon.vertices[j][R] = vertices[i+j][R];
350
fpolygon.vertices[j][G] = vertices[i+j][G];
351
fpolygon.vertices[j][B] = vertices[i+j][B];
352
fpolygon.vertices[j][A] = vertices[i+j][A];
354
fpolygon.vertices[j][TX] = vertices[i+j][TX];
355
fpolygon.vertices[j][TY] = vertices[i+j][TY];
356
fpolygon.vertices[j][TZ] = vertices[i+j][TZ];
358
if (textureImage != null) {
359
fpolygon.vertices[j][U] = vertices[i+j][U];
360
fpolygon.vertices[j][V] = vertices[i+j][V];
367
// first draw all vertices as a line strip
368
if (shape == TRIANGLE_STRIP) {
369
draw_lines(vertices, vertexCount-1, 1, 1, 0);
371
draw_lines(vertices, vertexCount-1, 1, 1, 3);
373
// then draw from vertex (n) to (n+2)
374
// incrementing n using the same as above
375
draw_lines(vertices, vertexCount-2, 2, increment, 0);
376
// changed this to vertexCount-2, because it seemed
377
// to be adding an extra (nonexistant) line
382
if (fill || textureImage != null) {
383
fpolygon.vertexCount = 4;
384
for (int i = 0; i < vertexCount-3; i += 4) {
385
for (int j = 0; j < 4; j++) {
387
fpolygon.vertices[j][R] = vertices[jj][R];
388
fpolygon.vertices[j][G] = vertices[jj][G];
389
fpolygon.vertices[j][B] = vertices[jj][B];
390
fpolygon.vertices[j][A] = vertices[jj][A];
392
fpolygon.vertices[j][TX] = vertices[jj][TX];
393
fpolygon.vertices[j][TY] = vertices[jj][TY];
394
fpolygon.vertices[j][TZ] = vertices[jj][TZ];
396
if (textureImage != null) {
397
fpolygon.vertices[j][U] = vertices[jj][U];
398
fpolygon.vertices[j][V] = vertices[jj][V];
405
for (int i = 0; i < vertexCount-3; i += 4) {
406
draw_line(vertices[i+0], vertices[i+1]);
407
draw_line(vertices[i+1], vertices[i+2]);
408
draw_line(vertices[i+2], vertices[i+3]);
409
draw_line(vertices[i+3], vertices[i+0]);
415
if (fill || textureImage != null) {
416
fpolygon.vertexCount = 4;
417
for (int i = 0; i < vertexCount-3; i += 2) {
418
for (int j = 0; j < 4; j++) {
420
if (j == 2) jj = i+3; // swap 2nd and 3rd vertex
421
if (j == 3) jj = i+2;
423
fpolygon.vertices[j][R] = vertices[jj][R];
424
fpolygon.vertices[j][G] = vertices[jj][G];
425
fpolygon.vertices[j][B] = vertices[jj][B];
426
fpolygon.vertices[j][A] = vertices[jj][A];
428
fpolygon.vertices[j][TX] = vertices[jj][TX];
429
fpolygon.vertices[j][TY] = vertices[jj][TY];
430
fpolygon.vertices[j][TZ] = vertices[jj][TZ];
432
if (textureImage != null) {
433
fpolygon.vertices[j][U] = vertices[jj][U];
434
fpolygon.vertices[j][V] = vertices[jj][V];
441
draw_lines(vertices, vertexCount-1, 1, 2, 0); // inner lines
442
draw_lines(vertices, vertexCount-2, 2, 1, 0); // outer lines
448
if (fill || textureImage != null) {
449
//System.out.println("convex");
450
fpolygon.renderPolygon(vertices, vertexCount);
451
//if (stroke) polygon.unexpand();
455
draw_lines(vertices, vertexCount-1, 1, 1, 0);
457
// draw the last line connecting back to the first point in poly
458
//svertices[0] = vertices[vertexCount-1];
459
//svertices[1] = vertices[0];
460
//draw_lines(svertices, 1, 1, 1, 0);
461
draw_line(vertices[vertexCount-1], vertices[0]);
464
} else { // not convex
465
//System.out.println("concave");
466
if (fill || textureImage != null) {
467
// the triangulator produces polygons that don't align
468
// when smoothing is enabled. but if there is a stroke around
469
// the polygon, then smoothing can be temporarily disabled.
470
boolean smoov = smooth;
471
//if (stroke && !hints[DISABLE_SMOOTH_HACK]) smooth = false;
472
if (stroke) smooth = false;
474
//if (stroke && !hints[DISABLE_SMOOTH_HACK]) smooth = smoov;
475
if (stroke) smooth = smoov;
479
draw_lines(vertices, vertexCount-1, 1, 1, 0);
481
// draw the last line connecting back
482
// to the first point in poly
483
// svertices[0] = vertices[vertexCount-1];
484
// svertices[1] = vertices[0];
485
// draw_lines(svertices, 1, 1, 1, 0);
486
draw_line(vertices[vertexCount-1], vertices[0]);
493
// to signify no shape being drawn
499
//////////////////////////////////////////////////////////////
501
// CONCAVE/CONVEX POLYGONS
504
private boolean isConvex() {
505
//float v[][] = polygon.vertices;
506
//int n = polygon.vertexCount;
508
//float tol = 0.001f;
510
if (vertexCount < 3) {
511
// ERROR: this is a line or a point, render as convex
516
// iterate along border doing dot product.
517
// if the sign of the result changes, then is concave
518
for (int i = 0; i < vertexCount; i++) {
519
float[] vi = vertices[i];
520
float[] vj = vertices[(i + 1) % vertexCount];
521
float[] vk = vertices[(i + 2) % vertexCount];
522
float calc = ((vj[TX] - vi[TX]) * (vk[TY] - vj[TY]) -
523
(vj[TY] - vi[TY]) * (vk[TX] - vj[TX]));
526
} else if (calc > 0) {
530
return false; // CONCAVE
534
return true; // CONVEX
536
// ERROR: colinear points, self intersection
544
* Triangulate the current polygon.
546
* Simple ear clipping polygon triangulation adapted from code by
547
* John W. Ratcliff (jratcliff at verant.com). Presumably
548
* <A HREF="http://www.flipcode.org/cgi-bin/fcarticles.cgi?show=63943">this</A>
549
* bit of code from the web.
551
protected void concaveRender() {
552
if (vertexOrder == null || vertexOrder.length != vertices.length) {
553
vertexOrder = new int[vertices.length];
554
// int[] temp = new int[vertices.length];
555
// // since vertex_start may not be zero, might need to keep old stuff around
556
// PApplet.arrayCopy(vertexOrder, temp, vertexCount);
557
// vertexOrder = temp;
560
if (tpolygon == null) {
561
tpolygon = new PPolygon(this);
565
// first we check if the polygon goes clockwise or counterclockwise
567
for (int p = vertexCount - 1, q = 0; q < vertexCount; p = q++) {
568
area += (vertices[q][X] * vertices[p][Y] -
569
vertices[p][X] * vertices[q][Y]);
571
// ain't nuthin there
572
if (area == 0) return;
574
// don't allow polygons to come back and meet themselves,
575
// otherwise it will anger the triangulator
576
// http://dev.processing.org/bugs/show_bug.cgi?id=97
577
float vfirst[] = vertices[0];
578
float vlast[] = vertices[vertexCount-1];
579
if ((Math.abs(vfirst[X] - vlast[X]) < EPSILON) &&
580
(Math.abs(vfirst[Y] - vlast[Y]) < EPSILON) &&
581
(Math.abs(vfirst[Z] - vlast[Z]) < EPSILON)) {
585
// then sort the vertices so they are always in a counterclockwise order
586
for (int i = 0; i < vertexCount; i++) {
587
vertexOrder[i] = (area > 0) ? i : (vertexCount-1 - i);
590
// remove vc-2 Vertices, creating 1 triangle every time
591
int vc = vertexCount; // vc will be decremented while working
592
int count = 2*vc; // complex polygon detection
594
for (int m = 0, v = vc - 1; vc > 2; ) {
597
// if we start over again, is a complex polygon
598
if (0 >= (count--)) {
599
break; // triangulation failed
602
// get 3 consecutive vertices <u,v,w>
603
int u = v ; if (vc <= u) u = 0; // previous
604
v = u + 1; if (vc <= v) v = 0; // current
605
int w = v + 1; if (vc <= w) w = 0; // next
607
// Upgrade values to doubles, and multiply by 10 so that we can have
608
// some better accuracy as we tessellate. This seems to have negligible
609
// speed differences on Windows and Intel Macs, but causes a 50% speed
610
// drop for PPC Macs with the bug's example code that draws ~200 points
611
// in a concave polygon. Apple has abandoned PPC so we may as well too.
612
// http://dev.processing.org/bugs/show_bug.cgi?id=774
615
double Ax = -10 * vertices[vertexOrder[u]][X];
616
double Ay = 10 * vertices[vertexOrder[u]][Y];
617
double Bx = -10 * vertices[vertexOrder[v]][X];
618
double By = 10 * vertices[vertexOrder[v]][Y];
619
double Cx = -10 * vertices[vertexOrder[w]][X];
620
double Cy = 10 * vertices[vertexOrder[w]][Y];
622
// first we check if <u,v,w> continues going ccw
623
if (EPSILON > (((Bx-Ax) * (Cy-Ay)) - ((By-Ay) * (Cx-Ax)))) {
627
for (int p = 0; p < vc; p++) {
628
if ((p == u) || (p == v) || (p == w)) {
632
double Px = -10 * vertices[vertexOrder[p]][X];
633
double Py = 10 * vertices[vertexOrder[p]][Y];
635
double ax = Cx - Bx; double ay = Cy - By;
636
double bx = Ax - Cx; double by = Ay - Cy;
637
double cx = Bx - Ax; double cy = By - Ay;
638
double apx = Px - Ax; double apy = Py - Ay;
639
double bpx = Px - Bx; double bpy = Py - By;
640
double cpx = Px - Cx; double cpy = Py - Cy;
642
double aCROSSbp = ax * bpy - ay * bpx;
643
double cCROSSap = cx * apy - cy * apx;
644
double bCROSScp = bx * cpy - by * cpx;
646
if ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)) {
652
tpolygon.renderTriangle(vertices[vertexOrder[u]],
653
vertices[vertexOrder[v]],
654
vertices[vertexOrder[w]]);
657
// remove v from remaining polygon
658
for (int s = v, t = v + 1; t < vc; s++, t++) {
659
vertexOrder[s] = vertexOrder[t];
663
// reset error detection counter
671
// triangulate the current polygon
672
private void concaveRender() {
673
float polyVertices[][] = polygon.vertices;
675
if (tpolygon == null) {
676
// allocate on first use, rather than slowing
677
// the startup of the class.
678
tpolygon = new PPolygon(this);
679
tpolygon_vertex_order = new int[TPOLYGON_MAX_VERTICES];
683
// copy render parameters
685
if (textureImage != null) {
686
tpolygon.texture(textureImage); //polygon.timage);
689
tpolygon.interpX = polygon.interpX;
690
tpolygon.interpUV = polygon.interpUV;
691
tpolygon.interpARGB = polygon.interpARGB;
693
// simple ear clipping polygon triangulation
694
// addapted from code by john w. ratcliff (jratcliff@verant.com)
696
// 1 - first we check if the polygon goes CW or CCW
697
// CW-CCW ordering adapted from code by
698
// Joseph O'Rourke orourke@cs.smith.edu
699
// 1A - we start by finding the lowest-right most vertex
701
boolean ccw = false; // clockwise
703
int n = polygon.vertexCount;
704
int mm; // postion for LR vertex
705
//float min[] = new float[2];
706
float minX = polyVertices[0][TX];
707
float minY = polyVertices[0][TY];
710
for(int i = 0; i < n; i++ ) {
711
if ((polyVertices[i][TY] < minY) ||
712
((polyVertices[i][TY] == minY) && (polyVertices[i][TX] > minX) )
715
minX = polyVertices[mm][TX];
716
minY = polyVertices[mm][TY];
720
// 1B - now we compute the cross product of the edges of this vertex
725
float a[] = new float[2];
726
float b[] = new float[2];
727
float c[] = new float[2];
729
mm1 = (mm + (n-1)) % n;
731
// assign a[0] to point to poly[m1][0] etc.
732
for(int i = 0; i < 2; i++ ) {
733
a[i] = polyVertices[mm1][i];
734
b[i] = polyVertices[mm][i];
735
c[i] = polyVertices[(mm+1)%n][i];
738
cp = a[0] * b[1] - a[1] * b[0] +
739
a[1] * c[0] - a[0] * c[1] +
740
b[0] * c[1] - c[0] * b[1];
747
// 1C - then we sort the vertices so they
748
// are always in a counterclockwise order
751
// keep the same order
752
for (int i = 0; i < n; i++) {
753
tpolygon_vertex_order[i] = i;
758
for (int i = 0; i < n; i++) {
759
tpolygon_vertex_order[i] = (n - 1) - i;
763
// 2 - begin triangulation
764
// resulting triangles are stored in the triangle array
765
// remove vc-2 Vertices, creating 1 triangle every time
767
int count = 2*vc; // complex polygon detection
769
for (int m = 0, v = vc - 1; vc > 2; ) {
772
// if we start over again, is a complex polygon
773
if (0 >= (count--)) {
774
break; // triangulation failed
777
// get 3 consecutive vertices <u,v,w>
778
int u = v ; if (vc <= u) u = 0; // previous
779
v = u+1; if (vc <= v) v = 0; // current
780
int w = v+1; if (vc <= w) w = 0; // next
783
float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
785
Ax = -polyVertices[tpolygon_vertex_order[u]][TX];
786
Ay = polyVertices[tpolygon_vertex_order[u]][TY];
787
Bx = -polyVertices[tpolygon_vertex_order[v]][TX];
788
By = polyVertices[tpolygon_vertex_order[v]][TY];
789
Cx = -polyVertices[tpolygon_vertex_order[w]][TX];
790
Cy = polyVertices[tpolygon_vertex_order[w]][TY];
792
if ( EPSILON > (((Bx-Ax) * (Cy-Ay)) - ((By-Ay) * (Cx-Ax)))) {
796
for (int p = 0; p < vc; p++) {
798
// this part is a bit osbscure, basically what it does
799
// is test if this tree vertices are and ear or not, looking for
800
// intersections with the remaining vertices using a cross product
801
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
802
float cCROSSap, bCROSScp, aCROSSbp;
804
if( (p == u) || (p == v) || (p == w) ) {
808
Px = -polyVertices[tpolygon_vertex_order[p]][TX];
809
Py = polyVertices[tpolygon_vertex_order[p]][TY];
811
ax = Cx - Bx; ay = Cy - By;
812
bx = Ax - Cx; by = Ay - Cy;
813
cx = Bx - Ax; cy = By - Ay;
814
apx= Px - Ax; apy= Py - Ay;
815
bpx= Px - Bx; bpy= Py - By;
816
cpx= Px - Cx; cpy= Py - Cy;
818
aCROSSbp = ax * bpy - ay * bpx;
819
cCROSSap = cx * apy - cy * apx;
820
bCROSScp = bx * cpy - by * cpx;
822
if ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)) {
828
// yes, the trio is an ear, render it and cut it
830
int triangle_vertices[] = new int[3];
833
// true names of the vertices
834
triangle_vertices[0] = tpolygon_vertex_order[u];
835
triangle_vertices[1] = tpolygon_vertex_order[v];
836
triangle_vertices[2] = tpolygon_vertex_order[w];
839
//render_triangle(triangle_vertices);
840
//private final void render_triangle(int[] triangle_vertices) {
841
// copy all fields of the triangle vertices
842
for (int i = 0; i < 3; i++) {
843
float[] src = polygon.vertices[triangle_vertices[i]];
844
float[] dest = tpolygon.vertices[i];
845
for (int k = 0; k < VERTEX_FIELD_COUNT; k++) {
855
// remove v from remaining polygon
856
for( s = v, t = v + 1; t < vc; s++, t++) {
857
tpolygon_vertex_order[s] = tpolygon_vertex_order[t];
862
// resest error detection counter
871
//////////////////////////////////////////////////////////////
876
//public void bezierVertex(float x2, float y2,
877
// float x3, float y3,
878
// float x4, float y4)
881
//public void bezierVertex(float x2, float y2, float z2,
882
// float x3, float y3, float z3,
883
// float x4, float y4, float z4)
887
//////////////////////////////////////////////////////////////
892
//public void curveVertex(float x, float y)
895
//public void curveVertex(float x, float y, float z)
899
//////////////////////////////////////////////////////////////
904
//public void flush()
908
//////////////////////////////////////////////////////////////
913
//public void point(float x, float y)
916
public void point(float x, float y, float z) {
917
showDepthWarningXYZ("point");
921
//public void line(float x1, float y1, float x2, float y2)
924
//public void line(float x1, float y1, float z1,
925
// float x2, float y2, float z2)
928
//public void triangle(float x1, float y1,
929
// float x2, float y2,
930
// float x3, float y3)
933
//public void quad(float x1, float y1, float x2, float y2,
934
// float x3, float y3, float x4, float y4)
938
//////////////////////////////////////////////////////////////
943
protected void rectImpl(float x1f, float y1f, float x2f, float y2f) {
944
if (smooth || strokeAlpha || ctm.isWarped()) {
945
// screw the efficiency, send this off to beginShape().
946
super.rectImpl(x1f, y1f, x2f, y2f);
949
int x1 = (int) (x1f + ctm.m02);
950
int y1 = (int) (y1f + ctm.m12);
951
int x2 = (int) (x2f + ctm.m02);
952
int y2 = (int) (y2f + ctm.m12);
955
simple_rect_fill(x1, y1, x2, y2);
959
if (strokeWeight == 1) {
960
thin_flat_line(x1, y1, x2, y1);
961
thin_flat_line(x2, y1, x2, y2);
962
thin_flat_line(x2, y2, x1, y2);
963
thin_flat_line(x1, y2, x1, y1);
966
thick_flat_line(x1, y1, strokeR, strokeG, strokeB, strokeA,
967
x2, y1, strokeR, strokeG, strokeB, strokeA);
968
thick_flat_line(x2, y1, strokeR, strokeG, strokeB, strokeA,
969
x2, y2, strokeR, strokeG, strokeB, strokeA);
970
thick_flat_line(x2, y2, strokeR, strokeG, strokeB, strokeA,
971
x1, y2, strokeR, strokeG, strokeB, strokeA);
972
thick_flat_line(x1, y2, strokeR, strokeG, strokeB, strokeA,
973
x1, y1, strokeR, strokeG, strokeB, strokeA);
981
* Draw a rectangle that hasn't been warped by the CTM (though it may be
982
* translated). Just fill a bunch of pixels, or blend them if there's alpha.
984
private void simple_rect_fill(int x1, int y1, int x2, int y2) {
986
int temp = y1; y1 = y2; y2 = temp;
989
int temp = x1; x1 = x2; x2 = temp;
991
// check to see if completely off-screen (e.g. if the left edge of the
992
// rectangle is bigger than the width, and so on.)
993
if ((x1 > width1) || (x2 < 0) ||
994
(y1 > height1) || (y2 < 0)) return;
996
// these only affect the fill, not the stroke
997
// (otherwise strange boogers at edges b/c frame changes shape)
999
if (x2 > width) x2 = width;
1001
if (y2 > height) y2 = height;
1006
for (int y = y1; y < y2; y++) {
1007
int index = y*width + x1;
1008
for (int x = 0; x < ww; x++) {
1009
pixels[index] = blend_fill(pixels[index]);
1015
// on avg. 20-25% faster fill routine using System.arraycopy() [toxi 031223]
1016
// removed temporary row[] array for (hopefully) better performance [fry 081117]
1018
// int[] row = new int[ww];
1019
// for (int i = 0; i < ww; i++) row[i] = fillColor;
1020
int index = y1 * width + x1;
1021
int rowIndex = index;
1022
for (int i = 0; i < ww; i++) {
1023
pixels[index + i] = fillColor;
1025
for (int y = 0; y < hh; y++) {
1026
// System.arraycopy(row, 0, pixels, idx, ww);
1027
System.arraycopy(pixels, rowIndex, pixels, index, ww);
1036
//////////////////////////////////////////////////////////////
1041
protected void ellipseImpl(float x, float y, float w, float h) {
1042
if (smooth || (strokeWeight != 1) ||
1043
fillAlpha || strokeAlpha || ctm.isWarped()) {
1044
// identical to PGraphics version, but uses POLYGON
1045
// for the fill instead of a TRIANGLE_FAN
1046
float radiusH = w / 2;
1047
float radiusV = h / 2;
1049
float centerX = x + radiusH;
1050
float centerY = y + radiusV;
1052
float sx1 = screenX(x, y);
1053
float sy1 = screenY(x, y);
1054
float sx2 = screenX(x+w, y+h);
1055
float sy2 = screenY(x+w, y+h);
1056
int accuracy = (int) (TWO_PI * PApplet.dist(sx1, sy1, sx2, sy2) / 8);
1057
if (accuracy < 4) return; // don't bother?
1058
//System.out.println("diameter " + w + " " + h + " -> " + accuracy);
1060
float inc = (float)SINCOS_LENGTH / accuracy;
1065
boolean savedStroke = stroke;
1069
for (int i = 0; i < accuracy; i++) {
1070
vertex(centerX + cosLUT[(int) val] * radiusH,
1071
centerY + sinLUT[(int) val] * radiusV);
1076
stroke = savedStroke;
1080
boolean savedFill = fill;
1085
for (int i = 0; i < accuracy; i++) {
1086
vertex(centerX + cosLUT[(int) val] * radiusH,
1087
centerY + sinLUT[(int) val] * radiusV);
1095
float hradius = w / 2f;
1096
float vradius = h / 2f;
1098
int centerX = (int) (x + hradius + ctm.m02);
1099
int centerY = (int) (y + vradius + ctm.m12);
1101
int hradiusi = (int) hradius;
1102
int vradiusi = (int) vradius;
1104
if (hradiusi == vradiusi) {
1105
if (fill) flat_circle_fill(centerX, centerY, hradiusi);
1106
if (stroke) flat_circle_stroke(centerX, centerY, hradiusi);
1109
if (fill) flat_ellipse_internal(centerX, centerY, hradiusi, vradiusi, true);
1110
if (stroke) flat_ellipse_internal(centerX, centerY, hradiusi, vradiusi, false);
1117
* Draw the outline around a flat circle using a bresenham-style
1118
* algorithm. Adapted from drawCircle function in "Computer Graphics
1119
* for Java Programmers" by Leen Ammeraal, p. 110.
1121
* This function is included because the quality is so much better,
1122
* and the drawing significantly faster than with adaptive ellipses
1123
* drawn using the sine/cosine tables.
1125
* Circle quadrants break down like so:
1131
* -------------------
1137
* @param xc x center
1138
* @param yc y center
1141
private void flat_circle_stroke(int xC, int yC, int r) {
1142
int x = 0, y = r, u = 1, v = 2 * r - 1, E = 0;
1144
thin_point(xC + x, yC + y, strokeColor); // NNE
1145
thin_point(xC + y, yC - x, strokeColor); // ESE
1146
thin_point(xC - x, yC - y, strokeColor); // SSW
1147
thin_point(xC - y, yC + x, strokeColor); // WNW
1149
x++; E += u; u += 2;
1151
y--; E -= v; v -= 2;
1155
thin_point(xC + y, yC + x, strokeColor); // ENE
1156
thin_point(xC + x, yC - y, strokeColor); // SSE
1157
thin_point(xC - y, yC - x, strokeColor); // WSW
1158
thin_point(xC - x, yC + y, strokeColor); // NNW
1164
* Heavily adapted version of the above algorithm that handles
1165
* filling the ellipse. Works by drawing from the center and
1166
* outwards to the points themselves. Has to be done this way
1167
* because the values for the points are changed halfway through
1168
* the function, making it impossible to just store a series of
1169
* left and right edges to be drawn more quickly.
1171
* @param xc x center
1172
* @param yc y center
1175
private void flat_circle_fill(int xc, int yc, int r) {
1176
int x = 0, y = r, u = 1, v = 2 * r - 1, E = 0;
1178
for (int xx = xc; xx < xc + x; xx++) { // NNE
1179
thin_point(xx, yc + y, fillColor);
1181
for (int xx = xc; xx < xc + y; xx++) { // ESE
1182
thin_point(xx, yc - x, fillColor);
1184
for (int xx = xc - x; xx < xc; xx++) { // SSW
1185
thin_point(xx, yc - y, fillColor);
1187
for (int xx = xc - y; xx < xc; xx++) { // WNW
1188
thin_point(xx, yc + x, fillColor);
1191
x++; E += u; u += 2;
1193
y--; E -= v; v -= 2;
1197
for (int xx = xc; xx < xc + y; xx++) { // ENE
1198
thin_point(xx, yc + x, fillColor);
1200
for (int xx = xc; xx < xc + x; xx++) { // SSE
1201
thin_point(xx, yc - y, fillColor);
1203
for (int xx = xc - y; xx < xc; xx++) { // WSW
1204
thin_point(xx, yc - x, fillColor);
1206
for (int xx = xc - x; xx < xc; xx++) { // NNW
1207
thin_point(xx, yc + y, fillColor);
1213
// unfortunately this can't handle fill and stroke simultaneously,
1214
// because the fill will later replace some of the stroke points
1216
private final void flat_ellipse_symmetry(int centerX, int centerY,
1217
int ellipseX, int ellipseY,
1220
for (int i = centerX - ellipseX + 1; i < centerX + ellipseX; i++) {
1221
thin_point(i, centerY - ellipseY, fillColor);
1222
thin_point(i, centerY + ellipseY, fillColor);
1225
thin_point(centerX - ellipseX, centerY + ellipseY, strokeColor);
1226
thin_point(centerX + ellipseX, centerY + ellipseY, strokeColor);
1227
thin_point(centerX - ellipseX, centerY - ellipseY, strokeColor);
1228
thin_point(centerX + ellipseX, centerY - ellipseY, strokeColor);
1234
* Bresenham-style ellipse drawing function, adapted from a posting to
1235
* comp.graphics.algortihms.
1237
* This function is included because the quality is so much better,
1238
* and the drawing significantly faster than with adaptive ellipses
1239
* drawn using the sine/cosine tables.
1241
* @param centerX x coordinate of the center
1242
* @param centerY y coordinate of the center
1243
* @param a horizontal radius
1244
* @param b vertical radius
1246
private void flat_ellipse_internal(int centerX, int centerY,
1247
int a, int b, boolean filling) {
1248
int x, y, a2, b2, s, t;
1254
s = a2*(1-2*b) + 2*b2;
1255
t = b2 - 2*a2*(2*b-1);
1256
flat_ellipse_symmetry(centerX, centerY, x, y, filling);
1264
s += 2*b2*(2*x+3) - 4*a2*(y-1);
1265
t += 4*b2*(x+1) - 2*a2*(2*y-3);
1273
flat_ellipse_symmetry(centerX, centerY, x, y, filling);
1279
// TODO really need a decent arc function in here..
1281
protected void arcImpl(float x, float y, float w, float h,
1282
float start, float stop) {
1286
float centerX = x + hr;
1287
float centerY = y + vr;
1290
// shut off stroke for a minute
1291
boolean savedStroke = stroke;
1294
int startLUT = (int) (-0.5f + (start / TWO_PI) * SINCOS_LENGTH);
1295
int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
1298
vertex(centerX, centerY);
1299
for (int i = startLUT; i < stopLUT; i++) {
1300
int ii = i % SINCOS_LENGTH;
1301
// modulo won't make the value positive
1302
if (ii < 0) ii += SINCOS_LENGTH;
1303
vertex(centerX + cosLUT[ii] * hr,
1304
centerY + sinLUT[ii] * vr);
1308
stroke = savedStroke;
1312
// Almost identical to above, but this uses a LINE_STRIP
1313
// and doesn't include the first (center) vertex.
1315
boolean savedFill = fill;
1318
int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
1319
int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
1321
beginShape(); //LINE_STRIP);
1322
int increment = 1; // what's a good algorithm? stopLUT - startLUT;
1323
for (int i = startLUT; i < stopLUT; i += increment) {
1324
int ii = i % SINCOS_LENGTH;
1325
if (ii < 0) ii += SINCOS_LENGTH;
1326
vertex(centerX + cosLUT[ii] * hr,
1327
centerY + sinLUT[ii] * vr);
1329
// draw last point explicitly for accuracy
1330
vertex(centerX + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
1331
centerY + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
1340
//////////////////////////////////////////////////////////////
1345
public void box(float size) {
1346
showDepthWarning("box");
1349
public void box(float w, float h, float d) {
1350
showDepthWarning("box");
1355
//////////////////////////////////////////////////////////////
1360
public void sphereDetail(int res) {
1361
showDepthWarning("sphereDetail");
1364
public void sphereDetail(int ures, int vres) {
1365
showDepthWarning("sphereDetail");
1368
public void sphere(float r) {
1369
showDepthWarning("sphere");
1374
//////////////////////////////////////////////////////////////
1379
public void bezier(float x1, float y1, float z1,
1380
float x2, float y2, float z2,
1381
float x3, float y3, float z3,
1382
float x4, float y4, float z4) {
1383
showDepthWarningXYZ("bezier");
1387
public void curve(float x1, float y1, float z1,
1388
float x2, float y2, float z2,
1389
float x3, float y3, float z3,
1390
float x4, float y4, float z4) {
1391
showDepthWarningXYZ("curve");
1396
//////////////////////////////////////////////////////////////
1401
protected void imageImpl(PImage image,
1402
float x1, float y1, float x2, float y2,
1403
int u1, int v1, int u2, int v2) {
1404
if ((x2 - x1 == image.width) &&
1405
(y2 - y1 == image.height) &&
1406
!tint && !ctm.isWarped()) {
1407
simple_image(image, (int) (x1 + ctm.m02), (int) (y1 + ctm.m12), u1, v1, u2, v2);
1410
super.imageImpl(image, x1, y1, x2, y2, u1, v1, u2, v2);
1416
* Image drawn in flat "screen space", with no scaling or warping.
1417
* this is so common that a special routine is included for it,
1418
* because the alternative is much slower.
1420
* @param image image to be drawn
1421
* @param sx1 x coordinate of upper-lefthand corner in screen space
1422
* @param sy1 y coordinate of upper-lefthand corner in screen space
1424
private void simple_image(PImage image, int sx1, int sy1,
1425
int ix1, int iy1, int ix2, int iy2) {
1426
int sx2 = sx1 + image.width;
1427
int sy2 = sy1 + image.height;
1429
// don't draw if completely offscreen
1430
// (without this check, ArrayIndexOutOfBoundsException)
1431
if ((sx1 > width1) || (sx2 < 0) ||
1432
(sy1 > height1) || (sy2 < 0)) return;
1434
if (sx1 < 0) { // off left edge
1438
if (sy1 < 0) { // off top edge
1442
if (sx2 > width) { // off right edge
1446
if (sy2 > height) { // off bottom edge
1447
iy2 -= sy2 - height;
1451
int source = iy1 * image.width + ix1;
1452
int target = sy1 * width;
1454
if (image.format == ARGB) {
1455
for (int y = sy1; y < sy2; y++) {
1458
for (int x = sx1; x < sx2; x++) {
1459
pixels[target + x] =
1460
// _blend(pixels[target + x],
1461
// image.pixels[source + tx],
1462
// image.pixels[source + tx++] >>> 24);
1463
blend_color(pixels[target + x],
1464
image.pixels[source + tx++]);
1466
source += image.width;
1469
} else if (image.format == ALPHA) {
1470
for (int y = sy1; y < sy2; y++) {
1473
for (int x = sx1; x < sx2; x++) {
1474
pixels[target + x] =
1475
blend_color_alpha(pixels[target + x],
1477
image.pixels[source + tx++]);
1479
source += image.width;
1483
} else if (image.format == RGB) {
1486
for (int y = sy1; y < sy2; y++) {
1487
System.arraycopy(image.pixels, source, pixels, target, tw);
1488
// should set z coordinate in here
1489
// or maybe not, since dims=0, meaning no relevant z
1490
source += image.width;
1497
//////////////////////////////////////////////////////////////
1502
// These will be handled entirely by PGraphics.
1506
//////////////////////////////////////////////////////////////
1508
// UGLY RENDERING SHITE
1511
// expects properly clipped coords, hence does
1512
// NOT check if x/y are in bounds [toxi]
1513
private void thin_point_at(int x, int y, float z, int color) {
1514
int index = y*width+x; // offset values are pre-calced in constructor
1515
pixels[index] = color;
1518
// expects offset/index in pixelbuffer array instead of x/y coords
1519
// used by optimized parts of thin_flat_line() [toxi]
1520
private void thin_point_at_index(int offset, float z, int color) {
1521
pixels[offset] = color;
1525
private void thick_point(float x, float y, float z, // note floats
1526
float r, float g, float b, float a) {
1528
spolygon.interpARGB = false; // no changes for vertices of a point
1530
float strokeWidth2 = strokeWeight/2.0f;
1532
float svertex[] = spolygon.vertices[0];
1533
svertex[TX] = x - strokeWidth2;
1534
svertex[TY] = y - strokeWidth2;
1542
svertex = spolygon.vertices[1];
1543
svertex[TX] = x + strokeWidth2;
1544
svertex[TY] = y - strokeWidth2;
1547
svertex = spolygon.vertices[2];
1548
svertex[TX] = x + strokeWidth2;
1549
svertex[TY] = y + strokeWidth2;
1552
svertex = spolygon.vertices[3];
1553
svertex[TX] = x - strokeWidth2;
1554
svertex[TY] = y + strokeWidth2;
1561
// new bresenham clipping code, as old one was buggy [toxi]
1562
private void thin_flat_line(int x1, int y1, int x2, int y2) {
1563
int nx1,ny1,nx2,ny2;
1565
// get the "dips" for the points to clip
1566
int code1 = thin_flat_line_clip_code(x1, y1);
1567
int code2 = thin_flat_line_clip_code(x2, y2);
1569
if ((code1 & code2)!=0) {
1572
int dip = code1 | code2;
1574
// now calculate the clipped points
1575
float a1 = 0, a2 = 1, a = 0;
1576
for (int i=0;i<4;i++) {
1577
if (((dip>>i)%2)==1) {
1578
a = thin_flat_line_slope((float)x1, (float)y1,
1579
(float)x2, (float)y2, i+1);
1580
if (((code1>>i)%2)==1) {
1581
a1 = (float)Math.max(a, a1);
1583
a2 = (float)Math.min(a, a2);
1589
nx1=(int) (x1+a1*(x2-x1));
1590
ny1=(int) (y1+a1*(y2-y1));
1591
nx2=(int) (x1+a2*(x2-x1));
1592
ny2=(int) (y1+a2*(y2-y1));
1594
// line is fully visible/unclipped
1601
// new "extremely fast" line code
1602
// adapted from http://www.edepot.com/linee.html
1604
boolean yLonger=false;
1605
int shortLen=ny2-ny1;
1606
int longLen=nx2-nx1;
1607
if (Math.abs(shortLen)>Math.abs(longLen)) {
1614
if (longLen==0) decInc=0;
1615
else decInc = (shortLen << 16) / longLen;
1618
// special case: vertical line
1619
if (ny1>ny2) { int ty=ny1; ny1=ny2; ny2=ty; }
1620
int offset=ny1*width+nx1;
1621
for(int j=ny1; j<=ny2; j++) {
1622
thin_point_at_index(offset,0,strokeColor);
1626
} else if (ny1==ny2) {
1627
// special case: horizontal line
1628
if (nx1>nx2) { int tx=nx1; nx1=nx2; nx2=tx; }
1629
int offset=ny1*width+nx1;
1630
for(int j=nx1; j<=nx2; j++) thin_point_at_index(offset++,0,strokeColor);
1632
} else if (yLonger) {
1635
for (int j=0x8000+(nx1<<16);ny1<=longLen;++ny1) {
1636
thin_point_at(j>>16, ny1, 0, strokeColor);
1642
for (int j=0x8000+(nx1<<16);ny1>=longLen;--ny1) {
1643
thin_point_at(j>>16, ny1, 0, strokeColor);
1647
} else if (longLen>0) {
1649
for (int j=0x8000+(ny1<<16);nx1<=longLen;++nx1) {
1650
thin_point_at(nx1, j>>16, 0, strokeColor);
1656
for (int j=0x8000+(ny1<<16);nx1>=longLen;--nx1) {
1657
thin_point_at(nx1, j>>16, 0, strokeColor);
1663
private int thin_flat_line_clip_code(float x, float y) {
1664
return ((y < 0 ? 8 : 0) | (y > height1 ? 4 : 0) |
1665
(x < 0 ? 2 : 0) | (x > width1 ? 1 : 0));
1669
private float thin_flat_line_slope(float x1, float y1,
1670
float x2, float y2, int border) {
1673
return (-y1)/(y2-y1);
1676
return (height1-y1)/(y2-y1);
1679
return (-x1)/(x2-x1);
1682
return (width1-x1)/(x2-x1);
1689
private void thick_flat_line(float ox1, float oy1,
1690
float r1, float g1, float b1, float a1,
1691
float ox2, float oy2,
1692
float r2, float g2, float b2, float a2) {
1693
spolygon.interpARGB = (r1 != r2) || (g1 != g2) || (b1 != b2) || (a1 != a2);
1694
// spolygon.interpZ = false;
1696
float dX = ox2-ox1 + EPSILON;
1697
float dY = oy2-oy1 + EPSILON;
1698
float len = (float) Math.sqrt(dX*dX + dY*dY);
1700
// TODO stroke width should be transformed!
1701
float rh = (strokeWeight / len) / 2;
1703
float dx0 = rh * dY;
1704
float dy0 = rh * dX;
1705
float dx1 = rh * dY;
1706
float dy1 = rh * dX;
1710
float svertex[] = spolygon.vertices[0];
1711
svertex[TX] = ox1+dx0;
1712
svertex[TY] = oy1-dy0;
1718
svertex = spolygon.vertices[1];
1719
svertex[TX] = ox1-dx0;
1720
svertex[TY] = oy1+dy0;
1726
svertex = spolygon.vertices[2];
1727
svertex[TX] = ox2-dx1;
1728
svertex[TY] = oy2+dy1;
1734
svertex = spolygon.vertices[3];
1735
svertex[TX] = ox2+dx1;
1736
svertex[TY] = oy2-dy1;
1746
private void draw_line(float[] v1, float[] v2) {
1747
if (strokeWeight == 1) {
1748
if (line == null) line = new PLine(this);
1751
line.setIntensities(v1[SR], v1[SG], v1[SB], v1[SA],
1752
v2[SR], v2[SG], v2[SB], v2[SA]);
1753
line.setVertices(v1[TX], v1[TY], v1[TZ],
1754
v2[TX], v2[TY], v2[TZ]);
1757
} else { // use old line code for thickness != 1
1758
thick_flat_line(v1[TX], v1[TY], v1[SR], v1[SG], v1[SB], v1[SA],
1759
v2[TX], v2[TY], v2[SR], v2[SG], v2[SB], v2[SA]);
1765
* @param max is what to count to
1766
* @param offset is offset to the 'next' vertex
1767
* @param increment is how much to increment in the loop
1769
private void draw_lines(float vertices[][], int max,
1770
int offset, int increment, int skip) {
1772
if (strokeWeight == 1) {
1773
for (int i = 0; i < max; i += increment) {
1774
if ((skip != 0) && (((i+offset) % skip) == 0)) continue;
1776
float a[] = vertices[i];
1777
float b[] = vertices[i+offset];
1779
if (line == null) line = new PLine(this);
1782
line.setIntensities(a[SR], a[SG], a[SB], a[SA],
1783
b[SR], b[SG], b[SB], b[SA]);
1784
line.setVertices(a[TX], a[TY], a[TZ],
1785
b[TX], b[TY], b[TZ]);
1789
} else { // use old line code for thickness != 1
1790
for (int i = 0; i < max; i += increment) {
1791
if ((skip != 0) && (((i+offset) % skip) == 0)) continue;
1793
float v1[] = vertices[i];
1794
float v2[] = vertices[i+offset];
1795
thick_flat_line(v1[TX], v1[TY], v1[SR], v1[SG], v1[SB], v1[SA],
1796
v2[TX], v2[TY], v2[SR], v2[SG], v2[SB], v2[SA]);
1802
private void thin_point(float fx, float fy, int color) {
1803
int x = (int) (fx + 0.4999f);
1804
int y = (int) (fy + 0.4999f);
1805
if (x < 0 || x > width1 || y < 0 || y > height1) return;
1807
int index = y*width + x;
1808
if ((color & 0xff000000) == 0xff000000) { // opaque
1809
pixels[index] = color;
1811
} else { // transparent
1812
// a1 is how much of the orig pixel
1813
int a2 = (color >> 24) & 0xff;
1816
int p2 = strokeColor;
1817
int p1 = pixels[index];
1819
int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00;
1820
int g = (a1 * ((p1 >> 8) & 0xff) + a2 * ((p2 >> 8) & 0xff)) & 0xff00;
1821
int b = (a1 * ( p1 & 0xff) + a2 * ( p2 & 0xff)) >> 8;
1823
pixels[index] = 0xff000000 | (r << 8) | g | b;
1829
//////////////////////////////////////////////////////////////
1831
// MATRIX TRANSFORMATIONS
1834
public void translate(float tx, float ty) {
1835
ctm.translate(tx, ty);
1839
public void translate(float tx, float ty, float tz) {
1840
showDepthWarningXYZ("translate");
1844
public void rotate(float angle) {
1846
// float c = (float) Math.cos(angle);
1847
// float s = (float) Math.sin(angle);
1848
// applyMatrix(c, -s, 0, s, c, 0);
1852
public void rotateX(float angle) {
1853
showDepthWarning("rotateX");
1856
public void rotateY(float angle) {
1857
showDepthWarning("rotateY");
1861
public void rotateZ(float angle) {
1862
showDepthWarning("rotateZ");
1866
public void rotate(float angle, float vx, float vy, float vz) {
1867
showVariationWarning("rotate(angle, x, y, z)");
1871
public void scale(float s) {
1873
// applyMatrix(s, 0, 0,
1878
public void scale(float sx, float sy) {
1880
// applyMatrix(sx, 0, 0,
1885
public void scale(float x, float y, float z) {
1886
showDepthWarningXYZ("scale");
1891
//////////////////////////////////////////////////////////////
1893
// TRANSFORMATION MATRIX
1896
public void pushMatrix() {
1897
if (matrixStackDepth == MATRIX_STACK_DEPTH) {
1898
throw new RuntimeException(ERROR_PUSHMATRIX_OVERFLOW);
1900
ctm.get(matrixStack[matrixStackDepth]);
1905
public void popMatrix() {
1906
if (matrixStackDepth == 0) {
1907
throw new RuntimeException(ERROR_PUSHMATRIX_UNDERFLOW);
1910
ctm.set(matrixStack[matrixStackDepth]);
1915
* Load identity as the transform/model matrix.
1916
* Same as glLoadIdentity().
1918
public void resetMatrix() {
1920
// m00 = 1; m01 = 0; m02 = 0;
1921
// m10 = 0; m11 = 1; m12 = 0;
1926
* Apply a 3x2 affine transformation matrix.
1928
public void applyMatrix(float n00, float n01, float n02,
1929
float n10, float n11, float n12) {
1930
ctm.apply(n00, n01, n02,
1933
// float r00 = m00*n00 + m01*n10;
1934
// float r01 = m00*n01 + m01*n11;
1935
// float r02 = m00*n02 + m01*n12 + m02;
1937
// float r10 = m10*n00 + m11*n10;
1938
// float r11 = m10*n01 + m11*n11;
1939
// float r12 = m10*n02 + m11*n12 + m12;
1941
// m00 = r00; m01 = r01; m02 = r02;
1942
// m10 = r10; m11 = r11; m12 = r12;
1946
public void applyMatrix(float n00, float n01, float n02, float n03,
1947
float n10, float n11, float n12, float n13,
1948
float n20, float n21, float n22, float n23,
1949
float n30, float n31, float n32, float n33) {
1950
showDepthWarningXYZ("applyMatrix");
1955
* Loads the current matrix into m00, m01 etc (or modelview and
1956
* projection when using 3D) so that the values can be read.
1958
* Note that there is no "updateMatrix" because that gets too
1959
* complicated (unnecessary) when considering the 3D matrices.
1961
// public void loadMatrix() {
1962
// no-op on base PGraphics because they're used directly
1967
* Print the current model (or "transformation") matrix.
1969
public void printMatrix() {
1972
// loadMatrix(); // just to make sure
1974
// float big = Math.abs(m00);
1975
// if (Math.abs(m01) > big) big = Math.abs(m01);
1976
// if (Math.abs(m02) > big) big = Math.abs(m02);
1977
// if (Math.abs(m10) > big) big = Math.abs(m10);
1978
// if (Math.abs(m11) > big) big = Math.abs(m11);
1979
// if (Math.abs(m12) > big) big = Math.abs(m12);
1981
// // avoid infinite loop
1982
// if (Float.isNaN(big) || Float.isInfinite(big)) {
1983
// big = 1000000; // set to something arbitrary
1987
// int bigi = (int) big;
1988
// while ((bigi /= 10) != 0) d++; // cheap log()
1990
// System.out.println(PApplet.nfs(m00, d, 4) + " " +
1991
// PApplet.nfs(m01, d, 4) + " " +
1992
// PApplet.nfs(m02, d, 4));
1994
// System.out.println(PApplet.nfs(m10, d, 4) + " " +
1995
// PApplet.nfs(m11, d, 4) + " " +
1996
// PApplet.nfs(m12, d, 4));
1998
// System.out.println();
2003
//////////////////////////////////////////////////////////////
2005
// SCREEN TRANSFORMS
2008
public float screenX(float x, float y) {
2009
return ctm.m00 * x + ctm.m01 * y + ctm.m02;
2013
public float screenY(float x, float y) {
2014
return ctm.m10 * x + ctm.m11 * y + ctm.m12;
2019
//////////////////////////////////////////////////////////////
2021
// BACKGROUND AND FRIENDS
2025
* Clear the pixel buffer.
2027
protected void backgroundImpl() {
2028
Arrays.fill(pixels, backgroundColor);
2034
public void ambient(int rgb) {
2035
showDepthError("ambient");
2038
public void ambient(float gray) {
2039
showDepthError("ambient");
2042
public void ambient(float x, float y, float z) {
2043
// This doesn't take
2044
if ((x != PMaterial.DEFAULT_AMBIENT) ||
2045
(y != PMaterial.DEFAULT_AMBIENT) ||
2046
(z != PMaterial.DEFAULT_AMBIENT)) {
2047
showDepthError("ambient");
2051
public void specular(int rgb) {
2052
showDepthError("specular");
2055
public void specular(float gray) {
2056
showDepthError("specular");
2059
public void specular(float x, float y, float z) {
2060
showDepthError("specular");
2063
public void shininess(float shine) {
2064
showDepthError("shininess");
2068
public void emissive(int rgb) {
2069
showDepthError("emissive");
2072
public void emissive(float gray) {
2073
showDepthError("emissive");
2076
public void emissive(float x, float y, float z ) {
2077
showDepthError("emissive");
2083
//////////////////////////////////////////////////////////////
2085
// INTERNAL SCHIZZLE
2088
// TODO make this more efficient, or move into PMatrix2D
2089
// private boolean untransformed() {
2090
// return ((ctm.m00 == 1) && (ctm.m01 == 0) && (ctm.m02 == 0) &&
2091
// (ctm.m10 == 0) && (ctm.m11 == 1) && (ctm.m12 == 0));
2095
// // TODO make this more efficient, or move into PMatrix2D
2096
// private boolean unwarped() {
2097
// return ((ctm.m00 == 1) && (ctm.m01 == 0) &&
2098
// (ctm.m10 == 0) && (ctm.m11 == 1));
2102
// only call this if there's an alpha in the fill
2103
private final int blend_fill(int p1) {
2107
int r = (a1 * ((p1 >> 16) & 0xff)) + (a2 * fillRi) & 0xff00;
2108
int g = (a1 * ((p1 >> 8) & 0xff)) + (a2 * fillGi) & 0xff00;
2109
int b = (a1 * ( p1 & 0xff)) + (a2 * fillBi) & 0xff00;
2111
return 0xff000000 | (r << 8) | g | (b >> 8);
2115
private final int blend_color(int p1, int p2) {
2116
int a2 = (p2 >>> 24);
2124
int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00;
2125
int g = (a1 * ((p1 >> 8) & 0xff) + a2 * ((p2 >> 8) & 0xff)) & 0xff00;
2126
int b = (a1 * ( p1 & 0xff) + a2 * ( p2 & 0xff)) >> 8;
2128
return 0xff000000 | (r << 8) | g | b;
2133
private final int blend_color_alpha(int p1, int p2, int a2) {
2134
// scale alpha by alpha of incoming pixel
2135
a2 = (a2 * (p2 >>> 24)) >> 8;
2138
int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00;
2139
int g = (a1 * ((p1 >> 8) & 0xff) + a2 * ((p2 >> 8) & 0xff)) & 0xff00;
2140
int b = (a1 * ( p1 & 0xff) + a2 * ( p2 & 0xff)) >> 8;
2142
return 0xff000000 | (r << 8) | g | b;