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    }