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                URI src = sty.getURIValue(getXMLBase());
117                if ("data".equals(src.getScheme()))
118                {
119                    imageSrc = new URL(null, src.toASCIIString(), new Handler());
120                }
121                else if (!diagram.getUniverse().isImageDataInlineOnly())
122                {
123                    try
124                    {
125                        imageSrc = src.toURL();
126                    } catch (Exception e)
127                    {
128                        Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
129                            "Could not parse xlink:href " + src, e);
130                        imageSrc = null;
131                    }
132                }
133            }
134        } catch (Exception e)
135        {
136            throw new SVGException(e);
137        }
138
139        if (imageSrc != null)
140        {
141            diagram.getUniverse().registerImage(imageSrc);
142
143            //Set widths if not set
144            BufferedImage img = diagram.getUniverse().getImage(imageSrc);
145            if (img == null)
146            {
147                xform = new AffineTransform();
148                bounds = new Rectangle2D.Float();
149                return;
150            }
151
152            if (width == 0)
153            {
154                width = img.getWidth();
155            }
156            if (height == 0)
157            {
158                height = img.getHeight();
159            }
160
161            //Determine image xform
162            xform = new AffineTransform();
163            xform.translate(this.x, this.y);
164            xform.scale(this.width / img.getWidth(), this.height / img.getHeight());
165        }
166
167        bounds = new Rectangle2D.Float(this.x, this.y, this.width, this.height);
168    }
169
170    public float getX()
171    {
172        return x;
173    }
174
175    public float getY()
176    {
177        return y;
178    }
179
180    public float getWidth()
181    {
182        return width;
183    }
184
185    public float getHeight()
186    {
187        return height;
188    }
189
190    @Override
191    void pick(Point2D point, boolean boundingBox, List<List<SVGElement>> retVec) throws SVGException
192    {
193        if (getBoundingBox().contains(point))
194        {
195            retVec.add(getPath(null));
196        }
197    }
198
199    @Override
200    void pick(Rectangle2D pickArea, AffineTransform ltw, boolean boundingBox, List<List<SVGElement>> retVec) throws SVGException
201    {
202        if (ltw.createTransformedShape(getBoundingBox()).intersects(pickArea))
203        {
204            retVec.add(getPath(null));
205        }
206    }
207
208    @Override
209    public void render(Graphics2D g) throws SVGException
210    {
211        StyleAttribute styleAttrib = new StyleAttribute();
212        if (getStyle(styleAttrib.setName("visibility")))
213        {
214            if (!styleAttrib.getStringValue().equals("visible"))
215            {
216                return;
217            }
218        }
219
220        if (getStyle(styleAttrib.setName("display")))
221        {
222            if (styleAttrib.getStringValue().equals("none"))
223            {
224                return;
225            }
226        }
227
228        beginLayer(g);
229
230        float opacity = 1f;
231        if (getStyle(styleAttrib.setName("opacity")))
232        {
233            opacity = styleAttrib.getRatioValue();
234        }
235
236        if (opacity <= 0)
237        {
238            return;
239        }
240
241        Composite oldComp = null;
242
243        if (opacity < 1)
244        {
245            oldComp = g.getComposite();
246            Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
247            g.setComposite(comp);
248        }
249
250        BufferedImage img = diagram.getUniverse().getImage(imageSrc);
251        if (img == null)
252        {
253            return;
254        }
255
256        AffineTransform curXform = g.getTransform();
257        g.transform(xform);
258
259        g.drawImage(img, 0, 0, null);
260
261        g.setTransform(curXform);
262        if (oldComp != null)
263        {
264            g.setComposite(oldComp);
265        }
266
267        finishLayer(g);
268    }
269
270    @Override
271    public Rectangle2D getBoundingBox()
272    {
273        return boundsToParent(bounds);
274    }
275
276    /**
277     * Updates all attributes in this diagram associated with a time event. Ie,
278     * all attributes with track information.
279     *
280     * @return - true if this node has changed state as a result of the time
281     * update
282     */
283    @Override
284    public boolean updateTime(double curTime) throws SVGException
285    {
286//        if (trackManager.getNumTracks() == 0) return false;
287        boolean changeState = super.updateTime(curTime);
288
289        //Get current values for parameters
290        StyleAttribute sty = new StyleAttribute();
291        boolean shapeChange = false;
292
293        if (getPres(sty.setName("x")))
294        {
295            float newVal = sty.getFloatValueWithUnits();
296            if (newVal != x)
297            {
298                x = newVal;
299                shapeChange = true;
300            }
301        }
302
303        if (getPres(sty.setName("y")))
304        {
305            float newVal = sty.getFloatValueWithUnits();
306            if (newVal != y)
307            {
308                y = newVal;
309                shapeChange = true;
310            }
311        }
312
313        if (getPres(sty.setName("width")))
314        {
315            float newVal = sty.getFloatValueWithUnits();
316            if (newVal != width)
317            {
318                width = newVal;
319                shapeChange = true;
320            }
321        }
322
323        if (getPres(sty.setName("height")))
324        {
325            float newVal = sty.getFloatValueWithUnits();
326            if (newVal != height)
327            {
328                height = newVal;
329                shapeChange = true;
330            }
331        }
332
333        try
334        {
335            if (getPres(sty.setName("xlink:href")))
336            {
337                URI src = sty.getURIValue(getXMLBase());
338
339                URL newVal = null;
340                if ("data".equals(src.getScheme()))
341                {
342                    newVal = new URL(null, src.toASCIIString(), new Handler());
343                } else if (!diagram.getUniverse().isImageDataInlineOnly())
344                {
345                    newVal = src.toURL();
346                }
347
348                if (newVal != null && !newVal.equals(imageSrc))
349                {
350                    imageSrc = newVal;
351                    shapeChange = true;
352                }
353            }
354        } catch (IllegalArgumentException ie)
355        {
356            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
357                "Image provided with illegal value for href: \""
358                + sty.getStringValue() + '"', ie);
359        } catch (Exception e)
360        {
361            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
362                "Could not parse xlink:href", e);
363        }
364
365
366        if (shapeChange)
367        {
368            build();
369//            diagram.getUniverse().registerImage(imageSrc);
370//
371//            //Set widths if not set
372//            BufferedImage img = diagram.getUniverse().getImage(imageSrc);
373//            if (img == null)
374//            {
375//                xform = new AffineTransform();
376//                bounds = new Rectangle2D.Float();
377//            }
378//            else
379//            {
380//                if (width == 0) width = img.getWidth();
381//                if (height == 0) height = img.getHeight();
382//
383//                //Determine image xform
384//                xform = new AffineTransform();
385////                xform.setToScale(this.width / img.getWidth(), this.height / img.getHeight());
386////                xform.translate(this.x, this.y);
387//                xform.translate(this.x, this.y);
388//                xform.scale(this.width / img.getWidth(), this.height / img.getHeight());
389//
390//                bounds = new Rectangle2D.Float(this.x, this.y, this.width, this.height);
391//            }
392//
393//            return true;
394        }
395
396        return changeState || shapeChange;
397    }
398}