/*
* 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.Graphics2D;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.net.URI;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.kitfox.svg.pattern.PatternPaint;
import com.kitfox.svg.xml.StyleAttribute;
/**
* @author Mark McKay
* @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
*/
public class PatternSVG extends FillElement {
public static final String TAG_NAME = "pattern";
public static final int GU_OBJECT_BOUNDING_BOX = 0;
public static final int GU_USER_SPACE_ON_USE = 1;
int gradientUnits = GU_OBJECT_BOUNDING_BOX;
float x;
float y;
float width;
float height;
AffineTransform patternXform = new AffineTransform();
Rectangle2D.Float viewBox;
Paint texPaint;
/**
* Creates a new instance of Gradient
*/
public PatternSVG() {
}
@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);
}
@Override
protected void build() throws SVGException {
super.build();
StyleAttribute sty = new StyleAttribute();
// Load style string
String href = null;
if (getPres(sty.setName("xlink:href"))) {
href = sty.getStringValue();
}
// String href = attrs.getValue("xlink:href");
// If we have a link to another pattern, initialize ourselves with it's
// values
if (href != null) {
// System.err.println("Gradient.loaderStartElement() href '" + href
// + "'");
try {
URI src = getXMLBase().resolve(href);
PatternSVG patSrc = (PatternSVG) diagram.getUniverse()
.getElement(src);
gradientUnits = patSrc.gradientUnits;
x = patSrc.x;
y = patSrc.y;
width = patSrc.width;
height = patSrc.height;
viewBox = patSrc.viewBox;
patternXform.setTransform(patSrc.patternXform);
children.addAll(patSrc.children);
} catch (Exception e) {
Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
"Could not parse xlink:href", e);
}
}
String gradientUnits = "";
if (getPres(sty.setName("gradientUnits"))) {
gradientUnits = sty.getStringValue().toLowerCase();
}
if (gradientUnits.equals("userspaceonuse")) {
this.gradientUnits = GU_USER_SPACE_ON_USE;
} else {
this.gradientUnits = GU_OBJECT_BOUNDING_BOX;
}
String patternTransform = "";
if (getPres(sty.setName("patternTransform"))) {
patternTransform = sty.getStringValue();
}
patternXform = parseTransform(patternTransform);
if (getPres(sty.setName("x"))) {
x = sty.getFloatValueWithUnits();
}
if (getPres(sty.setName("y"))) {
y = sty.getFloatValueWithUnits();
}
if (getPres(sty.setName("width"))) {
width = sty.getFloatValueWithUnits();
}
if (getPres(sty.setName("height"))) {
height = sty.getFloatValueWithUnits();
}
if (getPres(sty.setName("viewBox"))) {
float[] dim = sty.getFloatList();
viewBox = new Rectangle2D.Float(dim[0], dim[1], dim[2], dim[3]);
}
preparePattern();
}
/*
* public void loaderEndElement(SVGLoaderHelper helper) { build(); }
*/
protected void preparePattern() throws SVGException {
// For now, treat all fills as UserSpaceOnUse. Otherwise, we'll need
// a different paint for every object.
int tileWidth = (int) width;
int tileHeight = (int) height;
float stretchX = 1f, stretchY = 1f;
if (!patternXform.isIdentity()) {
// Scale our source tile so that we can have nice sampling from it.
float xlateX = (float) patternXform.getTranslateX();
float xlateY = (float) patternXform.getTranslateY();
Point2D.Float pt = new Point2D.Float(), pt2 = new Point2D.Float();
pt.setLocation(width, 0);
patternXform.transform(pt, pt2);
pt2.x -= xlateX;
pt2.y -= xlateY;
stretchX = (float) Math.sqrt(pt2.x * pt2.x + pt2.y * pt2.y) * 1.5f
/ width;
pt.setLocation(height, 0);
patternXform.transform(pt, pt2);
pt2.x -= xlateX;
pt2.y -= xlateY;
stretchY = (float) Math.sqrt(pt2.x * pt2.x + pt2.y * pt2.y) * 1.5f
/ height;
tileWidth *= stretchX;
tileHeight *= stretchY;
}
if (tileWidth == 0 || tileHeight == 0) {
// Use defaults if tile has degenerate size
return;
}
BufferedImage buf = new BufferedImage(tileWidth, tileHeight,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = buf.createGraphics();
g.setClip(0, 0, tileWidth, tileHeight);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Iterator it = children.iterator(); it.hasNext();) {
SVGElement ele = (SVGElement) it.next();
if (ele instanceof RenderableElement) {
AffineTransform xform = new AffineTransform();
if (viewBox == null) {
xform.translate(-x, -y);
} else {
xform.scale(tileWidth / viewBox.width,
tileHeight / viewBox.height);
xform.translate(-viewBox.x, -viewBox.y);
}
g.setTransform(xform);
((RenderableElement) ele).render(g);
}
}
g.dispose();
// try {
// javax.imageio.ImageIO.write(buf, "png", new
// java.io.File("c:\\tmp\\texPaint.png"));
// } catch (Exception e ) {}
if (patternXform.isIdentity()) {
texPaint = new TexturePaint(buf,
new Rectangle2D.Float(x, y, width, height));
} else {
patternXform.scale(1 / stretchX, 1 / stretchY);
texPaint = new PatternPaint(buf, patternXform);
}
}
@Override
public Paint getPaint(Rectangle2D bounds, AffineTransform xform) {
return texPaint;
}
/**
* 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 {
// Patterns don't change state
return false;
}
}