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 java.net.*;
031    import java.util.*;
032    import java.awt.geom.*;
033    import java.awt.*;
034    
035    import com.kitfox.svg.xml.*;
036    
037    /**
038     * @author Mark McKay
039     * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
040     */
041    abstract public class Gradient extends FillElement
042    {
043    
044        public static final int SM_PAD = 0;
045        public static final int SM_REPEAT = 1;
046        public static final int SM_REFLECT = 2;
047    
048        int spreadMethod = SM_PAD;
049    
050        public static final int GU_OBJECT_BOUNDING_BOX = 0;
051        public static final int GU_USER_SPACE_ON_USE = 1;
052    
053        protected int gradientUnits = GU_OBJECT_BOUNDING_BOX;
054    
055        //Either this gradient contains a list of stops, or it will take it's
056        // stops from the referenced gradient
057        ArrayList stops = new ArrayList();
058        URI stopRef = null;
059    //    Gradient stopRef = null;
060    
061        protected AffineTransform gradientTransform = null;
062    
063        //Cache arrays of stop values here
064        float[] stopFractions;
065        Color[] stopColors;
066    
067        /** Creates a new instance of Gradient */
068        public Gradient() {
069        }
070        
071        /**
072         * Called after the start element but before the end element to indicate
073         * each child tag that has been processed
074         */
075        public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException
076        {
077            super.loaderAddChild(helper, child);
078    
079            if (!(child instanceof Stop)) return;
080            appendStop((Stop)child);
081        }
082    
083        protected void build() throws SVGException
084        {
085            super.build();
086            
087            StyleAttribute sty = new StyleAttribute();
088            String strn;
089            
090            if (getPres(sty.setName("spreadMethod")))
091            {
092                strn = sty.getStringValue().toLowerCase();
093                if (strn.equals("repeat")) spreadMethod = SM_REPEAT;
094                else if (strn.equals("reflect")) spreadMethod = SM_REFLECT;
095                else spreadMethod = SM_PAD;
096            }
097    
098            if (getPres(sty.setName("gradientUnits")))
099            {
100                strn = sty.getStringValue().toLowerCase();
101                if (strn.equals("userspaceonuse")) gradientUnits = GU_USER_SPACE_ON_USE;
102                else gradientUnits = GU_OBJECT_BOUNDING_BOX;
103            }
104    
105            if (getPres(sty.setName("gradientTransform"))) gradientTransform = parseTransform(sty.getStringValue());
106            //If we still don't have one, set it to identity
107            if (gradientTransform == null) gradientTransform = new AffineTransform();
108    
109            
110            //Check to see if we're using our own stops or referencing someone else's
111            if (getPres(sty.setName("xlink:href")))
112            {
113                try {
114                    stopRef = sty.getURIValue(getXMLBase());
115    //System.err.println("Gradient: " + sty.getStringValue() + ", " + getXMLBase() + ", " + src);
116    //                URI src = getXMLBase().resolve(href);
117    //                stopRef = (Gradient)diagram.getUniverse().getElement(src);
118                }
119                catch (Exception e)
120                {
121                    throw new SVGException("Could not resolve relative URL in Gradient: " + sty.getStringValue() + ", " + getXMLBase(), e);
122                }
123            }
124        }
125        
126        public float[] getStopFractions()
127        {
128            if (stopRef != null)
129            {
130                Gradient grad = (Gradient)diagram.getUniverse().getElement(stopRef);
131                return grad.getStopFractions();
132            }
133    
134            if (stopFractions != null) return stopFractions;
135    
136            stopFractions = new float[stops.size()];
137            int idx = 0;
138            for (Iterator it = stops.iterator(); it.hasNext();)
139            {
140                Stop stop = (Stop)it.next();
141                float val = stop.offset;
142                if (idx != 0 && val < stopFractions[idx - 1]) val = stopFractions[idx - 1];
143                stopFractions[idx++] = val;
144            }
145    
146            return stopFractions;
147        }
148    
149        public Color[] getStopColors()
150        {
151            if (stopRef != null)
152            {
153                Gradient grad = (Gradient)diagram.getUniverse().getElement(stopRef);
154                return grad.getStopColors();
155            }
156    
157            if (stopColors != null) return stopColors;
158    
159            stopColors = new Color[stops.size()];
160            int idx = 0;
161            for (Iterator it = stops.iterator(); it.hasNext();)
162            {
163                Stop stop = (Stop)it.next();
164                int stopColorVal = stop.color.getRGB();
165                Color stopColor = new Color((stopColorVal >> 16) & 0xff, (stopColorVal >> 8) & 0xff, stopColorVal & 0xff, clamp((int)(stop.opacity * 255), 0, 255));
166                stopColors[idx++] = stopColor;
167            }
168    
169            return stopColors;
170        }
171        
172        public void setStops(Color[] colors, float[] fractions)
173        {
174            if (colors.length != fractions.length)
175            {
176                throw new IllegalArgumentException();
177            }
178            
179            this.stopColors = colors;
180            this.stopFractions = fractions;
181            stopRef = null;
182        }
183        
184        private int clamp(int val, int min, int max)
185        {
186            if (val < min) return min;
187            if (val > max) return max;
188            return val;
189        }
190        
191        public void setStopRef(URI grad)
192        {
193            stopRef = grad;
194        }
195    
196        public void appendStop(Stop stop)
197        {
198            stops.add(stop);
199        }
200    
201        /**
202         * Updates all attributes in this diagram associated with a time event.
203         * Ie, all attributes with track information.
204         * @return - true if this node has changed state as a result of the time
205         * update
206         */
207        public boolean updateTime(double curTime) throws SVGException
208        {
209    //        if (trackManager.getNumTracks() == 0) return false;
210            boolean stateChange = false;
211    
212            //Get current values for parameters
213            StyleAttribute sty = new StyleAttribute();
214            boolean shapeChange = false;
215            String strn;
216            
217    
218            if (getPres(sty.setName("spreadMethod")))
219            {
220                int newVal;
221                strn = sty.getStringValue().toLowerCase();
222                if (strn.equals("repeat")) newVal = SM_REPEAT;
223                else if (strn.equals("reflect")) newVal = SM_REFLECT;
224                else newVal = SM_PAD;
225                if (spreadMethod != newVal)
226                {
227                    spreadMethod = newVal;
228                    stateChange = true;
229                }
230            }
231            
232            if (getPres(sty.setName("gradientUnits")))
233            {
234                int newVal;
235                strn = sty.getStringValue().toLowerCase();
236                if (strn.equals("userspaceonuse")) newVal = GU_USER_SPACE_ON_USE;
237                else newVal = GU_OBJECT_BOUNDING_BOX;
238                if (newVal != gradientUnits)
239                {
240                    gradientUnits = newVal;
241                    stateChange = true;
242                }
243            }
244    
245            if (getPres(sty.setName("gradientTransform")))
246            {
247                AffineTransform newVal = parseTransform(sty.getStringValue());
248                if (newVal != null && newVal.equals(gradientTransform))
249                {
250                    gradientTransform = newVal;
251                    stateChange = true;
252                }
253            }
254    
255            
256            //Check to see if we're using our own stops or referencing someone else's
257            if (getPres(sty.setName("xlink:href")))
258            {
259                try {
260                    URI newVal = sty.getURIValue(getXMLBase());
261                    if ((newVal == null && stopRef != null) || !newVal.equals(stopRef))
262                    {
263                        stopRef = newVal;
264                        stateChange = true;
265                    }
266                }
267                catch (Exception e)
268                {
269                    e.printStackTrace();
270                }
271            }
272            
273            //Check stops, if any
274            for (Iterator it = stops.iterator(); it.hasNext();)
275            {
276                Stop stop = (Stop)it.next();
277                if (stop.updateTime(curTime))
278                {
279                    stateChange = true;
280                    stopFractions = null;
281                    stopColors = null;
282                }
283            }
284            
285            return stateChange;
286        }
287    
288    }