Files
processing4/core/PGraphics2.java
2005-05-10 22:46:46 +00:00

1032 lines
27 KiB
Java

/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2005 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
/**
* Subclass for PGraphics that implements the graphics API
* in Java 1.3+ using Java 2D.
*/
public class PGraphics2 extends PGraphics {
public Graphics2D g2;
GeneralPath gpath;
int transformCount;
AffineTransform transformStack[] =
new AffineTransform[MATRIX_STACK_DEPTH];
double transform[] = new double[6];
Line2D.Float line = new Line2D.Float();
Ellipse2D.Float ellipse = new Ellipse2D.Float();
Rectangle2D.Float rect = new Rectangle2D.Float();
Arc2D.Float arc = new Arc2D.Float();
protected Color tintColorObject;
protected Color fillColorObject;
protected Color strokeColorObject;
//////////////////////////////////////////////////////////////
// INTERNAL
/**
* Constructor for the PGraphics2 object.
* This prototype only exists because of annoying
* java compilers, and should not be used.
*/
public PGraphics2() { }
/**
* Constructor for the PGraphics object. Use this to ensure that
* the defaults get set properly. In a subclass, use this(w, h)
* as the first line of a subclass' constructor to properly set
* the internal fields and defaults.
*
* @param iwidth viewport width
* @param iheight viewport height
*/
public PGraphics2(int iwidth, int iheight, PApplet parent) {
super(iwidth, iheight, parent);
//resize(iwidth, iheight);
}
/**
* Called in repsonse to a resize event, handles setting the
* new width and height internally, as well as re-allocating
* the pixel buffer for the new size.
*
* Note that this will nuke any cameraMode() settings.
*/
public void resize(int iwidth, int iheight) { // ignore
//System.out.println("resize " + iwidth + " " + iheight);
width = iwidth;
height = iheight;
width1 = width - 1;
height1 = height - 1;
allocate();
// clear the screen with the old background color
background(backgroundColor);
}
// broken out because of subclassing for opengl
protected void allocate() {
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
g2 = (Graphics2D) image.getGraphics();
}
//////////////////////////////////////////////////////////////
// FRAME
// turn off mis.newPixels
public void endFrame() {
// moving this back here (post-68) because of macosx thread problem
//mis.newPixels(pixels, cm, 0, width);
}
//////////////////////////////////////////////////////////////
// SHAPES
public void vertex(float x, float y) {
splineVertexCount = 0;
float vertex[];
if (vertexCount == vertices.length) {
float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
System.arraycopy(vertices, 0, temp, 0, vertexCount);
vertices = temp;
//message(CHATTER, "allocating more vertices " + vertices.length);
}
// not everyone needs this, but just easier to store rather
// than adding another moving part to the code...
vertices[vertexCount][MX] = x;
vertices[vertexCount][MY] = y;
vertexCount++;
switch (shape) {
case POINTS:
point(x, y);
break;
case LINES:
if ((vertexCount % 2) == 0) {
line(vertices[vertexCount-2][MX],
vertices[vertexCount-2][MY], x, y);
}
break;
case LINE_STRIP:
case LINE_LOOP:
if (gpath == null) {
gpath = new GeneralPath();
gpath.moveTo(x, y);
} else {
gpath.lineTo(x, y);
}
break;
case TRIANGLES:
if ((vertexCount % 3) == 0) {
triangle(vertices[vertexCount - 3][MX],
vertices[vertexCount - 3][MY],
vertices[vertexCount - 2][MX],
vertices[vertexCount - 2][MY],
x, y);
}
break;
case TRIANGLE_STRIP:
if (vertexCount >= 3) {
triangle(vertices[vertexCount - 2][MX],
vertices[vertexCount - 2][MY],
vertices[vertexCount - 1][MX],
vertices[vertexCount - 1][MY],
vertices[vertexCount - 3][MX],
vertices[vertexCount - 3][MY]);
}
break;
case TRIANGLE_FAN:
if (vertexCount == 3) {
triangle(vertices[0][MX], vertices[0][MY],
vertices[1][MX], vertices[1][MY],
x, y);
} else if (vertexCount > 3) {
gpath = new GeneralPath();
// when vertexCount > 3, draw an un-closed triangle
// for indices 0 (center), previous, current
gpath.moveTo(vertices[0][MX],
vertices[0][MY]);
gpath.lineTo(vertices[vertexCount - 2][MX],
vertices[vertexCount - 2][MY]);
gpath.lineTo(x, y);
draw_shape(gpath);
}
break;
case QUADS:
if ((vertexCount % 4) == 0) {
quad(vertices[vertexCount - 4][MX],
vertices[vertexCount - 4][MY],
vertices[vertexCount - 3][MX],
vertices[vertexCount - 3][MY],
vertices[vertexCount - 2][MX],
vertices[vertexCount - 2][MY],
x, y);
}
break;
case QUAD_STRIP:
// 0---2---4
// | | |
// 1---3---5
if ((vertexCount >= 4) && ((vertexCount % 2) == 0)) {
quad(vertices[vertexCount - 4][MX],
vertices[vertexCount - 4][MY],
vertices[vertexCount - 2][MX],
vertices[vertexCount - 2][MY],
x, y,
vertices[vertexCount - 3][MX],
vertices[vertexCount - 3][MY]);
}
break;
case POLYGON:
if (gpath == null) {
gpath = new GeneralPath();
gpath.moveTo(x, y);
} else {
gpath.lineTo(x, y);
}
break;
}
}
public void bezierVertex(float x1, float y1,
float x2, float y2,
float x3, float y3) {
if (gpath == null) {
throw new RuntimeException("Must call vertex() at least once " +
"before using bezierVertex()");
}
switch (shape) {
case LINE_LOOP:
case LINE_STRIP:
case POLYGON:
gpath.curveTo(x1, y1, x2, y2, x3, y3);
break;
default:
throw new RuntimeException("bezierVertex() can only be used with " +
"LINE_STRIP, LINE_LOOP, or POLYGON");
}
}
float curveX[] = new float[4];
float curveY[] = new float[4];
public void curveVertex(float x, float y) {
if ((shape != LINE_LOOP) && (shape != LINE_STRIP) && (shape != POLYGON)) {
throw new RuntimeException("curveVertex() can only be used with " +
"LINE_LOOP, LINE_STRIP, and POLYGON shapes");
}
if (!curve_inited) curve_init();
vertexCount = 0;
if (splineVertices == null) {
splineVertices = new float[DEFAULT_SPLINE_VERTICES][VERTEX_FIELD_COUNT];
}
// if more than 128 points, shift everything back to the beginning
if (splineVertexCount == DEFAULT_SPLINE_VERTICES) {
System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 3], 0,
splineVertices[0], 0, VERTEX_FIELD_COUNT);
System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 2], 0,
splineVertices[1], 0, VERTEX_FIELD_COUNT);
splineVertexCount = 3;
}
// this new guy will be the fourth point (or higher),
// which means it's time to draw segments of the curve
if (splineVertexCount >= 3) {
curveX[0] = splineVertices[splineVertexCount-3][MX];
curveY[0] = splineVertices[splineVertexCount-3][MY];
curveX[1] = splineVertices[splineVertexCount-2][MX];
curveY[1] = splineVertices[splineVertexCount-2][MY];
curveX[2] = splineVertices[splineVertexCount-1][MX];
curveY[2] = splineVertices[splineVertexCount-1][MY];
curveX[3] = x;
curveY[3] = y;
curveToBezierMatrix.mult(curveX, curveX);
curveToBezierMatrix.mult(curveY, curveY);
// since the paths are continuous,
// only the first point needs the actual moveto
if (gpath == null) {
gpath = new GeneralPath();
gpath.moveTo(curveX[0], curveY[0]);
}
gpath.curveTo(curveX[1], curveY[1],
curveX[2], curveY[2],
curveX[3], curveY[3]);
}
// add the current point to the list
splineVertices[splineVertexCount][MX] = x;
splineVertices[splineVertexCount][MY] = y;
splineVertexCount++;
}
public void beginShape(int kind) {
super.beginShape(kind);
// set gpath to null, because when mixing curves and straight
// lines, vertexCount will be set back to zero, so vertexCount == 1
// is no longer a good indicator of whether the shape is new.
// this way, just check to see if gpath is null, and if it isn't
// then just use it to continue the shape.
gpath = null;
}
public void endShape() {
if (gpath != null) { // make sure something has been drawn
switch (shape) {
case LINE_STRIP:
stroke_shape(gpath);
break;
case LINE_LOOP:
gpath.closePath();
stroke_shape(gpath);
break;
case POLYGON:
gpath.closePath();
draw_shape(gpath);
break;
}
}
shape = 0;
}
//////////////////////////////////////////////////////////////
protected void fill_shape(Shape s) {
if (fill) {
g2.setColor(fillColorObject);
g2.fill(s);
}
}
protected void stroke_shape(Shape s) {
if (stroke) {
g2.setColor(strokeColorObject);
g2.draw(s);
}
}
protected void draw_shape(Shape s) {
if (fill) {
g2.setColor(fillColorObject);
g2.fill(s);
}
if (stroke) {
g2.setColor(strokeColorObject);
g2.draw(s);
}
}
//////////////////////////////////////////////////////////////
public void point(float x, float y) {
line(x, y, x, y);
}
public void line(float x1, float y1, float x2, float y2) {
//graphics.setColor(strokeColorObject);
//graphics.drawLine(x1, y1, x2, y2);
line.setLine(x1, y1, x2, y2);
stroke_shape(line);
}
public void triangle(float x1, float y1, float x2, float y2,
float x3, float y3) {
gpath = new GeneralPath();
gpath.moveTo(x1, y1);
gpath.lineTo(x2, y2);
gpath.lineTo(x3, y3);
gpath.closePath();
draw_shape(gpath);
}
public void quad(float x1, float y1, float x2, float y2,
float x3, float y3, float x4, float y4) {
GeneralPath gp = new GeneralPath();
gp.moveTo(x1, y1);
gp.lineTo(x2, y2);
gp.lineTo(x3, y3);
gp.lineTo(x4, y4);
gp.closePath();
draw_shape(gp);
}
//////////////////////////////////////////////////////////////
protected void rectImpl(float x1, float y1, float x2, float y2) {
rect.setFrame(x1, y1, x2-x1, y2-y1);
draw_shape(rect);
}
protected void ellipseImpl(float x, float y, float w, float h) {
ellipse.setFrame(x, y, w, h);
draw_shape(ellipse);
}
protected void arcImpl(float x, float y, float w, float h,
float start, float stop) {
// 0 to 90 in java would be 0 to -90 for p5 renderer
// but that won't work, so -90 to 0?
if (stop - start >= TWO_PI) {
start = 0;
stop = 360;
} else {
start = -start * RAD_TO_DEG;
stop = -stop * RAD_TO_DEG;
// ok to do this because already checked for NaN
while (start < 0) {
start += 360;
stop += 360;
}
if (start > stop) {
float temp = start;
start = stop;
stop = temp;
}
}
float span = stop - start;
// stroke as Arc2D.OPEN, fill as Arc2D.PIE
if (fill) {
//System.out.println("filla");
arc.setArc(x, y, w, h, start, span, Arc2D.PIE);
fill_shape(arc);
}
if (stroke) {
//System.out.println("strokey");
arc.setArc(x, y, w, h, start, span, Arc2D.OPEN);
stroke_shape(arc);
}
}
//////////////////////////////////////////////////////////////
/** Ignored (not needed) in Java 2D. */
public void bezierDetail(int detail) {
}
/** Ignored (not needed) in Java 2D. */
public void curveDetail(int detail) {
}
//////////////////////////////////////////////////////////////
protected void imageImpl(PImage who,
float x1, float y1, float x2, float y2,
//float x, float y, float w, float h,
int u1, int v1, int u2, int v2) {
if (who.cache == null) {
who.cache = new ImageCache(who);
who.updatePixels(); // mark the whole thing for update
}
ImageCache cash = (ImageCache) who.cache;
// if image previously was tinted, or the color changed
// or the image was tinted, and tint is now disabled
if ((tint && !cash.tinted) ||
(tint && (cash.tintedColor != tintColor)) ||
(!tint && cash.tinted)) {
// for tint change, mark all pixels as needing update
who.updatePixels();
}
if (who.modified) {
cash.update();
who.modified = false;
//System.out.println("image modified");
}
g2.drawImage(((ImageCache) who.cache).image,
(int) x1, (int) y1, (int) x2, (int) y2,
u1, v1, u2, v2, null);
}
class ImageCache {
PImage source;
boolean tinted;
int tintedColor;
int tintedPixels[];
BufferedImage image;
public ImageCache(PImage source) {
this.source = source;
// even if RGB, set the image type to ARGB, because the
// image may have an alpha value for its tint().
int type = BufferedImage.TYPE_INT_ARGB;
image = new BufferedImage(source.width, source.height, type);
}
public void update() { //boolean t, int argb) {
if ((source.format == ARGB) || (source.format == RGB)) {
if (tint) {
// create tintedPixels[] if necessary
if (tintedPixels == null) {
tintedPixels = new int[source.width * source.height];
}
int argb2 = tintColor;
int a2 = (tintColor >> 24) & 0xff;
int r2 = (tintColor >> 16) & 0xff;
int g2 = (tintColor >> 8) & 0xff;
int b2 = (tintColor) & 0xff;
//System.out.println("a2 is " + a2);
// multiply each of the color components into tintedPixels
// if straight RGB image, don't bother multiplying
// (also avoids problems if high bits not set)
if (source.format == RGB) {
int alpha = a2 << 24;
for (int i = 0; i < tintedPixels.length; i++) {
int argb1 = source.pixels[i];
int r1 = (argb1 >> 16) & 0xff;
int g1 = (argb1 >> 8) & 0xff;
int b1 = (argb1) & 0xff;
tintedPixels[i] = alpha |
(((r2 * r1) & 0xff00) << 8) |
((g2 * g1) & 0xff00) |
(((b2 * b1) & 0xff00) >> 8);
}
} else {
for (int i = 0; i < tintedPixels.length; i++) {
int argb1 = source.pixels[i];
int a1 = (argb1 >> 24) & 0xff;
int r1 = (argb1 >> 16) & 0xff;
int g1 = (argb1 >> 8) & 0xff;
int b1 = (argb1) & 0xff;
tintedPixels[i] =
(((a2 * a1) & 0xff00) << 16) |
(((r2 * r1) & 0xff00) << 8) |
((g2 * g1) & 0xff00) |
(((b2 * b1) & 0xff00) >> 8);
}
}
tinted = true;
tintedColor = tintColor;
// finally, do a setRGB based on tintedPixels
image.setRGB(0, 0, source.width, source.height,
tintedPixels, 0, source.width);
} else { // no tint
// just do a setRGB like before
// (and we'll just hope that the high bits are set)
image.setRGB(0, 0, source.width, source.height,
source.pixels, 0, source.width);
}
} else if (source.format == ALPHA) {
if (tintedPixels == null) {
tintedPixels = new int[source.width * source.height];
}
int lowbits = tintColor & 0x00ffffff;
if (((tintColor >> 24) & 0xff) >= 254) {
// no actual alpha to the tint, set the image's alpha
// as the high 8 bits, and use the color as the low 24 bits
for (int i = 0; i < tintedPixels.length; i++) {
// don't bother with the math if value is zero
tintedPixels[i] = (source.pixels[i] == 0) ?
0 : (source.pixels[i] << 24) | lowbits;
}
} else {
// multiply each image alpha by the tint alpha
int alphabits = (tintColor >> 24) & 0xff;
for (int i = 0; i < tintedPixels.length; i++) {
tintedPixels[i] = (source.pixels[i] == 0) ?
0 : (((alphabits * source.pixels[i]) & 0xFF00) << 16) | lowbits;
}
}
// mark the pixels for next time
tinted = true;
tintedColor = tintColor;
// finally, do a setRGB based on tintedPixels
image.setRGB(0, 0, source.width, source.height,
tintedPixels, 0, source.width);
}
}
}
//////////////////////////////////////////////////////////////
public void translate(float tx, float ty) {
g2.translate(tx, ty);
}
public void rotate(float angle) {
g2.rotate(angle);
}
public void scale(float s) {
g2.scale(s, s);
}
public void scale(float sx, float sy) {
g2.scale(sx, sy);
}
//////////////////////////////////////////////////////////////
public void pushMatrix() {
if (transformCount == transformStack.length) {
throw new RuntimeException("pushMatrix() cannot use push more than " +
transformStack.length + " times");
}
transformStack[transformCount] = g2.getTransform();
transformCount++;
}
public void popMatrix() {
if (transformCount == 0) {
throw new RuntimeException("missing a popMatrix() " +
"to go with that pushMatrix()");
}
transformCount--;
g2.setTransform(transformStack[transformCount]);
}
public void resetMatrix() {
g2.setTransform(new AffineTransform());
}
public void applyMatrix(float n00, float n01, float n02,
float n10, float n11, float n12) {
g2.transform(new AffineTransform(n00, n10, n01, n11, n02, n12));
}
public void printMatrix() {
g2.getTransform().getMatrix(transform);
m00 = (float) transform[0];
m01 = (float) transform[2];
m02 = (float) transform[4];
m10 = (float) transform[1];
m11 = (float) transform[3];
m12 = (float) transform[5];
super.printMatrix();
}
public float screenX(float x, float y) {
g2.getTransform().getMatrix(transform);
//return m00*x + m01*y + m02;
return (float)transform[0]*x + (float)transform[2]*y + (float)transform[4];
}
public float screenY(float x, float y) {
g2.getTransform().getMatrix(transform);
return (float)transform[1]*x + (float)transform[3]*y + (float)transform[5];
}
//////////////////////////////////////////////////////////////
protected void colorTint() {
super.colorTint();
// TODO actually implement tinted images
tintColorObject = new Color(tintColor, true);
}
protected void colorFill() {
super.colorFill();
fillColorObject = new Color(fillColor, true);
}
protected void colorStroke() {
super.colorStroke();
strokeColorObject = new Color(strokeColor, true);
}
//////////////////////////////////////////////////////////////
public void strokeWeight(float weight) {
super.strokeWeight(weight);
set_stroke();
}
public void strokeJoin(int join) {
super.strokeJoin(join);
set_stroke();
}
public void strokeCap(int cap) {
super.strokeCap(cap);
set_stroke();
}
protected void set_stroke() {
int cap = BasicStroke.CAP_BUTT;
if (strokeCap == ROUND) {
cap = BasicStroke.CAP_ROUND;
} else if (strokeCap == PROJECT) {
cap = BasicStroke.CAP_SQUARE;
}
int join = BasicStroke.JOIN_BEVEL;
if (strokeJoin == MITER) {
join = BasicStroke.JOIN_MITER;
} else if (strokeJoin == ROUND) {
join = BasicStroke.JOIN_ROUND;
}
g2.setStroke(new BasicStroke(strokeWeight, cap, join));
}
//////////////////////////////////////////////////////////////
public void background(PImage image) {
if ((image.width != width) || (image.height != height)) {
throw new RuntimeException("background image must be " +
"the same size as your application");
}
if ((image.format != RGB) && (image.format != ARGB)) {
throw new RuntimeException("background images should be RGB or ARGB");
}
// draw the image to screen without any transformations
set(0, 0, image);
}
public void clear() {
g2.setColor(new Color(backgroundColor));
g2.fillRect(0, 0, width, height);
}
//////////////////////////////////////////////////////////////
// FROM PIMAGE
public void smooth() {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
public void noSmooth() {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_OFF);
}
//////////////////////////////////////////////////////////////
public void loadPixels() {
if ((pixels == null) || (pixels.length != width * height)) {
pixels = new int[width * height];
}
((BufferedImage) image).getRGB(0, 0, width, height, pixels, 0, width);
}
/**
* Update the pixels[] buffer to the PGraphics image.
* <P>
* Unlike in PImage, where updatePixels() only asks that the
* update happens, in PGraphics2, this will happen immediately.
*/
public void updatePixels() {
updatePixels(0, 0, width, height);
}
/**
* Update the pixels[] buffer to the PGraphics image.
* <P>
* Unlike in PImage, where updatePixels() only asks that the
* update happens, in PGraphics2, this will happen immediately.
*/
public void updatePixels(int x, int y, int c, int d) {
((BufferedImage) image).setRGB(x, y,
(imageMode == CORNER) ? c : (c - x),
(imageMode == CORNER) ? d : (d - y),
pixels, 0, width);
}
//////////////////////////////////////////////////////////////
public int get(int x, int y) {
if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0;
return ((BufferedImage) image).getRGB(x, y);
}
public PImage get(int x, int y, int w, int h) {
if (imageMode == CORNERS) { // if CORNER, do nothing
// w/h are x2/y2 in this case, bring em down to size
w = (w - x);
h = (h - x);
}
if (x < 0) {
w += x; // clip off the left edge
x = 0;
}
if (y < 0) {
h += y; // clip off some of the height
y = 0;
}
if (x + w > width) w = width - x;
if (y + h > height) h = height - y;
PImage output = new PImage(w, h);
// oops, the last parameter is the scan size of the *target* buffer
((BufferedImage) image).getRGB(x, y, w, h, output.pixels, 0, w);
return output;
}
/**
* Grab a copy of the current pixel buffer.
*/
public PImage get() {
PImage outgoing = new PImage(width, height);
((BufferedImage) image).getRGB(0, 0, width, height,
outgoing.pixels, 0, width);
return outgoing;
}
public void set(int x, int y, int argb) {
if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return;
((BufferedImage) image).setRGB(x, y, argb);
}
// fully debugged
/*
public void set(int dx, int dy, PImage src) {
int sx = 0;
int sy = 0;
int sw = src.width;
int sh = src.height;
if (dx < 0) { // off left edge
sx -= dx;
sw += dx;
dx = 0;
}
if (dy < 0) { // off top edge
sy -= dy;
sh += dy;
dy = 0;
}
if (dx + sw > width) { // off right edge
sw = width - dx;
}
if (dy + sh > height) { // off bottom edge
sh = height - dy;
}
//System.out.println(dx + " " + dy + " " +
// sx + " " + sy + " " + sw + " " + sh + " " +
// src.pixels + " " + 0 + " " + src.width);
BufferedImage bi = (BufferedImage) image;
bi.setRGB(dx, dy, sw, sh, src.pixels, sy*src.width + sx, src.width);
}
*/
protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh,
PImage src) {
BufferedImage bi = (BufferedImage) image;
bi.setRGB(dx, dy, sw, sh, src.pixels, sy*src.width + sx, src.width);
}
//////////////////////////////////////////////////////////////
public void filter(int kind) {
loadPixels();
super.filter(kind);
updatePixels();
}
public void filter(int kind, float param) {
loadPixels();
super.filter(kind, param);
updatePixels();
}
//////////////////////////////////////////////////////////////
public void copy(PImage src, int sx1, int sy1, int sx2, int sy2,
int dx1, int dy1, int dx2, int dy2) {
loadPixels();
super.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
updatePixels();
}
public void blend(PImage src, int sx, int sy, int dx, int dy, int mode) {
loadPixels();
super.blend(src, sx, sy, dx, dy, mode);
updatePixels();
}
public void blend(int sx, int sy, int dx, int dy, int mode) {
loadPixels();
super.blend(sx, sy, dx, dy, mode);
updatePixels();
}
public void blend(int sx1, int sy1, int sx2, int sy2,
int dx1, int dy1, int dx2, int dy2, int mode) {
loadPixels();
super.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode);
updatePixels();
}
public void blend(PImage src, int sx1, int sy1, int sx2, int sy2,
int dx1, int dy1, int dx2, int dy2, int mode) {
loadPixels();
super.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode);
updatePixels();
}
public void save(String filename) {
loadPixels();
super.save(filename);
//boolean ImageIO.write(RenderedImage im, String formatName, File output)
}
}