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