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

Last change on this file since 5299 was 5275, checked in by bastiK, 12 years ago

doc improvements

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