001    /*
002     * Gradient.java
003     *
004     *
005     *  The Salamander Project - 2D and 3D graphics libraries in Java
006     *  Copyright (C) 2004 Mark McKay
007     *
008     *  This library is free software; you can redistribute it and/or
009     *  modify it under the terms of the GNU Lesser General Public
010     *  License as published by the Free Software Foundation; either
011     *  version 2.1 of the License, or (at your option) any later version.
012     *
013     *  This library is distributed in the hope that it will be useful,
014     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
015     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016     *  Lesser General Public License for more details.
017     *
018     *  You should have received a copy of the GNU Lesser General Public
019     *  License along with this library; if not, write to the Free Software
020     *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     *
022     *  Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
023     *  projects can be found at http://www.kitfox.com
024     *
025     * Created on January 26, 2004, 3:25 AM
026     */
027    
028    package com.kitfox.svg;
029    
030    import com.kitfox.svg.xml.StyleAttribute;
031    import java.net.*;
032    import java.util.*;
033    import java.awt.geom.*;
034    import java.awt.*;
035    import java.awt.image.*;
036    
037    import com.kitfox.svg.pattern.*;
038    import com.kitfox.svg.xml.*;
039    import org.xml.sax.*;
040    
041    /**
042     * @author Mark McKay
043     * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
044     */
045    public class PatternSVG extends FillElement {
046    
047        public static final int GU_OBJECT_BOUNDING_BOX = 0;
048        public static final int GU_USER_SPACE_ON_USE = 1;
049    
050        int gradientUnits = GU_OBJECT_BOUNDING_BOX;
051    
052        float x;
053        float y;
054        float width;
055        float height;
056    
057        AffineTransform patternXform = new AffineTransform();
058        Rectangle2D.Float viewBox;
059    
060        Paint texPaint;
061    
062        /** Creates a new instance of Gradient */
063        public PatternSVG() {
064        }
065    /*
066        public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent)
067        {
068                    //Load style string
069            super.loaderStartElement(helper, attrs, parent);
070    
071            String href = attrs.getValue("xlink:href");
072            //If we have a link to another pattern, initialize ourselves with it's values
073            if (href != null)
074            {
075    //System.err.println("Gradient.loaderStartElement() href '" + href + "'");
076                try {
077                    URI src = getXMLBase().resolve(href);
078    //                URL url = srcUrl.toURL();
079    //                URL url = new URL(helper.docRoot, href);
080                    PatternSVG patSrc = (PatternSVG)helper.universe.getElement(src);
081    
082                    gradientUnits = patSrc.gradientUnits;
083                    x = patSrc.x;
084                    y = patSrc.y;
085                    width = patSrc.width;
086                    height = patSrc.height;
087                    viewBox = patSrc.viewBox;
088                    patternXform.setTransform(patSrc.patternXform);
089                    members.addAll(patSrc.members);
090                }
091                catch (Exception e)
092                {
093                    e.printStackTrace();
094                }
095            }
096    
097    
098            String gradientUnits = attrs.getValue("gradientUnits");
099    
100            if (gradientUnits != null)
101            {
102                if (gradientUnits.toLowerCase().equals("userspaceonuse")) this.gradientUnits = GU_USER_SPACE_ON_USE;
103                else this.gradientUnits = GU_OBJECT_BOUNDING_BOX;
104            }
105    
106            String patternTransform = attrs.getValue("patternTransform");
107            if (patternTransform != null)
108            {
109                patternXform = parseTransform(patternTransform);
110            }
111    
112            String x = attrs.getValue("x");
113            String y = attrs.getValue("y");
114            String width = attrs.getValue("width");
115            String height = attrs.getValue("height");
116    
117            if (x != null) this.x = XMLParseUtil.parseFloat(x);
118            if (y != null) this.y = XMLParseUtil.parseFloat(y);
119            if (width != null) this.width = XMLParseUtil.parseFloat(width);
120            if (height != null) this.height = XMLParseUtil.parseFloat(height);
121    
122            String viewBoxStrn = attrs.getValue("viewBox");
123            if (viewBoxStrn != null)
124            {
125                float[] dim = XMLParseUtil.parseFloatList(viewBoxStrn);
126                viewBox = new Rectangle2D.Float(dim[0], dim[1], dim[2], dim[3]);
127            }
128        }
129      */  
130        /**
131         * Called after the start element but before the end element to indicate
132         * each child tag that has been processed
133         */
134        public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException
135        {
136            super.loaderAddChild(helper, child);
137    
138    //        members.add(child);
139        }
140        
141        protected void build() throws SVGException
142        {
143            super.build();
144            
145            StyleAttribute sty = new StyleAttribute();
146            
147                    //Load style string
148            String href = null;
149            if (getPres(sty.setName("xlink:href"))) href = sty.getStringValue();
150            //String href = attrs.getValue("xlink:href");
151            //If we have a link to another pattern, initialize ourselves with it's values
152            if (href != null)
153            {
154    //System.err.println("Gradient.loaderStartElement() href '" + href + "'");
155                try {
156                    URI src = getXMLBase().resolve(href);
157                    PatternSVG patSrc = (PatternSVG)diagram.getUniverse().getElement(src);
158    
159                    gradientUnits = patSrc.gradientUnits;
160                    x = patSrc.x;
161                    y = patSrc.y;
162                    width = patSrc.width;
163                    height = patSrc.height;
164                    viewBox = patSrc.viewBox;
165                    patternXform.setTransform(patSrc.patternXform);
166                    children.addAll(patSrc.children);
167                }
168                catch (Exception e)
169                {
170                    e.printStackTrace();
171                }
172            }
173    
174            String gradientUnits = "";
175            if (getPres(sty.setName("gradientUnits"))) gradientUnits = sty.getStringValue().toLowerCase();
176            if (gradientUnits.equals("userspaceonuse")) this.gradientUnits = GU_USER_SPACE_ON_USE;
177            else this.gradientUnits = GU_OBJECT_BOUNDING_BOX;
178    
179            String patternTransform = "";
180            if (getPres(sty.setName("patternTransform"))) patternTransform = sty.getStringValue();
181            patternXform = parseTransform(patternTransform);
182    
183            
184            if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits();
185            
186            if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits();
187            
188            if (getPres(sty.setName("width"))) width = sty.getFloatValueWithUnits();
189            
190            if (getPres(sty.setName("height"))) height = sty.getFloatValueWithUnits();
191            
192            if (getPres(sty.setName("viewBox")))
193            {
194                float[] dim = sty.getFloatList();
195                viewBox = new Rectangle2D.Float(dim[0], dim[1], dim[2], dim[3]);
196            }
197                
198            preparePattern();
199        }
200        
201    /*
202        public void loaderEndElement(SVGLoaderHelper helper)
203        {
204            build();
205        }
206        */
207    
208        protected void preparePattern() throws SVGException
209        {
210            //For now, treat all fills as UserSpaceOnUse.  Otherwise, we'll need
211            // a different paint for every object.
212            int tileWidth = (int)width;
213            int tileHeight = (int)height;
214    
215            float stretchX = 1f, stretchY = 1f;
216            if (!patternXform.isIdentity())
217            {
218                //Scale our source tile so that we can have nice sampling from it.
219                float xlateX = (float)patternXform.getTranslateX();
220                float xlateY = (float)patternXform.getTranslateY();
221    
222                Point2D.Float pt = new Point2D.Float(), pt2 = new Point2D.Float();
223    
224                pt.setLocation(width, 0);
225                patternXform.transform(pt, pt2);
226                pt2.x -= xlateX;
227                pt2.y -= xlateY;
228                stretchX = (float)Math.sqrt(pt2.x * pt2.x + pt2.y * pt2.y) * 1.5f / width;
229    
230                pt.setLocation(height, 0);
231                patternXform.transform(pt, pt2);
232                pt2.x -= xlateX;
233                pt2.y -= xlateY;
234                stretchY = (float)Math.sqrt(pt2.x * pt2.x + pt2.y * pt2.y) * 1.5f / height;
235    
236                tileWidth *= stretchX;
237                tileHeight *= stretchY;
238            }
239    
240            if (tileWidth == 0 || tileHeight == 0) 
241            {
242                //Use defaults if tile has degenerate size
243                return;
244            }
245            
246            BufferedImage buf = new BufferedImage(tileWidth, tileHeight, BufferedImage.TYPE_INT_ARGB);
247            Graphics2D g = buf.createGraphics();
248            g.setClip(0, 0, tileWidth, tileHeight);
249            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
250    
251            for (Iterator it = children.iterator(); it.hasNext();)
252            {
253                SVGElement ele = (SVGElement)it.next();
254                if (ele instanceof RenderableElement)
255                {
256                    AffineTransform xform = new AffineTransform();
257    
258                    if (viewBox == null)
259                    {
260                        xform.translate(-x, -y);
261                    }
262                    else
263                    {
264                        xform.scale(tileWidth / viewBox.width, tileHeight / viewBox.height);
265                        xform.translate(-viewBox.x, -viewBox.y);
266                    }
267    
268                    g.setTransform(xform);
269                    ((RenderableElement)ele).render(g);
270                }
271            }
272    
273            g.dispose();
274    
275    //try {
276    //javax.imageio.ImageIO.write(buf, "png", new java.io.File("c:\\tmp\\texPaint.png"));
277    //} catch (Exception e ) {}
278    
279            if (patternXform.isIdentity())
280            {
281                texPaint = new TexturePaint(buf, new Rectangle2D.Float(x, y, width, height));
282            }
283            else
284            {
285                patternXform.scale(1 / stretchX, 1 / stretchY);
286                texPaint = new PatternPaint(buf, patternXform);
287            }
288        }
289    
290        public Paint getPaint(Rectangle2D bounds, AffineTransform xform)
291        {
292            return texPaint;
293        }
294    
295        /**
296         * Updates all attributes in this diagram associated with a time event.
297         * Ie, all attributes with track information.
298         * @return - true if this node has changed state as a result of the time
299         * update
300         */
301        public boolean updateTime(double curTime) throws SVGException
302        {
303            //Patterns don't change state
304            return false;
305        }
306    }