1
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
4
Part of the Processing project - http://processing.org
6
Copyright (c) 2004-08 Ben Fry and Casey Reas
7
Copyright (c) 2001-04 Massachusetts Institute of Technology
9
This library is free software; you can redistribute it and/or
10
modify it under the terms of the GNU Lesser General Public
11
License as published by the Free Software Foundation; either
12
version 2.1 of the License, or (at your option) any later version.
14
This library is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
Lesser General Public License for more details.
19
You should have received a copy of the GNU Lesser General
20
Public License along with this library; if not, write to the
21
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
22
Boston, MA 02111-1307 USA
25
package processing.core;
29
* Z-buffer polygon rendering object used by PGraphics2D.
31
public class PPolygon implements PConstants {
33
static final int DEFAULT_SIZE = 64; // this is needed for spheres
34
float vertices[][] = new float[DEFAULT_SIZE][VERTEX_FIELD_COUNT];
37
float r[] = new float[DEFAULT_SIZE]; // storage used by incrementalize
38
float dr[] = new float[DEFAULT_SIZE];
39
float l[] = new float[DEFAULT_SIZE]; // more storage for incrementalize
40
float dl[] = new float[DEFAULT_SIZE];
41
float sp[] = new float[DEFAULT_SIZE]; // temporary storage for scanline
42
float sdp[] = new float[DEFAULT_SIZE];
44
protected boolean interpX;
45
protected boolean interpUV; // is this necessary? could just check timage != null
46
protected boolean interpARGB;
49
private int r2, g2, b2, a2, a2orig;
53
// the parent's width/height,
54
// or if smooth is enabled, parent's w/h scaled
55
// up by the smooth dimension
62
int theight1, twidth1;
66
static final int SUBXRES = 8;
67
static final int SUBXRES1 = 7;
68
static final int SUBYRES = 8;
69
static final int SUBYRES1 = 7;
70
static final int MAX_COVERAGE = SUBXRES * SUBYRES;
76
int aaleft[] = new int[SUBYRES];
77
int aaright[] = new int[SUBYRES];
78
int aaleftmin, aarightmin;
79
int aaleftmax, aarightmax;
80
int aaleftfull, aarightfull;
82
final private int MODYRES(int y) {
83
return (y & SUBYRES1);
87
public PPolygon(PGraphics iparent) {
93
protected void reset(int count) {
103
protected float[] nextVertex() {
104
if (vertexCount == vertices.length) {
105
float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
106
System.arraycopy(vertices, 0, temp, 0, vertexCount);
109
r = new float[vertices.length];
110
dr = new float[vertices.length];
111
l = new float[vertices.length];
112
dl = new float[vertices.length];
113
sp = new float[vertices.length];
114
sdp = new float[vertices.length];
116
return vertices[vertexCount++]; // returns v[0], sets vc to 1
121
* Return true if this vertex is redundant. If so, will also
122
* decrement the vertex count.
125
public boolean redundantVertex(float x, float y, float z) {
126
// because vertexCount will be 2 when setting vertex[1]
127
if (vertexCount < 2) return false;
129
// vertexCount-1 is the current vertex that would be used
130
// vertexCount-2 would be the previous feller
131
if ((Math.abs(vertices[vertexCount-2][MX] - x) < EPSILON) &&
132
(Math.abs(vertices[vertexCount-2][MY] - y) < EPSILON) &&
133
(Math.abs(vertices[vertexCount-2][MZ] - z) < EPSILON)) {
142
protected void texture(PImage image) {
146
this.tpixels = image.pixels;
147
this.twidth = image.width;
148
this.theight = image.height;
149
this.tformat = image.format;
151
twidth1 = twidth - 1;
152
theight1 = theight - 1;
161
protected void renderPolygon(float[][] v, int count) {
165
if (r.length < vertexCount) {
166
r = new float[vertexCount]; // storage used by incrementalize
167
dr = new float[vertexCount];
168
l = new float[vertexCount]; // more storage for incrementalize
169
dl = new float[vertexCount];
170
sp = new float[vertexCount]; // temporary storage for scanline
171
sdp = new float[vertexCount];
179
protected void renderTriangle(float[] v1, float[] v2, float[] v3) {
180
// Calling code will have already done reset(3).
181
// Can't do it here otherwise would nuke any texture settings.
192
protected void checkExpand() {
194
for (int i = 0; i < vertexCount; i++) {
195
vertices[i][TX] /= SUBXRES;
196
vertices[i][TY] /= SUBYRES;
202
protected void render() {
203
if (vertexCount < 3) return;
205
// these may have changed due to a resize()
206
// so they should be refreshed here
207
pixels = parent.pixels;
208
//zbuffer = parent.zbuffer;
210
// noDepthTest = parent.hints[DISABLE_DEPTH_TEST];
211
smooth = parent.smooth;
213
// by default, text turns on smooth for the textures
214
// themselves. but this should be shut off if the hint
215
// for DISABLE_TEXT_SMOOTH is set.
216
// texture_smooth = true;
218
width = smooth ? parent.width*SUBXRES : parent.width;
219
height = smooth ? parent.height*SUBYRES : parent.height;
222
height1 = height - 1;
225
r2 = (int) (vertices[0][R] * 255);
226
g2 = (int) (vertices[0][G] * 255);
227
b2 = (int) (vertices[0][B] * 255);
228
a2 = (int) (vertices[0][A] * 255);
229
a2orig = a2; // save an extra copy
230
rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
233
for (int i = 0; i < vertexCount; i++) {
234
r[i] = 0; dr[i] = 0; l[i] = 0; dl[i] = 0;
238
// hack to not make polygons fly into the screen
239
if (parent.hints[DISABLE_FLYING_POO]) {
240
float nwidth2 = -width * 2;
241
float nheight2 = -height * 2;
242
float width2 = width * 2;
243
float height2 = height * 2;
244
for (int i = 0; i < vertexCount; i++) {
245
if ((vertices[i][TX] < nwidth2) ||
246
(vertices[i][TX] > width2) ||
247
(vertices[i][TY] < nheight2) ||
248
(vertices[i][TY] > height2)) {
249
return; // this is a bad poly
255
// for (int i = 0; i < 4; i++) {
256
// System.out.println(vertices[i][R] + " " + vertices[i][G] + " " + vertices[i][B]);
258
// System.out.println();
261
for (int i = 0; i < vertexCount; i++) {
262
vertices[i][TX] *= SUBXRES;
263
vertices[i][TY] *= SUBYRES;
268
// find top vertex (y is zero at top, higher downwards)
270
float ymin = vertices[0][TY];
271
float ymax = vertices[0][TY]; // fry 031001
272
for (int i = 1; i < vertexCount; i++) {
273
if (vertices[i][TY] < ymin) {
274
ymin = vertices[i][TY];
277
if (vertices[i][TY] > ymax) {
278
ymax = vertices[i][TY];
282
// the last row is an exceptional case, because there won't
283
// necessarily be 8 rows of subpixel lines that will force
284
// the final line to render. so instead, the algo keeps track
285
// of the lastY (in subpixel resolution) that will be rendered
286
// and that will force a scanline to happen the same as
287
// every eighth in the other situations
288
//lastY = -1; // fry 031001
289
lastY = (int) (ymax - 0.5f); // global to class bc used by other fxns
291
int lefti = topi; // li, index of left vertex
292
int righti = topi; // ri, index of right vertex
293
int y = (int) (ymin + 0.5f); // current scan line
294
int lefty = y - 1; // lower end of left edge
295
int righty = y - 1; // lower end of right edge
299
int remaining = vertexCount;
301
// scan in y, activating new edges on left & right
302
// as scan line passes over new vertices
303
while (remaining > 0) {
304
// advance left edge?
305
while ((lefty <= y) && (remaining > 0)) {
307
// step ccw down left side
308
int i = (lefti != 0) ? (lefti-1) : (vertexCount-1);
309
incrementalizeY(vertices[lefti], vertices[i], l, dl, y);
310
lefty = (int) (vertices[i][TY] + 0.5f);
314
// advance right edge?
315
while ((righty <= y) && (remaining > 0)) {
317
// step cw down right edge
318
int i = (righti != vertexCount-1) ? (righti + 1) : 0;
319
incrementalizeY(vertices[righti], vertices[i], r, dr, y);
320
righty = (int) (vertices[i][TY] + 0.5f);
324
// do scanlines till end of l or r edge
325
while (y < lefty && y < righty) {
326
// this doesn't work because it's not always set here
327
//if (remaining == 0) {
328
//lastY = (lefty < righty) ? lefty-1 : righty-1;
329
//System.out.println("lastY is " + lastY);
332
if ((y >= 0) && (y < height)) {
333
//try { // hopefully this bug is fixed
334
if (l[TX] <= r[TX]) scanline(y, l, r);
335
else scanline(y, r, l);
336
//} catch (ArrayIndexOutOfBoundsException e) {
337
//e.printStackTrace();
341
// this increment probably needs to be different
342
// UV and RGB shouldn't be incremented until line is emitted
348
//System.out.println("y/lasty/lastmody = " + y + " " + lastY + " " + lastModY);
353
private void scanline(int y, float l[], float r[]) {
354
//System.out.println("scanline " + y);
355
for (int i = 0; i < vertexCount; i++) { // should be moved later
356
sp[i] = 0; sdp[i] = 0;
359
// this rounding doesn't seem to be relevant with smooth
360
int lx = (int) (l[TX] + 0.49999f); // ceil(l[TX]-.5);
362
int rx = (int) (r[TX] - 0.5f);
363
if (rx > width1) rx = width1;
368
int mody = MODYRES(y);
373
if (firstModY == -1) {
375
aaleftmin = lx; aaleftmax = lx;
376
aarightmin = rx; aarightmax = rx;
379
if (aaleftmin > aaleft[mody]) aaleftmin = aaleft[mody];
380
if (aaleftmax < aaleft[mody]) aaleftmax = aaleft[mody];
381
if (aarightmin > aaright[mody]) aarightmin = aaright[mody];
382
if (aarightmax < aaright[mody]) aarightmax = aaright[mody];
385
lastModY = mody; // moved up here (before the return) 031001
386
// not the eighth (or lastY) line, so not scanning this time
387
if ((mody != SUBYRES1) && (y != lastY)) return;
388
//lastModY = mody; // eeK! this was missing
392
//System.out.println("y is lasty");
395
aaleftfull = aaleftmax/SUBXRES + 1;
396
aarightfull = aarightmin/SUBXRES - 1;
399
// this is the setup, based on lx
400
incrementalizeX(l, r, sp, sdp, lx);
402
// scan in x, generating pixels
403
// using parent.width to get actual pixel index
404
// rather than scaled by smooth factor
405
int offset = smooth ? parent.width * (y / SUBYRES) : parent.width*y;
407
int truelx = 0, truerx = 0;
409
truelx = lx / SUBXRES;
410
truerx = (rx + SUBXRES1) / SUBXRES;
412
lx = aaleftmin / SUBXRES;
413
rx = (aarightmax + SUBXRES1) / SUBXRES;
415
if (rx > parent.width1) rx = parent.width1;
421
// System.out.println("P2D interp uv " + interpUV + " " +
422
// vertices[2][U] + " " + vertices[2][V]);
423
for (int x = lx; x <= rx; x++) {
424
// map texture based on U, V coords in sp[U] and sp[V]
426
int tu = (int) (sp[U] * twidth);
427
int tv = (int) (sp[V] * theight);
429
if (tu > twidth1) tu = twidth1;
430
if (tv > theight1) tv = theight1;
434
int txy = tv*twidth + tu;
436
int tuf1 = (int) (255f * (sp[U]*twidth - (float)tu));
437
int tvf1 = (int) (255f * (sp[V]*theight - (float)tv));
439
// the closer sp[U or V] is to the decimal being zero
440
// the more coverage it should get of the original pixel
441
int tuf = 255 - tuf1;
442
int tvf = 255 - tvf1;
444
// this code sucks! filled with bugs and slow as hell!
445
int pixel00 = tpixels[txy];
446
int pixel01 = (tv < theight1) ?
447
tpixels[txy + twidth] : tpixels[txy];
448
int pixel10 = (tu < twidth1) ?
449
tpixels[txy + 1] : tpixels[txy];
450
int pixel11 = ((tv < theight1) && (tu < twidth1)) ?
451
tpixels[txy + twidth + 1] : tpixels[txy];
453
int p00, p01, p10, p11;
454
int px0, px1; //, pxy;
457
// calculate alpha component (ta)
459
if (tformat == ALPHA) {
460
px0 = (pixel00*tuf + pixel10*tuf1) >> 8;
461
px1 = (pixel01*tuf + pixel11*tuf1) >> 8;
462
ta = (((px0*tvf + px1*tvf1) >> 8) *
463
(interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
465
} else if (tformat == ARGB) {
466
p00 = (pixel00 >> 24) & 0xff;
467
p01 = (pixel01 >> 24) & 0xff;
468
p10 = (pixel10 >> 24) & 0xff;
469
p11 = (pixel11 >> 24) & 0xff;
471
px0 = (p00*tuf + p10*tuf1) >> 8;
472
px1 = (p01*tuf + p11*tuf1) >> 8;
473
ta = (((px0*tvf + px1*tvf1) >> 8) *
474
(interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
476
} else { // RGB image, no alpha
477
ta = interpARGB ? ((int) (sp[A]*255)) : a2orig;
480
// calculate r,g,b components (tr, tg, tb)
482
if ((tformat == RGB) || (tformat == ARGB)) {
483
p00 = (pixel00 >> 16) & 0xff; // red
484
p01 = (pixel01 >> 16) & 0xff;
485
p10 = (pixel10 >> 16) & 0xff;
486
p11 = (pixel11 >> 16) & 0xff;
488
px0 = (p00*tuf + p10*tuf1) >> 8;
489
px1 = (p01*tuf + p11*tuf1) >> 8;
490
tr = (((px0*tvf + px1*tvf1) >> 8) *
491
(interpARGB ? ((int) (sp[R]*255)) : r2)) >> 8;
493
p00 = (pixel00 >> 8) & 0xff; // green
494
p01 = (pixel01 >> 8) & 0xff;
495
p10 = (pixel10 >> 8) & 0xff;
496
p11 = (pixel11 >> 8) & 0xff;
498
px0 = (p00*tuf + p10*tuf1) >> 8;
499
px1 = (p01*tuf + p11*tuf1) >> 8;
500
tg = (((px0*tvf + px1*tvf1) >> 8) *
501
(interpARGB ? ((int) (sp[G]*255)) : g2)) >> 8;
503
p00 = pixel00 & 0xff; // blue
504
p01 = pixel01 & 0xff;
505
p10 = pixel10 & 0xff;
506
p11 = pixel11 & 0xff;
508
px0 = (p00*tuf + p10*tuf1) >> 8;
509
px1 = (p01*tuf + p11*tuf1) >> 8;
510
tb = (((px0*tvf + px1*tvf1) >> 8) *
511
(interpARGB ? ((int) (sp[B]*255)) : b2)) >> 8;
513
} else { // alpha image, only use current fill color
515
tr = (int) (sp[R] * 255);
516
tg = (int) (sp[G] * 255);
517
tb = (int) (sp[B] * 255);
526
int weight = smooth ? coverage(x) : 255;
527
if (weight != 255) ta = ta*weight >> 8;
529
if ((ta == 254) || (ta == 255)) { // if (ta & 0xf8) would be good
531
pixels[offset+x] = 0xff000000 | (tr << 16) | (tg << 8) | tb;
534
// blend with pixel on screen
536
int r1 = (pixels[offset+x] >> 16) & 0xff;
537
int g1 = (pixels[offset+x] >> 8) & 0xff;
538
int b1 = (pixels[offset+x]) & 0xff;
540
pixels[offset+x] = 0xff000000 |
541
(((tr*ta + r1*a1) >> 8) << 16) |
542
((tg*ta + g1*a1) & 0xff00) |
543
((tb*ta + b1*a1) >> 8);
546
} else { // no image applied
547
int weight = smooth ? coverage(x) : 255;
550
r2 = (int) (sp[R] * 255);
551
g2 = (int) (sp[G] * 255);
552
b2 = (int) (sp[B] * 255);
553
if (sp[A] != 1) weight = (weight * ((int) (sp[A] * 255))) >> 8;
555
rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
558
if (a2orig != 255) weight = (weight * a2orig) >> 8;
562
// no blend, no aa, just the rgba
563
pixels[offset+x] = rgba;
564
//zbuffer[offset+x] = sp[Z];
567
int r1 = (pixels[offset+x] >> 16) & 0xff;
568
int g1 = (pixels[offset+x] >> 8) & 0xff;
569
int b1 = (pixels[offset+x]) & 0xff;
573
pixels[offset+x] = (0xff000000 |
574
((r1*a1 + r2*a2) >> 8) << 16 |
575
// use & instead of >> and << below
576
((g1*a1 + g2*a2) >> 8) << 8 |
577
((b1*a1 + b2*a2) >> 8));
581
// if smooth enabled, don't increment values
582
// for the pixel in the stretch out version
583
// of the scanline used to get smooth edges.
584
if (!smooth || ((x >= truelx) && (x <= truerx))) {
593
// x is in screen, not huge 8x coordinates
594
private int coverage(int x) {
595
if ((x >= aaleftfull) && (x <= aarightfull) &&
596
// important since not all SUBYRES lines may have been covered
597
(firstModY == 0) && (lastModY == SUBYRES1)) {
601
int pixelLeft = x*SUBXRES; // huh?
602
int pixelRight = pixelLeft + 8;
605
for (int i = firstModY; i <= lastModY; i++) {
606
if ((aaleft[i] > pixelRight) || (aaright[i] < pixelLeft)) {
609
// does this need a +1 ?
610
amt += ((aaright[i] < pixelRight ? aaright[i] : pixelRight) -
611
(aaleft[i] > pixelLeft ? aaleft[i] : pixelLeft));
614
return (amt == 256) ? 255 : amt;
618
private void incrementalizeY(float p1[], float p2[],
619
float p[], float dp[], int y) {
620
float delta = p2[TY] - p1[TY];
621
if (delta == 0) delta = 1;
622
float fraction = y + 0.5f - p1[TY];
625
dp[TX] = (p2[TX] - p1[TX]) / delta;
626
p[TX] = p1[TX] + dp[TX] * fraction;
630
dp[R] = (p2[R] - p1[R]) / delta;
631
dp[G] = (p2[G] - p1[G]) / delta;
632
dp[B] = (p2[B] - p1[B]) / delta;
633
dp[A] = (p2[A] - p1[A]) / delta;
634
p[R] = p1[R] + dp[R] * fraction;
635
p[G] = p1[G] + dp[G] * fraction;
636
p[B] = p1[B] + dp[B] * fraction;
637
p[A] = p1[A] + dp[A] * fraction;
641
dp[U] = (p2[U] - p1[U]) / delta;
642
dp[V] = (p2[V] - p1[V]) / delta;
644
p[U] = p1[U] + dp[U] * fraction;
645
p[V] = p1[V] + dp[V] * fraction;
650
private void incrementalizeX(float p1[], float p2[],
651
float p[], float dp[], int x) {
652
float delta = p2[TX] - p1[TX];
653
if (delta == 0) delta = 1;
654
float fraction = x + 0.5f - p1[TX];
661
dp[TX] = (p2[TX] - p1[TX]) / delta;
662
p[TX] = p1[TX] + dp[TX] * fraction;
666
dp[R] = (p2[R] - p1[R]) / delta;
667
dp[G] = (p2[G] - p1[G]) / delta;
668
dp[B] = (p2[B] - p1[B]) / delta;
669
dp[A] = (p2[A] - p1[A]) / delta;
670
p[R] = p1[R] + dp[R] * fraction;
671
p[G] = p1[G] + dp[G] * fraction;
672
p[B] = p1[B] + dp[B] * fraction;
673
p[A] = p1[A] + dp[A] * fraction;
677
dp[U] = (p2[U] - p1[U]) / delta;
678
dp[V] = (p2[V] - p1[V]) / delta;
680
p[U] = p1[U] + dp[U] * fraction;
681
p[V] = p1[V] + dp[V] * fraction;
686
private void increment(float p[], float dp[]) {
687
if (interpX) p[TX] += dp[TX];