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 February 20, 2004, 10:00 PM
035 */
036package com.kitfox.svg;
037
038import com.kitfox.svg.app.data.Handler;
039import com.kitfox.svg.xml.StyleAttribute;
040import java.awt.AlphaComposite;
041import java.awt.Composite;
042import java.awt.Graphics2D;
043import java.awt.geom.AffineTransform;
044import java.awt.geom.Point2D;
045import java.awt.geom.Rectangle2D;
046import java.awt.image.BufferedImage;
047import java.net.URI;
048import java.net.URL;
049import java.util.List;
050import java.util.logging.Level;
051import java.util.logging.Logger;
052
053/**
054 * Implements an image.
055 *
056 * @author Mark McKay
057 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
058 */
059public class ImageSVG extends RenderableElement
060{
061    public static final String TAG_NAME = "image";
062    
063    float x = 0f;
064    float y = 0f;
065    float width = 0f;
066    float height = 0f;
067//    BufferedImage href = null;
068    URL imageSrc = null;
069    AffineTransform xform;
070    Rectangle2D bounds;
071
072    /**
073     * Creates a new instance of Font
074     */
075    public ImageSVG()
076    {
077    }
078
079    @Override
080    public String getTagName()
081    {
082        return TAG_NAME;
083    }
084
085    @Override
086    protected void build() throws SVGException
087    {
088        super.build();
089
090        StyleAttribute sty = new StyleAttribute();
091
092        if (getPres(sty.setName("x")))
093        {
094            x = sty.getFloatValueWithUnits();
095        }
096
097        if (getPres(sty.setName("y")))
098        {
099            y = sty.getFloatValueWithUnits();
100        }
101
102        if (getPres(sty.setName("width")))
103        {
104            width = sty.getFloatValueWithUnits();
105        }
106
107        if (getPres(sty.setName("height")))
108        {
109            height = sty.getFloatValueWithUnits();
110        }
111
112        try
113        {
114            if (getPres(sty.setName("xlink:href")))
115            {
116                
117                
118                URI src = sty.getURIValue(getXMLBase());
119                if ("data".equals(src.getScheme()))
120                {
121                    imageSrc = new URL(null, src.toASCIIString(), new Handler());
122                }
123                else 
124                {
125                    
126                    if (!diagram.getUniverse().isImageDataInlineOnly())
127                    {
128                        try
129                        {
130                            imageSrc = src.toURL();
131                        } catch (Exception e)
132                        {
133                            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
134                                "Could not parse xlink:href " + src, e);
135    //                        e.printStackTrace();
136                            imageSrc = null;
137                        }
138                    }
139                }
140            }
141        } catch (Exception e)
142        {
143            throw new SVGException(e);
144        }
145
146        diagram.getUniverse().registerImage(imageSrc);
147
148        //Set widths if not set
149        BufferedImage img = diagram.getUniverse().getImage(imageSrc);
150        if (img == null)
151        {
152            xform = new AffineTransform();
153            bounds = new Rectangle2D.Float();
154            return;
155        }
156
157        if (width == 0)
158        {
159            width = img.getWidth();
160        }
161        if (height == 0)
162        {
163            height = img.getHeight();
164        }
165
166        //Determine image xform
167        xform = new AffineTransform();
168//        xform.setToScale(this.width / img.getWidth(), this.height / img.getHeight());
169//        xform.translate(this.x, this.y);
170        xform.translate(this.x, this.y);
171        xform.scale(this.width / img.getWidth(), this.height / img.getHeight());
172
173        bounds = new Rectangle2D.Float(this.x, this.y, this.width, this.height);
174    }
175
176    public float getX()
177    {
178        return x;
179    }
180
181    public float getY()
182    {
183        return y;
184    }
185
186    public float getWidth()
187    {
188        return width;
189    }
190
191    public float getHeight()
192    {
193        return height;
194    }
195
196    @Override
197    void pick(Point2D point, boolean boundingBox, List<List<SVGElement>> retVec) throws SVGException
198    {
199        if (getBoundingBox().contains(point))
200        {
201            retVec.add(getPath(null));
202        }
203    }
204
205    @Override
206    void pick(Rectangle2D pickArea, AffineTransform ltw, boolean boundingBox, List<List<SVGElement>> retVec) throws SVGException
207    {
208        if (ltw.createTransformedShape(getBoundingBox()).intersects(pickArea))
209        {
210            retVec.add(getPath(null));
211        }
212    }
213
214    @Override
215    public void render(Graphics2D g) throws SVGException
216    {
217        StyleAttribute styleAttrib = new StyleAttribute();
218        if (getStyle(styleAttrib.setName("visibility")))
219        {
220            if (!styleAttrib.getStringValue().equals("visible"))
221            {
222                return;
223            }
224        }
225
226        if (getStyle(styleAttrib.setName("display")))
227        {
228            if (styleAttrib.getStringValue().equals("none"))
229            {
230                return;
231            }
232        }
233
234        beginLayer(g);
235
236        float opacity = 1f;
237        if (getStyle(styleAttrib.setName("opacity")))
238        {
239            opacity = styleAttrib.getRatioValue();
240        }
241
242        if (opacity <= 0)
243        {
244            return;
245        }
246
247        Composite oldComp = null;
248
249        if (opacity < 1)
250        {
251            oldComp = g.getComposite();
252            Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
253            g.setComposite(comp);
254        }
255
256        BufferedImage img = diagram.getUniverse().getImage(imageSrc);
257        if (img == null)
258        {
259            return;
260        }
261
262        AffineTransform curXform = g.getTransform();
263        g.transform(xform);
264
265        g.drawImage(img, 0, 0, null);
266
267        g.setTransform(curXform);
268        if (oldComp != null)
269        {
270            g.setComposite(oldComp);
271        }
272
273        finishLayer(g);
274    }
275
276    @Override
277    public Rectangle2D getBoundingBox()
278    {
279        return boundsToParent(bounds);
280    }
281
282    /**
283     * Updates all attributes in this diagram associated with a time event. Ie,
284     * all attributes with track information.
285     *
286     * @return - true if this node has changed state as a result of the time
287     * update
288     */
289    @Override
290    public boolean updateTime(double curTime) throws SVGException
291    {
292//        if (trackManager.getNumTracks() == 0) return false;
293        boolean changeState = super.updateTime(curTime);
294
295        //Get current values for parameters
296        StyleAttribute sty = new StyleAttribute();
297        boolean shapeChange = false;
298
299        if (getPres(sty.setName("x")))
300        {
301            float newVal = sty.getFloatValueWithUnits();
302            if (newVal != x)
303            {
304                x = newVal;
305                shapeChange = true;
306            }
307        }
308
309        if (getPres(sty.setName("y")))
310        {
311            float newVal = sty.getFloatValueWithUnits();
312            if (newVal != y)
313            {
314                y = newVal;
315                shapeChange = true;
316            }
317        }
318
319        if (getPres(sty.setName("width")))
320        {
321            float newVal = sty.getFloatValueWithUnits();
322            if (newVal != width)
323            {
324                width = newVal;
325                shapeChange = true;
326            }
327        }
328
329        if (getPres(sty.setName("height")))
330        {
331            float newVal = sty.getFloatValueWithUnits();
332            if (newVal != height)
333            {
334                height = newVal;
335                shapeChange = true;
336            }
337        }
338
339        try
340        {
341            if (getPres(sty.setName("xlink:href")))
342            {
343                URI src = sty.getURIValue(getXMLBase());
344
345                URL newVal;
346                if ("data".equals(src.getScheme()))
347                {
348                    newVal = new URL(null, src.toASCIIString(), new Handler());
349                } else
350                {
351                    newVal = src.toURL();
352                }
353
354                if (!newVal.equals(imageSrc))
355                {
356                    imageSrc = newVal;
357                    shapeChange = true;
358                }
359            }
360        } catch (IllegalArgumentException ie)
361        {
362            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
363                "Image provided with illegal value for href: \""
364                + sty.getStringValue() + '"', ie);
365        } catch (Exception e)
366        {
367            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
368                "Could not parse xlink:href", e);
369        }
370
371
372        if (shapeChange)
373        {
374            build();
375//            diagram.getUniverse().registerImage(imageSrc);
376//
377//            //Set widths if not set
378//            BufferedImage img = diagram.getUniverse().getImage(imageSrc);
379//            if (img == null)
380//            {
381//                xform = new AffineTransform();
382//                bounds = new Rectangle2D.Float();
383//            }
384//            else
385//            {
386//                if (width == 0) width = img.getWidth();
387//                if (height == 0) height = img.getHeight();
388//
389//                //Determine image xform
390//                xform = new AffineTransform();
391////                xform.setToScale(this.width / img.getWidth(), this.height / img.getHeight());
392////                xform.translate(this.x, this.y);
393//                xform.translate(this.x, this.y);
394//                xform.scale(this.width / img.getWidth(), this.height / img.getHeight());
395//
396//                bounds = new Rectangle2D.Float(this.x, this.y, this.width, this.height);
397//            }
398//
399//            return true;
400        }
401
402        return changeState || shapeChange;
403    }
404}