source: josm/trunk/src/org/openstreetmap/josm/tools/ImageProvider.java @ 5241

Revision 5056, 29.5 KB checked in by bastiK, 3 months ago (diff)

typo

  • Property svn:eol-style set to native
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.tools;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.Cursor;
8import java.awt.Dimension;
9import java.awt.Graphics;
10import java.awt.Graphics2D;
11import java.awt.GraphicsConfiguration;
12import java.awt.GraphicsEnvironment;
13import java.awt.Image;
14import java.awt.Point;
15import java.awt.RenderingHints;
16import java.awt.Toolkit;
17import java.awt.Transparency;
18import java.awt.image.BufferedImage;
19import java.io.ByteArrayInputStream;
20import java.io.File;
21import java.io.IOException;
22import java.io.InputStream;
23import java.io.StringReader;
24import java.io.UnsupportedEncodingException;
25import java.net.MalformedURLException;
26import java.net.URI;
27import java.net.URL;
28import java.net.URLDecoder;
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Collection;
32import java.util.HashMap;
33import java.util.Map;
34import java.util.concurrent.Executors;
35import java.util.concurrent.ExecutorService;
36import java.util.regex.Matcher;
37import java.util.regex.Pattern;
38import java.util.zip.ZipEntry;
39import java.util.zip.ZipFile;
40
41import javax.imageio.ImageIO;
42import javax.swing.Icon;
43import javax.swing.ImageIcon;
44
45import org.apache.commons.codec.binary.Base64;
46import org.openstreetmap.josm.Main;
47import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
48import org.openstreetmap.josm.io.MirroredInputStream;
49import org.openstreetmap.josm.plugins.PluginHandler;
50import org.xml.sax.Attributes;
51import org.xml.sax.EntityResolver;
52import org.xml.sax.InputSource;
53import org.xml.sax.SAXException;
54import org.xml.sax.XMLReader;
55import org.xml.sax.helpers.DefaultHandler;
56import org.xml.sax.helpers.XMLReaderFactory;
57
58import com.kitfox.svg.SVGDiagram;
59import com.kitfox.svg.SVGException;
60import com.kitfox.svg.SVGUniverse;
61
62/**
63 * Helper class to support the application with images.
64 *
65 * How to use:
66 *
67 * <code>ImageIcon icon = new ImageProvider(name).setMaxWidth(24).setMaxHeight(24).get();</code>
68 * (there are more options, see below)
69 *
70 * short form:
71 * <code>ImageIcon icon = ImageProvider.get(name);</code>
72 *
73 * @author imi
74 */
75public class ImageProvider {
76
77    /**
78     * Position of an overlay icon
79     * @author imi
80     */
81    public static enum OverlayPosition {
82        NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST
83    }
84
85    public static enum ImageType {
86        SVG,    // scalable vector graphics
87        OTHER   // everything else, e.g. png, gif (must be supported by Java)
88    }
89
90    protected Collection<String> dirs;
91    protected String id;
92    protected String subdir;
93    protected String name;
94    protected File archive;
95    protected int width = -1;
96    protected int height = -1;
97    protected int maxWidth = -1;
98    protected int maxHeight = -1;
99    protected boolean optional;
100    protected boolean suppressWarnings;
101    protected Collection<ClassLoader> additionalClassLoaders;
102
103    private static SVGUniverse svgUniverse;
104
105    /**
106     * The icon cache
107     */
108    private static Map<String, ImageResource> cache = new HashMap<String, ImageResource>();
109
110    private final static ExecutorService imageFetcher = Executors.newSingleThreadExecutor();
111
112    public interface ImageCallback {
113        void finished(ImageIcon result);
114    }
115
116    /**
117     * @param subdir    Subdirectory the image lies in.
118     * @param name      The name of the image. If it does not end with '.png' or '.svg',
119     *                  both extensions are tried.
120     */
121    public ImageProvider(String subdir, String name) {
122        this.subdir = subdir;
123        this.name = name;
124    }
125
126    public ImageProvider(String name) {
127        this.name = name;
128    }
129
130    /**
131     * Directories to look for the image.
132     */
133    public ImageProvider setDirs(Collection<String> dirs) {
134        this.dirs = dirs;
135        return this;
136    }
137
138    /**
139     * An id used for caching. Id is not used for cache if name starts with http://. (URL is unique anyway.)
140     */
141    public ImageProvider setId(String id) {
142        this.id = id;
143        return this;
144    }
145
146    /**
147     * A zip file where the image is located.
148     */
149    public ImageProvider setArchive(File archive) {
150        this.archive = archive;
151        return this;
152    }
153
154    /**
155     * The dimensions of the image.
156     *
157     * If not specified, the original size of the image is used.
158     * The width part of the dimension can be -1. Then it will only set the height but
159     * keep the aspect ratio. (And the other way around.)
160     */
161    public ImageProvider setSize(Dimension size) {
162        this.width = size.width;
163        this.height = size.height;
164        return this;
165    }
166
167    /**
168     * see setSize
169     */
170    public ImageProvider setWidth(int width) {
171        this.width = width;
172        return this;
173    }
174
175    /**
176     * see setSize
177     */
178    public ImageProvider setHeight(int height) {
179        this.height = height;
180        return this;
181    }
182
183    /**
184     * The maximum size of the image.
185     *
186     * It will shrink the image if necessary, but keep the aspect ratio.
187     * The given width or height can be -1 which means this direction is not bounded.
188     *
189     * 'size' and 'maxSize' are not compatible, you should set only one of them.
190     */
191    public ImageProvider setMaxSize(Dimension maxSize) {
192        this.maxWidth = maxSize.width;
193        this.maxHeight = maxSize.height;
194        return this;
195    }
196
197    /**
198     * see setMaxSize
199     */
200    public ImageProvider setMaxWidth(int maxWidth) {
201        this.maxWidth = maxWidth;
202        return this;
203    }
204
205    /**
206     * see setMaxSize
207     */
208    public ImageProvider setMaxHeight(int maxHeight) {
209        this.maxHeight = maxHeight;
210        return this;
211    }
212
213    /**
214     * The image URL comes from user data and the image may be missing.
215     *
216     * Set true, if JOSM should *not* throw a RuntimeException in case the image cannot be located.
217     */
218    public ImageProvider setOptional(boolean optional) {
219        this.optional = optional;
220        return this;
221    }
222
223    /**
224     * Suppresses warning on the command line in case the image cannot be found.
225     *
226     * In combination with setOptional(true);
227     */
228    public ImageProvider setSuppressWarnings(boolean suppressWarnings) {
229        this.suppressWarnings = suppressWarnings;
230        return this;
231    }
232
233    /**
234     * Add a collection of additional class loaders to search image for.
235     */
236    public ImageProvider setAdditionalClassLoaders(Collection<ClassLoader> additionalClassLoaders) {
237        this.additionalClassLoaders = additionalClassLoaders;
238        return this;
239    }
240
241    /**
242     * Execute the image request.
243     */
244    public ImageIcon get() {
245        ImageResource ir = getIfAvailableImpl(additionalClassLoaders);
246        if (ir == null) {
247            if (!optional) {
248                String ext = name.indexOf('.') != -1 ? "" : ".???";
249                throw new RuntimeException(tr("Fatal: failed to locate image ''{0}''. This is a serious configuration problem. JOSM will stop working.", name + ext));
250            } else {
251                if (!suppressWarnings) {
252                    System.err.println(tr("Failed to locate image ''{0}''", name));
253                }
254                return null;
255            }
256        }
257        if (maxWidth != -1 || maxHeight != -1)
258            return ir.getImageIconBounded(new Dimension(maxWidth, maxHeight));
259        else
260            return ir.getImageIcon(new Dimension(width, height));
261    }
262
263    /**
264     * Load the image in a background thread.
265     *
266     * This method returns immediately and runs the image request
267     * asynchronously.
268     *
269     * @param callback is called, when the image is ready. This can happen
270     * before the call to getInBackground returns or it may be invoked some
271     * time (seconds) later.
272     * If no image is available, a null value is returned to callback (just
273     * like ImageProvider.get).
274     */
275    public void getInBackground(final ImageCallback callback) {
276        if (name.startsWith("http://") || name.startsWith("wiki://")) {
277            Runnable fetch = new Runnable() {
278                @Override
279                public void run() {
280                    ImageIcon result = get();
281                    callback.finished(result);
282                }
283            };
284            imageFetcher.submit(fetch);
285        } else {
286            ImageIcon result = get();
287            callback.finished(result);
288        }
289    }
290
291    /**
292     * Return an image from the specified location. Throws a RuntimeException if
293     * the image cannot be located.
294     *
295     * @param subdir The position of the directory, e.g. 'layer'
296     * @param name The icons name (with or without '.png' or '.svg' extension)
297     * @return The requested Image.
298     */
299    public static ImageIcon get(String subdir, String name) {
300        return new ImageProvider(subdir, name).get();
301    }
302
303    public static ImageIcon get(String name) {
304        return new ImageProvider(name).get();
305    }
306
307    public static ImageIcon getIfAvailable(String name) {
308        return new ImageProvider(name).setOptional(true).get();
309    }
310
311    public static ImageIcon getIfAvailable(String subdir, String name) {
312        return new ImageProvider(subdir, name).setOptional(true).get();
313    }
314
315    /**
316     * {@code data:[<mediatype>][;base64],<data>}
317     * @see RFC2397
318     */
319    private static final Pattern dataUrlPattern = Pattern.compile(
320            "^data:([a-zA-Z]+/[a-zA-Z+]+)?(;base64)?,(.+)$");
321
322    private ImageResource getIfAvailableImpl(Collection<ClassLoader> additionalClassLoaders) {
323        if (name == null)
324            return null;
325
326        try {
327            if (name.startsWith("data:")) {
328                Matcher m = dataUrlPattern.matcher(name);
329                if (m.matches()) {
330                    String mediatype = m.group(1);
331                    String base64 = m.group(2);
332                    String data = m.group(3);
333                    byte[] bytes = ";base64".equals(base64)
334                            ? Base64.decodeBase64(data)
335                            : URLDecoder.decode(data, "utf-8").getBytes();
336                    if (mediatype != null && mediatype.contains("image/svg+xml")) {
337                        URI uri = getSvgUniverse().loadSVG(new StringReader(new String(bytes)), name);
338                        return new ImageResource(getSvgUniverse().getDiagram(uri));
339                    } else {
340                        try {
341                            return new ImageResource(ImageIO.read(new ByteArrayInputStream(bytes)));
342                        } catch (IOException e) {}
343                    }
344                }
345            }
346        } catch (UnsupportedEncodingException ex) {
347            throw new RuntimeException(ex.getMessage(), ex);
348        } catch (IOException ex) {
349            throw new RuntimeException(ex.getMessage(), ex);
350        }
351
352        ImageType type = name.toLowerCase().endsWith(".svg") ? ImageType.SVG : ImageType.OTHER;
353
354        if (name.startsWith("http://")) {
355            String url = name;
356            ImageResource ir = cache.get(url);
357            if (ir != null) return ir;
358            ir = getIfAvailableHttp(url, type);
359            if (ir != null) {
360                cache.put(url, ir);
361            }
362            return ir;
363        } else if (name.startsWith("wiki://")) {
364            ImageResource ir = cache.get(name);
365            if (ir != null) return ir;
366            ir = getIfAvailableWiki(name, type);
367            if (ir != null) {
368                cache.put(name, ir);
369            }
370            return ir;
371        }
372
373        if (subdir == null) {
374            subdir = "";
375        } else if (!subdir.equals("")) {
376            subdir += "/";
377        }
378        String[] extensions;
379        if (name.indexOf('.') != -1) {
380            extensions = new String[] { "" };
381        } else {
382            extensions = new String[] { ".png", ".svg"};
383        }
384        final int ARCHIVE = 0, LOCAL = 1;
385        for (int place : new Integer[] { ARCHIVE, LOCAL }) {
386            for (String ext : extensions) {
387
388                if (".svg".equals(ext)) {
389                    type = ImageType.SVG;
390                } else if (".png".equals(ext)) {
391                    type = ImageType.OTHER;
392                }
393
394                String full_name = subdir + name + ext;
395                String cache_name = full_name;
396                /* cache separately */
397                if (dirs != null && dirs.size() > 0) {
398                    cache_name = "id:" + id + ":" + full_name;
399                    if(archive != null) {
400                        cache_name += ":" + archive.getName();
401                    }
402                }
403
404                ImageResource ir = cache.get(cache_name);
405                if (ir != null) return ir;
406
407                switch (place) {
408                    case ARCHIVE:
409                        if (archive != null) {
410                            ir = getIfAvailableZip(full_name, archive, type);
411                            if (ir != null) {
412                                cache.put(cache_name, ir);
413                                return ir;
414                            }
415                        }
416                        break;
417                    case LOCAL:
418                        // getImageUrl() does a ton of "stat()" calls and gets expensive
419                        // and redundant when you have a whole ton of objects. So,
420                        // index the cache by the name of the icon we're looking for
421                        // and don't bother to create a URL unless we're actually
422                        // creating the image.
423                        URL path = getImageUrl(full_name, dirs, additionalClassLoaders);
424                        if (path == null) {
425                            continue;
426                        }
427                        ir = getIfAvailableLocalURL(path, type);
428                        if (ir != null) {
429                            cache.put(cache_name, ir);
430                            return ir;
431                        }
432                        break;
433                }
434            }
435        }
436        return null;
437    }
438
439    private static ImageResource getIfAvailableHttp(String url, ImageType type) {
440        try {
441            MirroredInputStream is = new MirroredInputStream(url,
442                    new File(Main.pref.getCacheDirectory(), "images").getPath());
443            switch (type) {
444                case SVG:
445                    URI uri = getSvgUniverse().loadSVG(is, is.getFile().toURI().toURL().toString());
446                    SVGDiagram svg = getSvgUniverse().getDiagram(uri);
447                    return svg == null ? null : new ImageResource(svg);
448                case OTHER:
449                    BufferedImage img = null;
450                    try {
451                        img = ImageIO.read(is.getFile().toURI().toURL());
452                    } catch (IOException e) {}
453                    return img == null ? null : new ImageResource(img);
454                default:
455                    throw new AssertionError();
456            }
457        } catch (IOException e) {
458            return null;
459        }
460    }
461
462    private static ImageResource getIfAvailableWiki(String name, ImageType type) {
463        final Collection<String> defaultBaseUrls = Arrays.asList(
464                "http://wiki.openstreetmap.org/w/images/",
465                "http://upload.wikimedia.org/wikipedia/commons/",
466                "http://wiki.openstreetmap.org/wiki/File:"
467        );
468        final Collection<String> baseUrls = Main.pref.getCollection("image-provider.wiki.urls", defaultBaseUrls);
469
470        final String fn = name.substring(name.lastIndexOf('/') + 1);
471
472        ImageResource result = null;
473        for (String b : baseUrls) {
474            String url;
475            if (b.endsWith(":")) {
476                url = getImgUrlFromWikiInfoPage(b, fn);
477                if (url == null) {
478                    continue;
479                }
480            } else {
481                final String fn_md5 = Utils.md5Hex(fn);
482                url = b + fn_md5.substring(0,1) + "/" + fn_md5.substring(0,2) + "/" + fn;
483            }
484            result = getIfAvailableHttp(url, type);
485            if (result != null) {
486                break;
487            }
488        }
489        return result;
490    }
491
492    private static ImageResource getIfAvailableZip(String full_name, File archive, ImageType type) {
493        ZipFile zipFile = null;
494        try
495        {
496            zipFile = new ZipFile(archive);
497            ZipEntry entry = zipFile.getEntry(full_name);
498            if(entry != null)
499            {
500                int size = (int)entry.getSize();
501                int offs = 0;
502                byte[] buf = new byte[size];
503                InputStream is = null;
504                try {
505                    is = zipFile.getInputStream(entry);
506                    switch (type) {
507                        case SVG:
508                            URI uri = getSvgUniverse().loadSVG(is, full_name);
509                            SVGDiagram svg = getSvgUniverse().getDiagram(uri);
510                            return svg == null ? null : new ImageResource(svg);
511                        case OTHER:
512                            while(size > 0)
513                            {
514                                int l = is.read(buf, offs, size);
515                                offs += l;
516                                size -= l;
517                            }
518                            BufferedImage img = null;
519                            try {
520                                img = ImageIO.read(new ByteArrayInputStream(buf));
521                            } catch (IOException e) {}
522                            return img == null ? null : new ImageResource(img);
523                        default:
524                            throw new AssertionError();
525                    }
526                } finally {
527                    if (is != null) {
528                        is.close();
529                    }
530                }
531            }
532        } catch (Exception e) {
533            System.err.println(tr("Warning: failed to handle zip file ''{0}''. Exception was: {1}", archive.getName(), e.toString()));
534        } finally {
535            if (zipFile != null) {
536                try {
537                    zipFile.close();
538                } catch (IOException ex) {
539                }
540            }
541        }
542        return null;
543    }
544
545    private static ImageResource getIfAvailableLocalURL(URL path, ImageType type) {
546        switch (type) {
547            case SVG:
548                URI uri = getSvgUniverse().loadSVG(path);
549                SVGDiagram svg = getSvgUniverse().getDiagram(uri);
550                return svg == null ? null : new ImageResource(svg);
551            case OTHER:
552                BufferedImage img = null;
553                try {
554                    img = ImageIO.read(path);
555                } catch (IOException e) {}
556                return img == null ? null : new ImageResource(img);
557            default:
558                throw new AssertionError();
559        }
560    }
561
562    private static URL getImageUrl(String path, String name, Collection<ClassLoader> additionalClassLoaders) {
563        if (path != null && path.startsWith("resource://")) {
564            String p = path.substring("resource://".length());
565            Collection<ClassLoader> classLoaders = new ArrayList<ClassLoader>(PluginHandler.getResourceClassLoaders());
566            if (additionalClassLoaders != null) {
567                classLoaders.addAll(additionalClassLoaders);
568            }
569            for (ClassLoader source : classLoaders) {
570                URL res;
571                if ((res = source.getResource(p + name)) != null)
572                    return res;
573            }
574        } else {
575            try {
576                File f = new File(path, name);
577                if (f.exists())
578                    return f.toURI().toURL();
579            } catch (MalformedURLException e) {
580            }
581        }
582        return null;
583    }
584
585    private static URL getImageUrl(String imageName, Collection<String> dirs, Collection<ClassLoader> additionalClassLoaders) {
586        URL u = null;
587
588        // Try passed directories first
589        if (dirs != null) {
590            for (String name : dirs) {
591                try {
592                    u = getImageUrl(name, imageName, additionalClassLoaders);
593                    if (u != null)
594                        return u;
595                } catch (SecurityException e) {
596                    System.out.println(tr(
597                            "Warning: failed to access directory ''{0}'' for security reasons. Exception was: {1}",
598                            name, e.toString()));
599                }
600
601            }
602        }
603        // Try user-preference directory
604        String dir = Main.pref.getPreferencesDir() + "images";
605        try {
606            u = getImageUrl(dir, imageName, additionalClassLoaders);
607            if (u != null)
608                return u;
609        } catch (SecurityException e) {
610            System.out.println(tr(
611                    "Warning: failed to access directory ''{0}'' for security reasons. Exception was: {1}", dir, e
612                    .toString()));
613        }
614
615        // Absolute path?
616        u = getImageUrl(null, imageName, additionalClassLoaders);
617        if (u != null)
618            return u;
619
620        // Try plugins and josm classloader
621        u = getImageUrl("resource://images/", imageName, additionalClassLoaders);
622        if (u != null)
623            return u;
624
625        // Try all other resource directories
626        for (String location : Main.pref.getAllPossiblePreferenceDirs()) {
627            u = getImageUrl(location + "images", imageName, additionalClassLoaders);
628            if (u != null)
629                return u;
630            u = getImageUrl(location, imageName, additionalClassLoaders);
631            if (u != null)
632                return u;
633        }
634
635        return null;
636    }
637
638    /**
639     * Reads the wiki page on a certain file in html format in order to find the real image URL.
640     */
641    private static String getImgUrlFromWikiInfoPage(final String base, final String fn) {
642
643        /** Quit parsing, when a certain condition is met */
644        class SAXReturnException extends SAXException {
645            private String result;
646
647            public SAXReturnException(String result) {
648                this.result = result;
649            }
650
651            public String getResult() {
652                return result;
653            }
654        }
655
656        try {
657            final XMLReader parser = XMLReaderFactory.createXMLReader();
658            parser.setContentHandler(new DefaultHandler() {
659                @Override
660                public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
661                    System.out.println();
662                    if (localName.equalsIgnoreCase("img")) {
663                        String val = atts.getValue("src");
664                        if (val.endsWith(fn))
665                            throw new SAXReturnException(val);  // parsing done, quit early
666                    }
667                }
668            });
669
670            parser.setEntityResolver(new EntityResolver() {
671                public InputSource resolveEntity (String publicId, String systemId) {
672                    return new InputSource(new ByteArrayInputStream(new byte[0]));
673                }
674            });
675
676            parser.parse(new InputSource(new MirroredInputStream(
677                    base + fn,
678                    new File(Main.pref.getPreferencesDir(), "images").toString()
679            )));
680        } catch (SAXReturnException r) {
681            return r.getResult();
682        } catch (Exception e) {
683            System.out.println("INFO: parsing " + base + fn + " failed:\n" + e);
684            return null;
685        }
686        System.out.println("INFO: parsing " + base + fn + " failed: Unexpected content.");
687        return null;
688    }
689
690    public static Cursor getCursor(String name, String overlay) {
691        ImageIcon img = get("cursor", name);
692        if (overlay != null) {
693            img = overlay(img, "cursor/modifier/" + overlay, OverlayPosition.SOUTHEAST);
694        }
695        Cursor c = Toolkit.getDefaultToolkit().createCustomCursor(img.getImage(),
696                name.equals("crosshair") ? new Point(10, 10) : new Point(3, 2), "Cursor");
697        return c;
698    }
699
700    /**
701     * @return an icon that represent the overlay of the two given icons. The second icon is layed
702     * on the first relative to the given position.
703     */
704    public static ImageIcon overlay(Icon ground, String overlayImage, OverlayPosition pos) {
705        return overlay(ground, ImageProvider.get(overlayImage), pos);
706    }
707
708    public static ImageIcon overlay(Icon ground, Icon overlay, OverlayPosition pos) {
709        GraphicsConfiguration conf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
710        .getDefaultConfiguration();
711        int w = ground.getIconWidth();
712        int h = ground.getIconHeight();
713        int wo = overlay.getIconWidth();
714        int ho = overlay.getIconHeight();
715        BufferedImage img = conf.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
716        Graphics g = img.createGraphics();
717        ground.paintIcon(null, g, 0, 0);
718        int x = 0, y = 0;
719        switch (pos) {
720        case NORTHWEST:
721            x = 0;
722            y = 0;
723            break;
724        case NORTHEAST:
725            x = w - wo;
726            y = 0;
727            break;
728        case SOUTHWEST:
729            x = 0;
730            y = h - ho;
731            break;
732        case SOUTHEAST:
733            x = w - wo;
734            y = h - ho;
735            break;
736        }
737        overlay.paintIcon(null, g, x, y);
738        return new ImageIcon(img);
739    }
740
741    /*
742     * from: http://www.jidesoft.com/blog/2008/02/29/rotate-an-icon-in-java/ License:
743     * "feel free to use"
744     */
745    final static double DEGREE_90 = 90.0 * Math.PI / 180.0;
746
747    /**
748     * Creates a rotated version of the input image.
749     *
750     * @param c The component to get properties useful for painting, e.g. the foreground or
751     * background color.
752     * @param icon the image to be rotated.
753     * @param rotatedAngle the rotated angle, in degree, clockwise. It could be any double but we
754     * will mod it with 360 before using it.
755     *
756     * @return the image after rotating.
757     */
758    public static Image createRotatedImage(Component c, Image img, double rotatedAngle) {
759        // convert rotatedAngle to a value from 0 to 360
760        double originalAngle = rotatedAngle % 360;
761        if (rotatedAngle != 0 && originalAngle == 0) {
762            originalAngle = 360.0;
763        }
764
765        // convert originalAngle to a value from 0 to 90
766        double angle = originalAngle % 90;
767        if (originalAngle != 0.0 && angle == 0.0) {
768            angle = 90.0;
769        }
770
771        double radian = Math.toRadians(angle);
772
773        new ImageIcon(img); // load completely
774        int iw = img.getWidth(null);
775        int ih = img.getHeight(null);
776        int w;
777        int h;
778
779        if ((originalAngle >= 0 && originalAngle <= 90) || (originalAngle > 180 && originalAngle <= 270)) {
780            w = (int) (iw * Math.sin(DEGREE_90 - radian) + ih * Math.sin(radian));
781            h = (int) (iw * Math.sin(radian) + ih * Math.sin(DEGREE_90 - radian));
782        } else {
783            w = (int) (ih * Math.sin(DEGREE_90 - radian) + iw * Math.sin(radian));
784            h = (int) (ih * Math.sin(radian) + iw * Math.sin(DEGREE_90 - radian));
785        }
786        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
787        Graphics g = image.getGraphics();
788        Graphics2D g2d = (Graphics2D) g.create();
789
790        // calculate the center of the icon.
791        int cx = iw / 2;
792        int cy = ih / 2;
793
794        // move the graphics center point to the center of the icon.
795        g2d.translate(w / 2, h / 2);
796
797        // rotate the graphics about the center point of the icon
798        g2d.rotate(Math.toRadians(originalAngle));
799
800        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
801        g2d.drawImage(img, -cx, -cy, c);
802
803        g2d.dispose();
804        new ImageIcon(image); // load completely
805        return image;
806    }
807
808    /**
809     * Replies the icon for an OSM primitive type
810     * @param type the type
811     * @return the icon
812     */
813    public static ImageIcon get(OsmPrimitiveType type) throws IllegalArgumentException {
814        CheckParameterUtil.ensureParameterNotNull(type, "type");
815        return get("data", type.getAPIName());
816    }
817
818    public static BufferedImage createImageFromSvg(SVGDiagram svg, Dimension dim) {
819        float realWidth = svg.getWidth();
820        float realHeight = svg.getHeight();
821        int width = Math.round(realWidth);
822        int height = Math.round(realHeight);
823        Double scaleX = null, scaleY = null;
824        if (dim.width != -1) {
825            width = dim.width;
826            scaleX = (double) width / realWidth;
827            if (dim.height == -1) {
828                scaleY = scaleX;
829                height = (int) Math.round(realHeight * scaleY);
830            } else {
831                height = dim.height;
832                scaleY = (double) height / realHeight;
833            }
834        } else if (dim.height != -1) {
835            height = dim.height;
836            scaleX = scaleY = (double) height / realHeight;
837            width = (int) Math.round(realWidth * scaleX);
838        }
839        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
840        Graphics2D g = img.createGraphics();
841        g.setClip(0, 0, width, height);
842        if (scaleX != null) {
843            g.scale(scaleX, scaleY);
844        }
845        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
846        try {
847            svg.render(g);
848        } catch (SVGException ex) {
849            return null;
850        }
851        return img;
852    }
853
854    private static SVGUniverse getSvgUniverse() {
855        if (svgUniverse == null) {
856            svgUniverse = new SVGUniverse();
857        }
858        return svgUniverse;
859    }
860}
Note: See TracBrowser for help on using the repository browser.