more work on gradients

This commit is contained in:
benfry
2006-12-13 20:06:36 +00:00
parent 4c0094c47d
commit ba92a4dfcb
3 changed files with 462 additions and 274 deletions

View File

@@ -0,0 +1,187 @@
package processing.candy;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
//import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import processing.core.PApplet;
public class LinearGradientPaint implements Paint {
float x1, y1, x2, y2;
float[] offset;
int[] color;
int count;
float opacity;
public LinearGradientPaint(float x1, float y1, float x2, float y2,
float[] offset, int[] color, int count,
float opacity) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.offset = offset;
this.color = color;
this.count = count;
this.opacity = opacity;
}
public PaintContext createContext(ColorModel cm,
Rectangle deviceBounds, Rectangle2D userBounds,
AffineTransform xform, RenderingHints hints) {
//return new LinearGradientContext();
/*
Point2D t1 = xform.transform(new Point2D.Float(x1, y1), null);
Point2D t2 = xform.transform(new Point2D.Float(x2, y2), null);
return new LinearGradientContext((float) t1.getX(), (float) t1.getY(),
(float) t2.getX(), (float) t2.getY(),
offset, color, count, opacity);
*/
Point2D t1 = xform.transform(new Point2D.Float(x1, y1), null);
Point2D t2 = xform.transform(new Point2D.Float(x2, y2), null);
return new LinearGradientContext((float) t1.getX(), (float) t1.getY(),
(float) t2.getX(), (float) t2.getY());
}
public int getTransparency() {
/*
int a1 = mPointColor.getAlpha();
int a2 = mBackgroundColor.getAlpha();
return (((a1 & a2) == 0xff) ? OPAQUE : TRANSLUCENT);
*/
//return OPAQUE;
return TRANSLUCENT; // why not.. rather than checking each color
}
public class LinearGradientContext implements PaintContext {
int ACCURACY = 2;
float tx1, ty1, tx2, ty2;
public LinearGradientContext(float tx1, float ty1, float tx2, float ty2) {
this.tx1 = tx1;
this.ty1 = ty1;
this.tx2 = tx2;
this.ty2 = ty2;
//System.out.println(x1 + " " + y1 + " " + x2 + " " + y2 + " .. t = " +
// tx1 + " " + ty1 + " " + tx2 + " " + ty2);
}
/*
float x1, y1, x2, y2;
float[] offset;
int[] color;
int count;
float opacity;
public LinearGradientContext(float x1, float y1, float x2, float y2,
float[] offset, int[] color, int count,
float opacity) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.offset = offset;
this.color = color;
this.count = count;
this.opacity = opacity;
}
*/
public void dispose() { }
public ColorModel getColorModel() { return ColorModel.getRGBdefault(); }
public Raster getRaster(int x, int y, int w, int h) {
WritableRaster raster =
getColorModel().createCompatibleWritableRaster(w, h);
int[] data = new int[w * h * 4];
// make normalized version of base vector
float nx = tx2 - tx1;
float ny = ty2 - ty1;
float len = (float) Math.sqrt(nx*nx + ny*ny);
if (len != 0) {
nx /= len;
ny /= len;
}
int span = (int) PApplet.dist(tx1, ty1, tx2, ty2) * ACCURACY;
if (span <= 0) {
//System.err.println("span is too small");
// annoying edge case where the gradient isn't legit
int index = 0;
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
data[index++] = 0;
data[index++] = 0;
data[index++] = 0;
data[index++] = 255;
}
}
} else {
int[][] interp = new int[span][4];
int prev = 0;
for (int i = 1; i < count; i++) {
int c0 = color[i-1];
int c1 = color[i];
int last = (int) (offset[i] * (span-1));
//System.out.println("last is " + last);
for (int j = prev; j <= last; j++) {
float btwn = PApplet.norm(j, prev, last);
interp[j][0] = (int) PApplet.lerp((c0 >> 16) & 0xff, (c1 >> 16) & 0xff, btwn);
interp[j][1] = (int) PApplet.lerp((c0 >> 8) & 0xff, (c1 >> 8) & 0xff, btwn);
interp[j][2] = (int) PApplet.lerp(c0 & 0xff, c1 & 0xff, btwn);
interp[j][3] = (int) (PApplet.lerp((c0 >> 24) & 0xff, (c1 >> 24) & 0xff, btwn) * opacity);
//System.out.println(j + " " + interp[j][0] + " " + interp[j][1] + " " + interp[j][2]);
}
prev = last;
}
int index = 0;
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
//float distance = 0; //PApplet.dist(cx, cy, x + i, y + j);
//int which = PApplet.min((int) (distance * ACCURACY), interp.length-1);
float px = (x + i) - tx1;
float py = (y + j) - ty1;
// distance up the line is the dot product of the normalized
// vector of the gradient start/stop by the point being tested
int which = (int) ((px*nx + py*ny) * ACCURACY);
if (which < 0) which = 0;
if (which > interp.length-1) which = interp.length-1;
//if (which > 138) System.out.println("grabbing " + which);
data[index++] = interp[which][0];
data[index++] = interp[which][1];
data[index++] = interp[which][2];
data[index++] = interp[which][3];
}
}
}
raster.setPixels(0, 0, w, h, data);
return raster;
}
}
}

View File

@@ -0,0 +1,137 @@
package processing.candy;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
//import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import processing.core.PApplet;
public class RadialGradientPaint implements Paint {
float cx, cy, radius;
float[] offset;
int[] color;
int count;
float opacity;
public RadialGradientPaint(float cx, float cy, float radius,
float[] offset, int[] color, int count,
float opacity) {
this.cx = cx;
this.cy = cy;
this.radius = radius;
this.offset = offset;
this.color = color;
this.count = count;
this.opacity = opacity;
}
public PaintContext createContext(ColorModel cm,
Rectangle deviceBounds, Rectangle2D userBounds,
AffineTransform xform, RenderingHints hints) {
return new RadialGradientContext();
/*
Point2D transformedPoint =
xform.transform(new Point2D.Float(cx, cy), null);
// this causes problems
//Point2D transformedRadius =
// xform.deltaTransform(new Point2D.Float(radius, radius), null);
return new RadialGradientContext((float) transformedPoint.getX(),
(float) transformedPoint.getY(),
radius, //(float) transformedRadius.distance(0, 0),
offset, color, count, opacity);
*/
}
public int getTransparency() {
/*
int a1 = mPointColor.getAlpha();
int a2 = mBackgroundColor.getAlpha();
return (((a1 & a2) == 0xff) ? OPAQUE : TRANSLUCENT);
*/
//return (opacity == 1) ? OPAQUE : TRANSLUCENT;
return TRANSLUCENT; // why not.. rather than checking each color
}
public class RadialGradientContext implements PaintContext {
int ACCURACY = 5;
//float cx, cy, radius;
//float[] offset;
//int[] color;
//int count;
//float opacity;
/*
public RadialGradientContext(float cx, float cy, float radius,
float[] offset, int[] color, int count,
float opacity) {
this.cx = cx;
this.cy = cy;
this.radius = radius;
this.offset = offset;
this.color = color;
this.count = count;
this.opacity = opacity;
}
*/
public void dispose() {}
public ColorModel getColorModel() { return ColorModel.getRGBdefault(); }
public Raster getRaster(int x, int y, int w, int h) {
WritableRaster raster =
getColorModel().createCompatibleWritableRaster(w, h);
//System.out.println("radius here is " + radius);
//System.out.println("count is " + count);
int span = (int) radius * ACCURACY;
int[][] interp = new int[span][4];
int prev = 0;
for (int i = 1; i < count; i++) {
int c0 = color[i-1];
int c1 = color[i];
int last = (int) (offset[i] * (span - 1));
for (int j = prev; j <= last; j++) {
float btwn = PApplet.norm(j, prev, last);
interp[j][0] = (int) PApplet.lerp((c0 >> 16) & 0xff, (c1 >> 16) & 0xff, btwn);
interp[j][1] = (int) PApplet.lerp((c0 >> 8) & 0xff, (c1 >> 8) & 0xff, btwn);
interp[j][2] = (int) PApplet.lerp(c0 & 0xff, c1 & 0xff, btwn);
interp[j][3] = (int) (PApplet.lerp((c0 >> 24) & 0xff, (c1 >> 24) & 0xff, btwn) * opacity);
//System.out.println(interp[j][3]);
}
prev = last;
}
int[] data = new int[w * h * 4];
int index = 0;
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
float distance = PApplet.dist(cx, cy, x + i, y + j);
int which = PApplet.min((int) (distance * ACCURACY), interp.length-1);
data[index++] = interp[which][0];
data[index++] = interp[which][1];
data[index++] = interp[which][2];
data[index++] = interp[which][3];
}
}
raster.setPixels(0, 0, w, h, data);
return raster;
}
}
}

View File

@@ -20,8 +20,9 @@
package processing.candy;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.geom.AffineTransform;
//import java.awt.geom.*;
//import java.awt.image.*;
import java.util.Hashtable;
import processing.core.*;
@@ -157,7 +158,7 @@ public class SVG {
svg = new XMLElement(parent, filename);
if (!svg.getName().equals("svg")) {
throw new RuntimeException("root isn't svg, it's <" + svg.getName() + ">");
throw new RuntimeException("root is not <svg>, it's <" + svg.getName() + ">");
}
width = parseUnitSize(svg.getStringAttribute("width"));
@@ -270,6 +271,92 @@ public class SVG {
}
return null;
}
// grab the (fill) gradient from a particular object by name
// and apply it to either the stroke or fill
// based on
protected Paint getGradient(String name, float cx, float cy, float r) {
BaseObject obj = (BaseObject) table.get(name);
if (obj == null) {
// try with underscores instead of spaces
obj = (BaseObject) table.get(name.replace(' ', '_'));
}
if (obj != null) {
VectorObject vobj = (VectorObject) obj;
if (vobj.fillGradient != null) {
return vobj.calcGradientPaint(vobj.fillGradient, cx, cy, r);
}
}
throw new RuntimeException("No gradient found for shape " + name);
}
protected Paint getGradient(String name, float x1, float y1, float x2, float y2) {
BaseObject obj = (BaseObject) table.get(name);
if (obj == null) {
// try with underscores instead of spaces
obj = (BaseObject) table.get(name.replace(' ', '_'));
}
if (obj != null) {
VectorObject vobj = (VectorObject) obj;
if (vobj.fillGradient != null) {
return vobj.calcGradientPaint(vobj.fillGradient, x1, y1, x2, y2);
}
}
throw new RuntimeException("No gradient found for shape " + name);
}
public void strokeGradient(String name, float x, float y, float r) {
Paint paint = getGradient(name, x, y, r);
if (parent.g instanceof PGraphicsJava2D) {
PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g);
p2d.strokeGradient = true;
p2d.strokeGradientObject = paint;
}
}
public void strokeGradient(String name, float x1, float y1, float x2, float y2) {
Paint paint = getGradient(name, x1, y1, x2, y2);
if (parent.g instanceof PGraphicsJava2D) {
PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g);
p2d.strokeGradient = true;
p2d.strokeGradientObject = paint;
}
}
public void fillGradient(String name, float x, float y, float r) {
Paint paint = getGradient(name, x, y, r);
if (parent.g instanceof PGraphicsJava2D) {
PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g);
p2d.fillGradient = true;
p2d.fillGradientObject = paint;
}
}
public void fillGradient(String name, float x1, float y1, float x2, float y2) {
Paint paint = getGradient(name, x1, y1, x2, y2);
if (parent.g instanceof PGraphicsJava2D) {
PGraphicsJava2D p2d = ((PGraphicsJava2D) parent.g);
p2d.fillGradient = true;
p2d.fillGradientObject = paint;
}
}
/**
@@ -572,26 +659,15 @@ public class SVG {
}
protected Paint calcGradientPaint(Gradient gradient) { //, float opacity) {
protected Paint calcGradientPaint(Gradient gradient) {
if (gradient instanceof LinearGradient) {
LinearGradient grad = (LinearGradient) gradient;
/*
Color c1 = new Color(0xFF000000 | grad.color[0]);
Color c2 = new Color(0xFF000000 | grad.color[grad.count-1]);
return new GradientPaint(grad.x1, grad.y1, c1,
grad.x2, grad.y2, c2);
*/
return new LinearGradientPaint(grad.x1, grad.y1, grad.x2, grad.y2,
grad.offset, grad.color, grad.count,
opacity);
} else if (gradient instanceof RadialGradient) {
RadialGradient grad = (RadialGradient) gradient;
//Color c1 = new Color(0xFF000000 | grad.color[0]);
//Color c2 = new Color(0xFF000000 | grad.color[grad.count-1]);
return new RadialGradientPaint(grad.cx, grad.cy, grad.r,
grad.offset, grad.color, grad.count,
opacity);
@@ -600,6 +676,30 @@ public class SVG {
}
protected Paint calcGradientPaint(Gradient gradient,
float x1, float y1, float x2, float y2) {
if (gradient instanceof LinearGradient) {
LinearGradient grad = (LinearGradient) gradient;
return new LinearGradientPaint(x1, y1, x2, y2,
grad.offset, grad.color, grad.count,
opacity);
}
throw new RuntimeException("Not a linear gradient.");
}
protected Paint calcGradientPaint(Gradient gradient,
float cx, float cy, float r) {
if (gradient instanceof RadialGradient) {
RadialGradient grad = (RadialGradient) gradient;
return new RadialGradientPaint(cx, cy, r,
grad.offset, grad.color, grad.count,
opacity);
}
throw new RuntimeException("Not a radial gradient.");
}
protected abstract void drawShape();
@@ -674,265 +774,7 @@ public class SVG {
}
}
}
public class RadialGradientPaint implements Paint {
float cx, cy, radius;
float[] offset;
int[] color;
int count;
float opacity;
public RadialGradientPaint(float cx, float cy, float radius,
float[] offset, int[] color, int count,
float opacity) {
this.cx = cx;
this.cy = cy;
this.radius = radius;
this.offset = offset;
this.color = color;
this.count = count;
this.opacity = opacity;
}
public PaintContext createContext(ColorModel cm,
Rectangle deviceBounds, Rectangle2D userBounds,
AffineTransform xform, RenderingHints hints) {
Point2D transformedPoint =
xform.transform(new Point2D.Float(cx, cy), null);
// this causes problems
//Point2D transformedRadius =
// xform.deltaTransform(new Point2D.Float(radius, radius), null);
return new RadialGradientContext((float) transformedPoint.getX(),
(float) transformedPoint.getY(),
radius, //(float) transformedRadius.distance(0, 0),
offset, color, count, opacity);
}
public int getTransparency() {
/*
int a1 = mPointColor.getAlpha();
int a2 = mBackgroundColor.getAlpha();
return (((a1 & a2) == 0xff) ? OPAQUE : TRANSLUCENT);
*/
//return (opacity == 1) ? OPAQUE : TRANSLUCENT;
return TRANSLUCENT; // why not.. rather than checking each color
}
}
public class RadialGradientContext implements PaintContext {
float cx, cy, radius;
float[] offset;
int[] color;
int count;
float opacity;
public RadialGradientContext(float cx, float cy, float radius,
float[] offset, int[] color, int count,
float opacity) {
this.cx = cx;
this.cy = cy;
this.radius = radius;
this.offset = offset;
this.color = color;
this.count = count;
this.opacity = opacity;
}
public void dispose() {}
public ColorModel getColorModel() { return ColorModel.getRGBdefault(); }
int ACCURACY = 5;
public Raster getRaster(int x, int y, int w, int h) {
WritableRaster raster =
getColorModel().createCompatibleWritableRaster(w, h);
//System.out.println("radius here is " + radius);
//System.out.println("count is " + count);
int span = (int) radius * ACCURACY;
int[][] interp = new int[span][4];
int prev = 0;
for (int i = 1; i < count; i++) {
int c0 = color[i-1];
int c1 = color[i];
int last = (int) (offset[i] * (span - 1));
for (int j = prev; j <= last; j++) {
float btwn = PApplet.norm(j, prev, last);
interp[j][0] = (int) PApplet.lerp((c0 >> 16) & 0xff, (c1 >> 16) & 0xff, btwn);
interp[j][1] = (int) PApplet.lerp((c0 >> 8) & 0xff, (c1 >> 8) & 0xff, btwn);
interp[j][2] = (int) PApplet.lerp(c0 & 0xff, c1 & 0xff, btwn);
interp[j][3] = (int) (PApplet.lerp((c0 >> 24) & 0xff, (c1 >> 24) & 0xff, btwn) * opacity);
//System.out.println(interp[j][3]);
}
prev = last;
}
int[] data = new int[w * h * 4];
int index = 0;
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
float distance = PApplet.dist(cx, cy, x + i, y + j);
int which = PApplet.min((int) (distance * ACCURACY), interp.length-1);
data[index++] = interp[which][0];
data[index++] = interp[which][1];
data[index++] = interp[which][2];
data[index++] = interp[which][3];
}
}
raster.setPixels(0, 0, w, h, data);
return raster;
}
}
public class LinearGradientPaint implements Paint {
float x1, y1, x2, y2;
float[] offset;
int[] color;
int count;
float opacity;
public LinearGradientPaint(float x1, float y1, float x2, float y2,
float[] offset, int[] color, int count,
float opacity) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.offset = offset;
this.color = color;
this.count = count;
this.opacity = opacity;
}
public PaintContext createContext(ColorModel cm,
Rectangle deviceBounds, Rectangle2D userBounds,
AffineTransform xform, RenderingHints hints) {
Point2D t1 = xform.transform(new Point2D.Float(x1, y1), null);
Point2D t2 = xform.transform(new Point2D.Float(x2, y2), null);
return new LinearGradientContext((float) t1.getX(), (float) t1.getY(),
(float) t2.getX(), (float) t2.getY(),
offset, color, count, opacity);
}
public int getTransparency() {
/*
int a1 = mPointColor.getAlpha();
int a2 = mBackgroundColor.getAlpha();
return (((a1 & a2) == 0xff) ? OPAQUE : TRANSLUCENT);
*/
//return OPAQUE;
return TRANSLUCENT; // why not.. rather than checking each color
}
}
public class LinearGradientContext implements PaintContext {
float x1, y1, x2, y2;
float[] offset;
int[] color;
int count;
float opacity;
public LinearGradientContext(float x1, float y1, float x2, float y2,
float[] offset, int[] color, int count,
float opacity) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.offset = offset;
this.color = color;
this.count = count;
this.opacity = opacity;
}
public void dispose() { }
public ColorModel getColorModel() { return ColorModel.getRGBdefault(); }
int ACCURACY = 2;
public Raster getRaster(int x, int y, int w, int h) {
WritableRaster raster =
getColorModel().createCompatibleWritableRaster(w, h);
int[] data = new int[w * h * 4];
// make normalized version of base vector
float nx = x2 - x1;
float ny = y2 - y1;
float len = (float) Math.sqrt(nx*nx + ny*ny);
if (len != 0) {
nx /= len;
ny /= len;
}
int span = (int) PApplet.dist(x1, y1, x2, y2) * ACCURACY;
if (span <= 0) {
// annoying edge case where the gradient isn't legit
int index = 0;
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
data[index++] = 0;
data[index++] = 0;
data[index++] = 0;
data[index++] = 255;
}
}
} else {
//System.out.println("span is " + span + " " + x1 + " " + y1 + " " + x2 + " " + y2);
int[][] interp = new int[span][4];
int prev = 0;
for (int i = 1; i < count; i++) {
int c0 = color[i-1];
int c1 = color[i];
int last = (int) (offset[i] * (span-1));
//System.out.println("last is " + last);
for (int j = prev; j <= last; j++) {
float btwn = PApplet.norm(j, prev, last);
interp[j][0] = (int) PApplet.lerp((c0 >> 16) & 0xff, (c1 >> 16) & 0xff, btwn);
interp[j][1] = (int) PApplet.lerp((c0 >> 8) & 0xff, (c1 >> 8) & 0xff, btwn);
interp[j][2] = (int) PApplet.lerp(c0 & 0xff, c1 & 0xff, btwn);
interp[j][3] = (int) (PApplet.lerp((c0 >> 24) & 0xff, (c1 >> 24) & 0xff, btwn) * opacity);
//System.out.println(j + " " + interp[j][0] + " " + interp[j][1] + " " + interp[j][2]);
}
prev = last;
}
int index = 0;
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
//float distance = 0; //PApplet.dist(cx, cy, x + i, y + j);
//int which = PApplet.min((int) (distance * ACCURACY), interp.length-1);
float px = (x + i) - x1;
float py = (y + j) - y1;
// distance up the line is the dot product of the normalized
// vector of the gradient start/stop by the point being tested
int which = (int) ((px*nx + py*ny) * ACCURACY);
if (which < 0) which = 0;
if (which > interp.length-1) which = interp.length-1;
//if (which > 138) System.out.println("grabbing " + which);
data[index++] = interp[which][0];
data[index++] = interp[which][1];
data[index++] = interp[which][2];
data[index++] = interp[which][3];
}
}
}
raster.setPixels(0, 0, w, h, data);
return raster;
}
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -1076,6 +918,7 @@ public class SVG {
private class LinearGradient extends Gradient {
float x1, y1, x2, y2;
AffineTransform transform;
public LinearGradient(XMLElement properties) {
super(properties);
@@ -1084,11 +927,32 @@ public class SVG {
this.y1 = properties.getFloatAttribute("y1");
this.x2 = properties.getFloatAttribute("x2");
this.y2 = properties.getFloatAttribute("y2");
String transformStr =
properties.getStringAttribute("gradientTransform");
if (transformStr != null) {
this.transform = parseTransform(transformStr);
System.out.println(transform);
}
}
protected void drawShape(){
}
}
// complete version is here
// http://www.w3.org/TR/SVG/coords.html#TransformAttribute
AffineTransform parseTransform(String what) {
if (what != null) {
if (what.startsWith("matrix(") && what.endsWith(")")) {
// columns go first with AT constructor
what = what.substring(7, what.length() - 1);
return new AffineTransform(PApplet.parseFloat(PApplet.split(what, ' ')));
}
}
return null;
}
private class RadialGradient extends Gradient {