/*
* SVG Salamander
* Copyright (c) 2004, Mark McKay
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Mark McKay can be contacted at mark@kitfox.com. Salamander and other
* projects can be found at http://www.kitfox.com
*
* Created on January 26, 2004, 3:25 AM
*/
package com.kitfox.svg;
import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.kitfox.svg.xml.StyleAttribute;
/**
* @author Mark McKay
* @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
*/
abstract public class Gradient extends FillElement {
public static final String TAG_NAME = "gradient";
public static final int SM_PAD = 0;
public static final int SM_REPEAT = 1;
public static final int SM_REFLECT = 2;
int spreadMethod = SM_PAD;
public static final int GU_OBJECT_BOUNDING_BOX = 0;
public static final int GU_USER_SPACE_ON_USE = 1;
protected int gradientUnits = GU_OBJECT_BOUNDING_BOX;
// Either this gradient contains a list of stops, or it will take it's
// stops from the referenced gradient
ArrayList stops = new ArrayList();
URI stopRef = null;
protected AffineTransform gradientTransform = null;
// Cache arrays of stop values here
float[] stopFractions;
Color[] stopColors;
/**
* Creates a new instance of Gradient
*/
public Gradient() {
}
@Override
public String getTagName() {
return TAG_NAME;
}
/**
* Called after the start element but before the end element to indicate
* each child tag that has been processed
*/
@Override
public void loaderAddChild(SVGLoaderHelper helper, SVGElement child)
throws SVGElementException {
super.loaderAddChild(helper, child);
if (!(child instanceof Stop)) {
return;
}
appendStop((Stop) child);
}
@Override
protected void build() throws SVGException {
super.build();
StyleAttribute sty = new StyleAttribute();
String strn;
if (getPres(sty.setName("spreadMethod"))) {
strn = sty.getStringValue().toLowerCase();
if (strn.equals("repeat")) {
spreadMethod = SM_REPEAT;
} else if (strn.equals("reflect")) {
spreadMethod = SM_REFLECT;
} else {
spreadMethod = SM_PAD;
}
}
if (getPres(sty.setName("gradientUnits"))) {
strn = sty.getStringValue().toLowerCase();
if (strn.equals("userspaceonuse")) {
gradientUnits = GU_USER_SPACE_ON_USE;
} else {
gradientUnits = GU_OBJECT_BOUNDING_BOX;
}
}
if (getPres(sty.setName("gradientTransform"))) {
gradientTransform = parseTransform(sty.getStringValue());
}
// If we still don't have one, set it to identity
if (gradientTransform == null) {
gradientTransform = new AffineTransform();
}
// Check to see if we're using our own stops or referencing someone
// else's
if (getPres(sty.setName("xlink:href"))) {
try {
stopRef = sty.getURIValue(getXMLBase());
// System.err.println("Gradient: " + sty.getStringValue() + ", "
// + getXMLBase() + ", " + src);
// URI src = getXMLBase().resolve(href);
// stopRef = (Gradient)diagram.getUniverse().getElement(src);
} catch (Exception e) {
throw new SVGException(
"Could not resolve relative URL in Gradient: "
+ sty.getStringValue() + ", " + getXMLBase(),
e);
}
}
}
public float[] getStopFractions() {
if (stopRef != null) {
Gradient grad = (Gradient) diagram.getUniverse()
.getElement(stopRef);
return grad.getStopFractions();
}
if (stopFractions != null) {
return stopFractions;
}
stopFractions = new float[stops.size()];
int idx = 0;
for (Iterator it = stops.iterator(); it.hasNext();) {
Stop stop = (Stop) it.next();
float val = stop.offset;
if (idx != 0 && val < stopFractions[idx - 1]) {
val = stopFractions[idx - 1];
}
stopFractions[idx++] = val;
}
return stopFractions;
}
public Color[] getStopColors() {
if (stopRef != null) {
Gradient grad = (Gradient) diagram.getUniverse()
.getElement(stopRef);
return grad.getStopColors();
}
if (stopColors != null) {
return stopColors;
}
stopColors = new Color[stops.size()];
int idx = 0;
for (Iterator it = stops.iterator(); it.hasNext();) {
Stop stop = (Stop) it.next();
int stopColorVal = stop.color.getRGB();
Color stopColor = new Color((stopColorVal >> 16) & 0xff,
(stopColorVal >> 8) & 0xff, stopColorVal & 0xff,
clamp((int) (stop.opacity * 255), 0, 255));
stopColors[idx++] = stopColor;
}
return stopColors;
}
public void setStops(Color[] colors, float[] fractions) {
if (colors.length != fractions.length) {
throw new IllegalArgumentException();
}
this.stopColors = colors;
this.stopFractions = fractions;
stopRef = null;
}
private static int clamp(int val, int min, int max) {
if (val < min) {
return min;
}
if (val > max) {
return max;
}
return val;
}
public void setStopRef(URI grad) {
stopRef = grad;
}
public void appendStop(Stop stop) {
stops.add(stop);
}
/**
* Updates all attributes in this diagram associated with a time event. Ie,
* all attributes with track information.
*
* @return - true if this node has changed state as a result of the time
* update
*/
@Override
public boolean updateTime(double curTime) throws SVGException {
// if (trackManager.getNumTracks() == 0) return false;
boolean stateChange = false;
// Get current values for parameters
StyleAttribute sty = new StyleAttribute();
String strn;
if (getPres(sty.setName("spreadMethod"))) {
int newVal;
strn = sty.getStringValue().toLowerCase();
if (strn.equals("repeat")) {
newVal = SM_REPEAT;
} else if (strn.equals("reflect")) {
newVal = SM_REFLECT;
} else {
newVal = SM_PAD;
}
if (spreadMethod != newVal) {
spreadMethod = newVal;
stateChange = true;
}
}
if (getPres(sty.setName("gradientUnits"))) {
int newVal;
strn = sty.getStringValue().toLowerCase();
if (strn.equals("userspaceonuse")) {
newVal = GU_USER_SPACE_ON_USE;
} else {
newVal = GU_OBJECT_BOUNDING_BOX;
}
if (newVal != gradientUnits) {
gradientUnits = newVal;
stateChange = true;
}
}
if (getPres(sty.setName("gradientTransform"))) {
AffineTransform newVal = parseTransform(sty.getStringValue());
if (newVal != null && newVal.equals(gradientTransform)) {
gradientTransform = newVal;
stateChange = true;
}
}
// Check to see if we're using our own stops or referencing someone
// else's
if (getPres(sty.setName("xlink:href"))) {
try {
URI newVal = sty.getURIValue(getXMLBase());
if ((newVal == null && stopRef != null)
|| (newVal != null && !newVal.equals(stopRef))) {
stopRef = newVal;
stateChange = true;
}
} catch (Exception e) {
Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
"Could not parse xlink:href", e);
}
}
// Check stops, if any
for (Iterator it = stops.iterator(); it.hasNext();) {
Stop stop = (Stop) it.next();
if (stop.updateTime(curTime)) {
stateChange = true;
stopFractions = null;
stopColors = null;
}
}
return stateChange;
}
}