001/* 002 * SVG Salamander 003 * Copyright (c) 2004, Mark McKay 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or 007 * without modification, are permitted provided that the following 008 * conditions are met: 009 * 010 * - Redistributions of source code must retain the above 011 * copyright notice, this list of conditions and the following 012 * disclaimer. 013 * - Redistributions in binary form must reproduce the above 014 * copyright notice, this list of conditions and the following 015 * disclaimer in the documentation and/or other materials 016 * provided with the distribution. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 019 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 020 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 021 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 022 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 025 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 026 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 027 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 029 * OF THE POSSIBILITY OF SUCH DAMAGE. 030 * 031 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other 032 * projects can be found at http://www.kitfox.com 033 * 034 * Created on January 26, 2004, 3:25 AM 035 */ 036package com.kitfox.svg; 037 038import com.kitfox.svg.pattern.PatternPaint; 039import com.kitfox.svg.xml.StyleAttribute; 040import java.awt.Graphics2D; 041import java.awt.Paint; 042import java.awt.RenderingHints; 043import java.awt.TexturePaint; 044import java.awt.geom.AffineTransform; 045import java.awt.geom.Point2D; 046import java.awt.geom.Rectangle2D; 047import java.awt.image.BufferedImage; 048import java.net.URI; 049import java.util.Iterator; 050import java.util.logging.Level; 051import java.util.logging.Logger; 052 053/** 054 * @author Mark McKay 055 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a> 056 */ 057public class PatternSVG extends FillElement 058{ 059 public static final String TAG_NAME = "pattern"; 060 061 public static final int GU_OBJECT_BOUNDING_BOX = 0; 062 public static final int GU_USER_SPACE_ON_USE = 1; 063 int gradientUnits = GU_OBJECT_BOUNDING_BOX; 064 float x; 065 float y; 066 float width; 067 float height; 068 AffineTransform patternXform = new AffineTransform(); 069 Rectangle2D.Float viewBox; 070 Paint texPaint; 071 072 /** 073 * Creates a new instance of Gradient 074 */ 075 public PatternSVG() 076 { 077 } 078 079 public String getTagName() 080 { 081 return TAG_NAME; 082 } 083 084 /** 085 * Called after the start element but before the end element to indicate 086 * each child tag that has been processed 087 */ 088 public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException 089 { 090 super.loaderAddChild(helper, child); 091 } 092 093 protected void build() throws SVGException 094 { 095 super.build(); 096 097 StyleAttribute sty = new StyleAttribute(); 098 099 //Load style string 100 String href = null; 101 if (getPres(sty.setName("xlink:href"))) 102 { 103 href = sty.getStringValue(); 104 } 105 //String href = attrs.getValue("xlink:href"); 106 //If we have a link to another pattern, initialize ourselves with it's values 107 if (href != null) 108 { 109//System.err.println("Gradient.loaderStartElement() href '" + href + "'"); 110 try 111 { 112 URI src = getXMLBase().resolve(href); 113 PatternSVG patSrc = (PatternSVG) diagram.getUniverse().getElement(src); 114 115 gradientUnits = patSrc.gradientUnits; 116 x = patSrc.x; 117 y = patSrc.y; 118 width = patSrc.width; 119 height = patSrc.height; 120 viewBox = patSrc.viewBox; 121 patternXform.setTransform(patSrc.patternXform); 122 children.addAll(patSrc.children); 123 } catch (Exception e) 124 { 125 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 126 "Could not parse xlink:href", e); 127 } 128 } 129 130 String gradientUnits = ""; 131 if (getPres(sty.setName("gradientUnits"))) 132 { 133 gradientUnits = sty.getStringValue().toLowerCase(); 134 } 135 if (gradientUnits.equals("userspaceonuse")) 136 { 137 this.gradientUnits = GU_USER_SPACE_ON_USE; 138 } else 139 { 140 this.gradientUnits = GU_OBJECT_BOUNDING_BOX; 141 } 142 143 String patternTransform = ""; 144 if (getPres(sty.setName("patternTransform"))) 145 { 146 patternTransform = sty.getStringValue(); 147 } 148 patternXform = parseTransform(patternTransform); 149 150 151 if (getPres(sty.setName("x"))) 152 { 153 x = sty.getFloatValueWithUnits(); 154 } 155 156 if (getPres(sty.setName("y"))) 157 { 158 y = sty.getFloatValueWithUnits(); 159 } 160 161 if (getPres(sty.setName("width"))) 162 { 163 width = sty.getFloatValueWithUnits(); 164 } 165 166 if (getPres(sty.setName("height"))) 167 { 168 height = sty.getFloatValueWithUnits(); 169 } 170 171 if (getPres(sty.setName("viewBox"))) 172 { 173 float[] dim = sty.getFloatList(); 174 viewBox = new Rectangle2D.Float(dim[0], dim[1], dim[2], dim[3]); 175 } 176 177 preparePattern(); 178 } 179 180 /* 181 public void loaderEndElement(SVGLoaderHelper helper) 182 { 183 build(); 184 } 185 */ 186 protected void preparePattern() throws SVGException 187 { 188 //For now, treat all fills as UserSpaceOnUse. Otherwise, we'll need 189 // a different paint for every object. 190 int tileWidth = (int) width; 191 int tileHeight = (int) height; 192 193 float stretchX = 1f, stretchY = 1f; 194 if (!patternXform.isIdentity()) 195 { 196 //Scale our source tile so that we can have nice sampling from it. 197 float xlateX = (float) patternXform.getTranslateX(); 198 float xlateY = (float) patternXform.getTranslateY(); 199 200 Point2D.Float pt = new Point2D.Float(), pt2 = new Point2D.Float(); 201 202 pt.setLocation(width, 0); 203 patternXform.transform(pt, pt2); 204 pt2.x -= xlateX; 205 pt2.y -= xlateY; 206 stretchX = (float) Math.sqrt(pt2.x * pt2.x + pt2.y * pt2.y) * 1.5f / width; 207 208 pt.setLocation(height, 0); 209 patternXform.transform(pt, pt2); 210 pt2.x -= xlateX; 211 pt2.y -= xlateY; 212 stretchY = (float) Math.sqrt(pt2.x * pt2.x + pt2.y * pt2.y) * 1.5f / height; 213 214 tileWidth *= stretchX; 215 tileHeight *= stretchY; 216 } 217 218 if (tileWidth == 0 || tileHeight == 0) 219 { 220 //Use defaults if tile has degenerate size 221 return; 222 } 223 224 BufferedImage buf = new BufferedImage(tileWidth, tileHeight, BufferedImage.TYPE_INT_ARGB); 225 Graphics2D g = buf.createGraphics(); 226 g.setClip(0, 0, tileWidth, tileHeight); 227 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 228 229 for (Iterator it = children.iterator(); it.hasNext();) 230 { 231 SVGElement ele = (SVGElement) it.next(); 232 if (ele instanceof RenderableElement) 233 { 234 AffineTransform xform = new AffineTransform(); 235 236 if (viewBox == null) 237 { 238 xform.translate(-x, -y); 239 } else 240 { 241 xform.scale(tileWidth / viewBox.width, tileHeight / viewBox.height); 242 xform.translate(-viewBox.x, -viewBox.y); 243 } 244 245 g.setTransform(xform); 246 ((RenderableElement) ele).render(g); 247 } 248 } 249 250 g.dispose(); 251 252//try { 253//javax.imageio.ImageIO.write(buf, "png", new java.io.File("c:\\tmp\\texPaint.png")); 254//} catch (Exception e ) {} 255 256 if (patternXform.isIdentity()) 257 { 258 texPaint = new TexturePaint(buf, new Rectangle2D.Float(x, y, width, height)); 259 } else 260 { 261 patternXform.scale(1 / stretchX, 1 / stretchY); 262 texPaint = new PatternPaint(buf, patternXform); 263 } 264 } 265 266 public Paint getPaint(Rectangle2D bounds, AffineTransform xform) 267 { 268 return texPaint; 269 } 270 271 /** 272 * Updates all attributes in this diagram associated with a time event. Ie, 273 * all attributes with track information. 274 * 275 * @return - true if this node has changed state as a result of the time 276 * update 277 */ 278 public boolean updateTime(double curTime) throws SVGException 279 { 280 //Patterns don't change state 281 return false; 282 } 283}