package org.newdawn.slick.svg;
import java.util.ArrayList;
import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
import org.newdawn.slick.ImageBuffer;
import org.newdawn.slick.geom.Transform;
/**
* A gradient definition from an SVG file, includes the stops, name and transform.
*
* @author kevin
*/
public class Gradient {
/** The name/id given to the gradient */
private String name;
/** The steps in colour of the gradient */
private ArrayList steps = new ArrayList();
/** The first x coordiante given in the gradient (cx in radial) */
private float x1;
/** The second x coordiante given in the gradient (fx in radial) */
private float x2;
/** The first y coordiante given in the gradient (cy in radial) */
private float y1;
/** The first y coordiante given in the gradient (fy in radial) */
private float y2;
/** The radius given if any */
private float r;
/** The texture representing this gradient */
private Image image;
/** True if this gradient is radial in nature */
private boolean radial;
/** The transform specified for the gradient */
private Transform transform;
/** The name of the referenced gradient */
private String ref;
/**
* Create a new gradient definition
*
* @param name The name of the gradient
* @param radial True if the gradient is radial
*/
public Gradient(String name, boolean radial) {
this.name = name;
this.radial = radial;
}
/**
* Check if the gradient is radial
*
* @return True if the gradient is radial
*/
public boolean isRadial() {
return radial;
}
/**
* Set the transform given for this definition
*
* @param trans The transform given for this definition
*/
public void setTransform(Transform trans) {
this.transform = trans;
}
/**
* Get the transform to apply during this gradient application
*
* @return The transform given for this gradient
*/
public Transform getTransform() {
return transform;
}
/**
* Reference another gradient, i.e. use it's colour stops
*
* @param ref The name of the other gradient to reference
*/
public void reference(String ref) {
this.ref = ref;
}
/**
* Resolve the gradient reference
*
* @param diagram The diagram to resolve against
*/
public void resolve(Diagram diagram) {
if (ref == null) {
return;
}
Gradient other = diagram.getGradient(ref);
for (int i=0;i<other.steps.size();i++) {
steps.add(other.steps.get(i));
}
}
/**
* Generate the image used for texturing the gradient across shapes
*/
public void genImage() {
if (image == null) {
ImageBuffer buffer = new ImageBuffer(128,16);
for (int i=0;i<128;i++) {
Color col = getColorAt(i / 128.0f);
for (int j=0;j<16;j++) {
buffer.setRGBA(i, j, col.getRedByte(), col.getGreenByte(), col.getBlueByte(), col.getAlphaByte());
}
}
image = buffer.getImage();
}
}
/**
* Get the image generated for this gradient
*
* @return The image generated for the gradient
*/
public Image getImage() {
genImage();
return image;
}
/**
* Set the radius given in the SVG
*
* @param r The radius for radial gradients
*/
public void setR(float r) {
this.r = r;
}
/**
* Set the first x value given for the gradient (cx in the case of radial)
*
* @param x1 The first x value given for the gradient
*/
public void setX1(float x1) {
this.x1 = x1;
}
/**
* Set the second x value given for the gradient (fx in the case of radial)
*
* @param x2 The second x value given for the gradient
*/
public void setX2(float x2) {
this.x2 = x2;
}
/**
* Set the first y value given for the gradient (cy in the case of radial)
*
* @param y1 The first y value given for the gradient
*/
public void setY1(float y1) {
this.y1 = y1;
}
/**
* Set the second y value given for the gradient (fy in the case of radial)
*
* @param y2 The second y value given for the gradient
*/
public void setY2(float y2) {
this.y2 = y2;
}
/**
* Get the radius value given for this gradient
*
* @return The radius value given for this gradient
*/
public float getR() {
return r;
}
/**
* Get the first x value given for this gradient (cx in the case of radial)
*
* @return The first x value given for this gradient
*/
public float getX1() {
return x1;
}
/**
* Get the second x value given for this gradient (fx in the case of radial)
*
* @return The second x value given for this gradient
*/
public float getX2() {
return x2;
}
/**
* Get the first y value given for this gradient (cy in the case of radial)
*
* @return The first y value given for this gradient
*/
public float getY1() {
return y1;
}
/**
* Get the second y value given for this gradient (fy in the case of radial)
*
* @return The second y value given for this gradient
*/
public float getY2() {
return y2;
}
/**
* Add a colour step/stop to the gradient
*
* @param location The location on the gradient the colour affects
* @param c The color to apply
*/
public void addStep(float location, Color c) {
steps.add(new Step(location, c));
}
/**
* Get the intepolated colour at the given location on the gradient
*
* @param p The point of the gradient (0 >= n >= 1)
* @return The interpolated colour at the given location
*/
public Color getColorAt(float p) {
if (p <= 0) {
return ((Step) steps.get(0)).col;
}
if (p > 1) {
return ((Step) steps.get(steps.size()-1)).col;
}
for (int i=1;i<steps.size();i++) {
Step prev = ((Step) steps.get(i-1));
Step current = ((Step) steps.get(i));
if (p <= current.location) {
float dis = current.location - prev.location;
p -= prev.location;
float v = p / dis;
Color c = new Color(1,1,1,1);
c.a = (prev.col.a * (1 - v)) + (current.col.a * (v));
c.r = (prev.col.r * (1 - v)) + (current.col.r * (v));
c.g = (prev.col.g * (1 - v)) + (current.col.g * (v));
c.b = (prev.col.b * (1 - v)) + (current.col.b * (v));
return c;
}
}
// shouldn't ever happen
return Color.black;
}
/**
* The description of a single step on the gradient
*
* @author kevin
*/
private class Step {
/** The location on the gradient */
float location;
/** The colour applied */
Color col;
/**
* Create a new step
*
* @param location The location on the gradient the colour affects
* @param c The colour to apply
*/
public Step(float location, Color c) {
this.location = location;
this.col = c;
}
}
}