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