/*
* @(#)LinearGradient.java
*
* Copyright (c) 1996-2010 The authors and contributors of JHotDraw.
* You may not use, copy or modify this file, except in compliance with the
* accompanying license terms.
*/
package org.jhotdraw.samples.svg;
import java.awt.*;
import java.awt.geom.*;
import java.util.Arrays;
import org.jhotdraw.draw.*;
import static org.jhotdraw.samples.svg.SVGAttributeKeys.*;
/**
* Represents an SVG LinearGradient.
*
* @author Werner Randelshofer
* @version $Id$
*/
public class LinearGradient implements Gradient {
private double x1;
private double y1;
private double x2;
private double y2;
private boolean isRelativeToFigureBounds = true;
private double[] stopOffsets;
private Color[] stopColors;
private double[] stopOpacities;
private AffineTransform transform;
private int spreadMethod;
/** Creates a new instance. */
public LinearGradient() {
}
public LinearGradient(
double x1, double y1, double x2, double y2,
double[] stopOffsets, Color[] stopColors, double[] stopOpacities,
boolean isRelativeToFigureBounds,
AffineTransform tx) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.stopOffsets = stopOffsets.clone();
this.stopColors = stopColors.clone();
this.stopOpacities = stopOpacities.clone();
this.isRelativeToFigureBounds = isRelativeToFigureBounds;
this.transform = tx;
}
public void setGradientVector(double x1, double y1, double x2, double y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
public void setStops(double[] offsets, Color[] colors, double[] stopOpacities) {
this.stopOffsets = offsets.clone();
this.stopColors = colors.clone();
this.stopOpacities = stopOpacities.clone();
}
public void setRelativeToFigureBounds(boolean b) {
isRelativeToFigureBounds = b;
}
@Override
public boolean isRelativeToFigureBounds() {
return isRelativeToFigureBounds;
}
public double getX1() {
return x1;
}
public double getY1() {
return y1;
}
public double getX2() {
return x2;
}
public double getY2() {
return y2;
}
public double[] getStopOffsets() {
return stopOffsets.clone();
}
public Color[] getStopColors() {
return stopColors.clone();
}
public double[] getStopOpacities() {
return stopOpacities.clone();
}
public AffineTransform getTransform() {
return transform;
}
@Override
public Paint getPaint(Figure f, double opacity) {
// No stops, like fill = none
if (stopColors.length == 0) {
return new Color(0x0,true);
}
// Compute colors and fractions for the paint
Color[] colors = new Color[stopColors.length];
float[] fractions = new float[stopColors.length];
float previousFraction = 0;
for (int i=0; i < stopColors.length; i++) {
// Each fraction must be larger or equal the previous fraction.
fractions[i] = Math.min(1f, Math.max(previousFraction, (float) stopOffsets[i]));
colors[i] = new Color(
(stopColors[i].getRGB() & 0xffffff) |
((int) (opacity * stopOpacities[i] * 255) << 24),
true
);
previousFraction = fractions[i];
}
// Compute the dimensions and transforms for the paint
Point2D.Double p1;
Point2D.Double p2;
p1 = new Point2D.Double(x1, y1);
p2 = new Point2D.Double(x2, y2);
AffineTransform t = transform;
if (isRelativeToFigureBounds) {
t = (AffineTransform) t.clone();
Rectangle2D.Double bounds = f.getBounds();
t.translate(bounds.x, bounds.y);
t.scale(bounds.width, bounds.height);
}
// Construct a solid color, if only one stop color is given, or if
// transform is not invertible
if (stopColors.length == 1 || t.getDeterminant() == 0) {
return colors[0];
}
// Construct a gradient
LinearGradientPaint gp;
gp = new LinearGradientPaint(
p1, p2, fractions, colors,
LinearGradientPaint.CycleMethod.NO_CYCLE,
LinearGradientPaint.ColorSpaceType.SRGB,
t
);
return gp;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("LinearGradient@");
buf.append(hashCode());
buf.append('(');
for (int i=0; i < stopOffsets.length; i++) {
if (i != 0) buf.append(',');
buf.append(stopOffsets[i]);
buf.append('=');
buf.append(stopOpacities[i]);
buf.append(' ');
buf.append(Integer.toHexString(stopColors[i].getRGB()));
}
buf.append(')');
return buf.toString();
}
public void setTransform(AffineTransform tx) {
transform = tx;
}
@Override
public void transform(AffineTransform tx) {
if (transform == null) {
transform = (AffineTransform) tx.clone();
} else {
transform.preConcatenate(tx);
}
}
@Override
public Object clone() {
try {
LinearGradient that = (LinearGradient) super.clone();
that.stopOffsets = this.stopOffsets.clone();
that.stopColors = this.stopColors.clone();
that.stopOpacities = this.stopOpacities.clone();
that.transform = (AffineTransform) this.transform.clone();
return that;
} catch (CloneNotSupportedException ex) {
InternalError e = new InternalError();
e.initCause(ex);
throw e;
}
}
@Override
public void makeRelativeToFigureBounds(Figure f) {
if (! isRelativeToFigureBounds) {
isRelativeToFigureBounds = true;
Rectangle2D.Double bounds = f.getBounds();
x1 = (x1 - bounds.x) / bounds.width;
y1 = (y1 - bounds.y) / bounds.height;
x2 = (x2 - bounds.x) / bounds.width;
y2 = (y2 - bounds.y) / bounds.height;
}
}
@Override
public int hashCode() {
long bits = Double.doubleToLongBits(x1);
bits += Double.doubleToLongBits(y1) * 31;
bits += Double.doubleToLongBits(x2) * 35;
bits += Double.doubleToLongBits(y2) * 39;
bits += stopColors[0].hashCode() * 43;
bits += stopColors[stopColors.length - 1].hashCode() * 47;
return (((int) bits) ^ ((int) (bits >> 32)));
}
@Override
public boolean equals(Object o) {
if (o instanceof LinearGradient) {
return equals((LinearGradient) o);
} else {
return false;
}
}
public boolean equals(LinearGradient that) {
return x1 == that.x1 &&
y1 == that.y1 &&
x2 == that.x2 &&
y2 == that.y2 &&
isRelativeToFigureBounds == that.isRelativeToFigureBounds &&
Arrays.equals(stopOffsets, that.stopOffsets) &&
Arrays.equals(stopOpacities, that.stopOpacities) &&
Arrays.equals(stopColors, that.stopColors) &&
transform.equals(that.transform);
}
}