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.xml.StyleAttribute; 039import java.awt.Color; 040import java.awt.geom.AffineTransform; 041import java.net.URI; 042import java.util.ArrayList; 043import java.util.Iterator; 044import java.util.logging.Level; 045import java.util.logging.Logger; 046 047/** 048 * @author Mark McKay 049 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a> 050 */ 051abstract public class Gradient extends FillElement 052{ 053 public static final String TAG_NAME = "gradient"; 054 055 public static final int SM_PAD = 0; 056 public static final int SM_REPEAT = 1; 057 public static final int SM_REFLECT = 2; 058 int spreadMethod = SM_PAD; 059 public static final int GU_OBJECT_BOUNDING_BOX = 0; 060 public static final int GU_USER_SPACE_ON_USE = 1; 061 protected int gradientUnits = GU_OBJECT_BOUNDING_BOX; 062 //Either this gradient contains a list of stops, or it will take it's 063 // stops from the referenced gradient 064 ArrayList stops = new ArrayList(); 065 URI stopRef = null; 066 protected AffineTransform gradientTransform = null; 067 068 //Cache arrays of stop values here 069 float[] stopFractions; 070 Color[] stopColors; 071 072 /** 073 * Creates a new instance of Gradient 074 */ 075 public Gradient() 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 if (!(child instanceof Stop)) 093 { 094 return; 095 } 096 appendStop((Stop) child); 097 } 098 099 protected void build() throws SVGException 100 { 101 super.build(); 102 103 StyleAttribute sty = new StyleAttribute(); 104 String strn; 105 106 if (getPres(sty.setName("spreadMethod"))) 107 { 108 strn = sty.getStringValue().toLowerCase(); 109 if (strn.equals("repeat")) 110 { 111 spreadMethod = SM_REPEAT; 112 } else if (strn.equals("reflect")) 113 { 114 spreadMethod = SM_REFLECT; 115 } else 116 { 117 spreadMethod = SM_PAD; 118 } 119 } 120 121 if (getPres(sty.setName("gradientUnits"))) 122 { 123 strn = sty.getStringValue().toLowerCase(); 124 if (strn.equals("userspaceonuse")) 125 { 126 gradientUnits = GU_USER_SPACE_ON_USE; 127 } else 128 { 129 gradientUnits = GU_OBJECT_BOUNDING_BOX; 130 } 131 } 132 133 if (getPres(sty.setName("gradientTransform"))) 134 { 135 gradientTransform = parseTransform(sty.getStringValue()); 136 } 137 //If we still don't have one, set it to identity 138 if (gradientTransform == null) 139 { 140 gradientTransform = new AffineTransform(); 141 } 142 143 144 //Check to see if we're using our own stops or referencing someone else's 145 if (getPres(sty.setName("xlink:href"))) 146 { 147 try 148 { 149 stopRef = sty.getURIValue(getXMLBase()); 150//System.err.println("Gradient: " + sty.getStringValue() + ", " + getXMLBase() + ", " + src); 151// URI src = getXMLBase().resolve(href); 152// stopRef = (Gradient)diagram.getUniverse().getElement(src); 153 } catch (Exception e) 154 { 155 throw new SVGException("Could not resolve relative URL in Gradient: " + sty.getStringValue() + ", " + getXMLBase(), e); 156 } 157 } 158 } 159 160 public float[] getStopFractions() 161 { 162 if (stopRef != null) 163 { 164 Gradient grad = (Gradient) diagram.getUniverse().getElement(stopRef); 165 return grad.getStopFractions(); 166 } 167 168 if (stopFractions != null) 169 { 170 return stopFractions; 171 } 172 173 stopFractions = new float[stops.size()]; 174 int idx = 0; 175 for (Iterator it = stops.iterator(); it.hasNext();) 176 { 177 Stop stop = (Stop) it.next(); 178 float val = stop.offset; 179 if (idx != 0 && val < stopFractions[idx - 1]) 180 { 181 val = stopFractions[idx - 1]; 182 } 183 stopFractions[idx++] = val; 184 } 185 186 return stopFractions; 187 } 188 189 public Color[] getStopColors() 190 { 191 if (stopRef != null) 192 { 193 Gradient grad = (Gradient) diagram.getUniverse().getElement(stopRef); 194 return grad.getStopColors(); 195 } 196 197 if (stopColors != null) 198 { 199 return stopColors; 200 } 201 202 stopColors = new Color[stops.size()]; 203 int idx = 0; 204 for (Iterator it = stops.iterator(); it.hasNext();) 205 { 206 Stop stop = (Stop) it.next(); 207 int stopColorVal = stop.color.getRGB(); 208 Color stopColor = new Color((stopColorVal >> 16) & 0xff, (stopColorVal >> 8) & 0xff, stopColorVal & 0xff, clamp((int) (stop.opacity * 255), 0, 255)); 209 stopColors[idx++] = stopColor; 210 } 211 212 return stopColors; 213 } 214 215 public void setStops(Color[] colors, float[] fractions) 216 { 217 if (colors.length != fractions.length) 218 { 219 throw new IllegalArgumentException(); 220 } 221 222 this.stopColors = colors; 223 this.stopFractions = fractions; 224 stopRef = null; 225 } 226 227 private int clamp(int val, int min, int max) 228 { 229 if (val < min) 230 { 231 return min; 232 } 233 if (val > max) 234 { 235 return max; 236 } 237 return val; 238 } 239 240 public void setStopRef(URI grad) 241 { 242 stopRef = grad; 243 } 244 245 public void appendStop(Stop stop) 246 { 247 stops.add(stop); 248 } 249 250 /** 251 * Updates all attributes in this diagram associated with a time event. Ie, 252 * all attributes with track information. 253 * 254 * @return - true if this node has changed state as a result of the time 255 * update 256 */ 257 public boolean updateTime(double curTime) throws SVGException 258 { 259// if (trackManager.getNumTracks() == 0) return false; 260 boolean stateChange = false; 261 262 //Get current values for parameters 263 StyleAttribute sty = new StyleAttribute(); 264 boolean shapeChange = false; 265 String strn; 266 267 268 if (getPres(sty.setName("spreadMethod"))) 269 { 270 int newVal; 271 strn = sty.getStringValue().toLowerCase(); 272 if (strn.equals("repeat")) 273 { 274 newVal = SM_REPEAT; 275 } else if (strn.equals("reflect")) 276 { 277 newVal = SM_REFLECT; 278 } else 279 { 280 newVal = SM_PAD; 281 } 282 if (spreadMethod != newVal) 283 { 284 spreadMethod = newVal; 285 stateChange = true; 286 } 287 } 288 289 if (getPres(sty.setName("gradientUnits"))) 290 { 291 int newVal; 292 strn = sty.getStringValue().toLowerCase(); 293 if (strn.equals("userspaceonuse")) 294 { 295 newVal = GU_USER_SPACE_ON_USE; 296 } else 297 { 298 newVal = GU_OBJECT_BOUNDING_BOX; 299 } 300 if (newVal != gradientUnits) 301 { 302 gradientUnits = newVal; 303 stateChange = true; 304 } 305 } 306 307 if (getPres(sty.setName("gradientTransform"))) 308 { 309 AffineTransform newVal = parseTransform(sty.getStringValue()); 310 if (newVal != null && newVal.equals(gradientTransform)) 311 { 312 gradientTransform = newVal; 313 stateChange = true; 314 } 315 } 316 317 318 //Check to see if we're using our own stops or referencing someone else's 319 if (getPres(sty.setName("xlink:href"))) 320 { 321 try 322 { 323 URI newVal = sty.getURIValue(getXMLBase()); 324 if ((newVal == null && stopRef != null) || !newVal.equals(stopRef)) 325 { 326 stopRef = newVal; 327 stateChange = true; 328 } 329 } catch (Exception e) 330 { 331 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 332 "Could not parse xlink:href", e); 333 } 334 } 335 336 //Check stops, if any 337 for (Iterator it = stops.iterator(); it.hasNext();) 338 { 339 Stop stop = (Stop) it.next(); 340 if (stop.updateTime(curTime)) 341 { 342 stateChange = true; 343 stopFractions = null; 344 stopColors = null; 345 } 346 } 347 348 return stateChange; 349 } 350}