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 April 21, 2005, 10:45 AM 035 */ 036 037package com.kitfox.svg.app.beans; 038 039import com.kitfox.svg.*; 040import java.awt.*; 041import java.awt.geom.*; 042import java.beans.*; 043import java.net.*; 044import javax.swing.*; 045 046/** 047 * 048 * @author kitfox 049 */ 050public class SVGIcon implements Icon 051{ 052 public static final long serialVersionUID = 1; 053 054 private PropertyChangeSupport changes = new PropertyChangeSupport(this); 055 056 SVGUniverse svgUniverse = SVGCache.getSVGUniverse(); 057 public static final int INTERP_NEAREST_NEIGHBOR = 0; 058 public static final int INTERP_BILINEAR = 1; 059 public static final int INTERP_BICUBIC = 2; 060 061 private boolean antiAlias; 062 private int interpolation = INTERP_NEAREST_NEIGHBOR; 063 private boolean clipToViewbox; 064 065// private String svgPath; 066 URI svgURI; 067 068 private boolean scaleToFit; 069 AffineTransform scaleXform = new AffineTransform(); 070 071// Dimension preferredSize = new Dimension(100, 100); 072 Dimension preferredSize; 073 074 /** Creates a new instance of SVGIcon */ 075 public SVGIcon() 076 { 077 } 078 079 public void addPropertyChangeListener(PropertyChangeListener p) 080 { 081 changes.addPropertyChangeListener(p); 082 } 083 084 public void removePropertyChangeListener(PropertyChangeListener p) 085 { 086 changes.removePropertyChangeListener(p); 087 } 088 089 /** 090 * @return height of this icon 091 */ 092 public int getIconHeight() 093 { 094 if (scaleToFit && preferredSize != null) 095 { 096 return preferredSize.height; 097 } 098 099 SVGDiagram diagram = svgUniverse.getDiagram(svgURI); 100 if (diagram == null) 101 { 102 return 0; 103 } 104 return (int)diagram.getHeight(); 105 } 106 107 /** 108 * @return width of this icon 109 */ 110 public int getIconWidth() 111 { 112 if (scaleToFit && preferredSize != null) 113 { 114 return preferredSize.width; 115 } 116 117 SVGDiagram diagram = svgUniverse.getDiagram(svgURI); 118 if (diagram == null) 119 { 120 return 0; 121 } 122 return (int)diagram.getWidth(); 123 } 124 125 /** 126 * Draws the icon to the specified component. 127 * @param comp - Component to draw icon to. This is ignored by SVGIcon, and can be set to null; only gg is used for drawing the icon 128 * @param gg - Graphics context to render SVG content to 129 * @param x - X coordinate to draw icon 130 * @param y - Y coordinate to draw icon 131 */ 132 public void paintIcon(Component comp, Graphics gg, int x, int y) 133 { 134 //Copy graphics object so that 135 Graphics2D g = (Graphics2D)gg.create(); 136 paintIcon(comp, g, x, y); 137 g.dispose(); 138 } 139 140 private void paintIcon(Component comp, Graphics2D g, int x, int y) 141 { 142 Object oldAliasHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 143 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF); 144 145 Object oldInterpolationHint = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION); 146 switch (interpolation) 147 { 148 case INTERP_NEAREST_NEIGHBOR: 149 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); 150 break; 151 case INTERP_BILINEAR: 152 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 153 break; 154 case INTERP_BICUBIC: 155 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); 156 break; 157 } 158 159 160 SVGDiagram diagram = svgUniverse.getDiagram(svgURI); 161 if (diagram == null) 162 { 163 return; 164 } 165 166 g.translate(x, y); 167 diagram.setIgnoringClipHeuristic(!clipToViewbox); 168 if (clipToViewbox) 169 { 170 g.setClip(new Rectangle2D.Float(0, 0, diagram.getWidth(), diagram.getHeight())); 171 } 172 173 174 175 if (!scaleToFit) 176 { 177 try 178 { 179 diagram.render(g); 180 g.translate(-x, -y); 181 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAliasHint); 182 } 183 catch (Exception e) 184 { 185 throw new RuntimeException(e); 186 } 187 return; 188 } 189 190 final int width = getIconWidth(); 191 final int height = getIconHeight(); 192// int width = getWidth(); 193// int height = getHeight(); 194 195 if (width == 0 || height == 0) 196 { 197 return; 198 } 199 200// if (width == 0 || height == 0) 201// { 202// //Chances are we're rendering offscreen 203// Dimension dim = getSize(); 204// width = dim.width; 205// height = dim.height; 206// return; 207// } 208 209// g.setClip(0, 0, width, height); 210 211 212 final Rectangle2D.Double rect = new Rectangle2D.Double(); 213 diagram.getViewRect(rect); 214 215 scaleXform.setToScale(width / rect.width, height / rect.height); 216 217 AffineTransform oldXform = g.getTransform(); 218 g.transform(scaleXform); 219 220 try 221 { 222 diagram.render(g); 223 } 224 catch (SVGException e) 225 { 226 throw new RuntimeException(e); 227 } 228 229 g.setTransform(oldXform); 230 231 232 g.translate(-x, -y); 233 234 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAliasHint); 235 if (oldInterpolationHint != null) 236 { 237 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, oldInterpolationHint); 238 } 239 } 240 241 /** 242 * @return the universe this icon draws it's SVGDiagrams from 243 */ 244 public SVGUniverse getSvgUniverse() 245 { 246 return svgUniverse; 247 } 248 249 public void setSvgUniverse(SVGUniverse svgUniverse) 250 { 251 SVGUniverse old = this.svgUniverse; 252 this.svgUniverse = svgUniverse; 253 changes.firePropertyChange("svgUniverse", old, svgUniverse); 254 } 255 256 /** 257 * @return the uni of the document being displayed by this icon 258 */ 259 public URI getSvgURI() 260 { 261 return svgURI; 262 } 263 264 /** 265 * Loads an SVG document from a URI. 266 * @param svgURI - URI to load document from 267 */ 268 public void setSvgURI(URI svgURI) 269 { 270 URI old = this.svgURI; 271 this.svgURI = svgURI; 272 273 SVGDiagram diagram = svgUniverse.getDiagram(svgURI); 274 if (diagram != null) 275 { 276 Dimension size = getPreferredSize(); 277 if (size == null) 278 { 279 size = new Dimension((int)diagram.getRoot().getDeviceWidth(), (int)diagram.getRoot().getDeviceHeight()); 280 } 281 diagram.setDeviceViewport(new Rectangle(0, 0, size.width, size.height)); 282 } 283 284 changes.firePropertyChange("svgURI", old, svgURI); 285 } 286 287 /** 288 * Loads an SVG document from the classpath. This function is equivilant to 289 * setSvgURI(new URI(getClass().getResource(resourcePath).toString()); 290 * @param resourcePath - resource to load 291 */ 292 public void setSvgResourcePath(String resourcePath) 293 { 294 URI old = this.svgURI; 295 296 try 297 { 298 svgURI = new URI(getClass().getResource(resourcePath).toString()); 299 changes.firePropertyChange("svgURI", old, svgURI); 300 301 SVGDiagram diagram = svgUniverse.getDiagram(svgURI); 302 if (diagram != null) 303 { 304 diagram.setDeviceViewport(new Rectangle(0, 0, preferredSize.width, preferredSize.height)); 305 } 306 307 } 308 catch (Exception e) 309 { 310 svgURI = old; 311 } 312 } 313 314 /** 315 * If this SVG document has a viewbox, if scaleToFit is set, will scale the viewbox to match the 316 * preferred size of this icon 317 */ 318 public boolean isScaleToFit() 319 { 320 return scaleToFit; 321 } 322 323 public void setScaleToFit(boolean scaleToFit) 324 { 325 boolean old = this.scaleToFit; 326 this.scaleToFit = scaleToFit; 327 changes.firePropertyChange("scaleToFit", old, scaleToFit); 328 } 329 330 public Dimension getPreferredSize() 331 { 332 if (preferredSize == null) 333 { 334 SVGDiagram diagram = svgUniverse.getDiagram(svgURI); 335 if (diagram != null) 336 { 337 //preferredSize = new Dimension((int)diagram.getWidth(), (int)diagram.getHeight()); 338 setPreferredSize(new Dimension((int)diagram.getWidth(), (int)diagram.getHeight())); 339 } 340 } 341 342 return new Dimension(preferredSize); 343 } 344 345 public void setPreferredSize(Dimension preferredSize) 346 { 347 Dimension old = this.preferredSize; 348 this.preferredSize = preferredSize; 349 350 SVGDiagram diagram = svgUniverse.getDiagram(svgURI); 351 if (diagram != null) 352 { 353 diagram.setDeviceViewport(new Rectangle(0, 0, preferredSize.width, preferredSize.height)); 354 } 355 356 changes.firePropertyChange("preferredSize", old, preferredSize); 357 } 358 359 360 /** 361 * @return true if antiAliasing is turned on. 362 * @deprecated 363 */ 364 public boolean getUseAntiAlias() 365 { 366 return getAntiAlias(); 367 } 368 369 /** 370 * @param antiAlias true to use antiAliasing. 371 * @deprecated 372 */ 373 public void setUseAntiAlias(boolean antiAlias) 374 { 375 setAntiAlias(antiAlias); 376 } 377 378 /** 379 * @return true if antiAliasing is turned on. 380 */ 381 public boolean getAntiAlias() 382 { 383 return antiAlias; 384 } 385 386 /** 387 * @param antiAlias true to use antiAliasing. 388 */ 389 public void setAntiAlias(boolean antiAlias) 390 { 391 boolean old = this.antiAlias; 392 this.antiAlias = antiAlias; 393 changes.firePropertyChange("antiAlias", old, antiAlias); 394 } 395 396 /** 397 * @return interpolation used in rescaling images 398 */ 399 public int getInterpolation() 400 { 401 return interpolation; 402 } 403 404 /** 405 * @param interpolation Interpolation value used in rescaling images. 406 * Should be one of 407 * INTERP_NEAREST_NEIGHBOR - Fastest, one pixel resampling, poor quality 408 * INTERP_BILINEAR - four pixel resampling 409 * INTERP_BICUBIC - Slowest, nine pixel resampling, best quality 410 */ 411 public void setInterpolation(int interpolation) 412 { 413 int old = this.interpolation; 414 this.interpolation = interpolation; 415 changes.firePropertyChange("interpolation", old, interpolation); 416 } 417 418 /** 419 * clipToViewbox will set a clip box equivilant to the SVG's viewbox before 420 * rendering. 421 */ 422 public boolean isClipToViewbox() 423 { 424 return clipToViewbox; 425 } 426 427 public void setClipToViewbox(boolean clipToViewbox) 428 { 429 this.clipToViewbox = clipToViewbox; 430 } 431 432}