001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.renderer.lite;
017:
018: import java.awt.Canvas;
019: import java.awt.Graphics2D;
020: import java.awt.Image;
021: import java.awt.MediaTracker;
022: import java.awt.Toolkit;
023: import java.awt.image.BufferedImage;
024: import java.net.URL;
025: import java.util.HashMap;
026: import java.util.Map;
027: import java.util.logging.Level;
028: import java.util.logging.Logger;
029:
030: /**
031: * $Id: ImageLoader.java 27862 2007-11-12 19:51:19Z desruisseaux $
032: *
033: * @author Ian Turton
034: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/render/src/main/java/org/geotools/renderer/lite/ImageLoader.java $
035: */
036: public final class ImageLoader implements Runnable {
037: /** The logger for the rendering module. */
038: private static final Logger LOGGER = org.geotools.util.logging.Logging
039: .getLogger("org.geotools.rendering");
040:
041: /** The images managed by the loader */
042: private static Map images = new HashMap();
043:
044: /** A canvas used as the image observer on the tracker */
045: private static Canvas obs = new Canvas();
046:
047: /** Used to track the images loading status */
048: private static MediaTracker tracker = new MediaTracker(obs);
049:
050: /** Currently loading image */
051: private static int imageID = 1;
052:
053: /** A maximum time to wait for the image to load */
054: private static long timeout = 10000;
055:
056: /** Location of the loading image */
057: private URL location;
058:
059: /** Still waiting for the image? */
060: private boolean waiting = true;
061:
062: /**
063: * Returns the timeout for aborting an image loading sequence
064: *
065: * @return the timeout in milliseconds
066: */
067: public static long getTimeout() {
068: return timeout;
069: }
070:
071: /**
072: * Sets the maximum time to wait for getting an external image. Set it to -1 to wait
073: * undefinitely
074: *
075: * @param newTimeout the new timeout value in milliseconds
076: */
077: public static void setTimeout(long newTimeout) {
078: timeout = newTimeout;
079: }
080:
081: /**
082: * Add an image to be loaded by the ImageLoader
083: *
084: * @param location the image location
085: * @param interactive if true the methods returns immediatly, otherwise waits for the image to
086: * be loaded
087: */
088: private void add(URL location, boolean interactive) {
089: int imgId = imageID;
090: this .location = location;
091: LOGGER.finest("adding image, interactive? " + interactive);
092:
093: Thread t = new Thread(this );
094: t.start();
095:
096: if (interactive) {
097: LOGGER.finest("fast return");
098:
099: return;
100: } else {
101: waiting = true;
102:
103: long elapsed = 0;
104: final long step = 500;
105:
106: while (waiting && (elapsed < timeout || timeout < 0)) {
107: LOGGER.finest("waiting..." + waiting);
108:
109: try {
110: Thread.sleep(step);
111: elapsed += step;
112:
113: if (LOGGER.isLoggable(Level.FINEST)) {
114: LOGGER.finest("Waiting for image " + location
115: + ", elapsed " + elapsed
116: + " milliseconds");
117: }
118: } catch (InterruptedException e) {
119: LOGGER.warning(e.toString());
120: }
121: }
122:
123: if (LOGGER.isLoggable(Level.FINEST)) {
124: LOGGER.finest(imgId + " complete?: "
125: + (isFlagUp(imgId, MediaTracker.COMPLETE)));
126: LOGGER.finest(imgId + " abort?: "
127: + (isFlagUp(imgId, MediaTracker.ABORTED)));
128: LOGGER.finest(imgId + " error?: "
129: + (isFlagUp(imgId, MediaTracker.ERRORED)));
130: LOGGER.finest(imgId + " loading?: "
131: + (isFlagUp(imgId, MediaTracker.LOADING)));
132: LOGGER.finest(imgId + "slow return " + waiting);
133: }
134:
135: return;
136: }
137: }
138:
139: /**
140: * Checks the state of the current tracker against a flag
141: *
142: * @param id the image id
143: * @param flag the flag to be checked
144: *
145: * @return true if the flag is up
146: */
147: private boolean isFlagUp(int id, int flag) {
148: return (tracker.statusID(id, true) & flag) == flag;
149: }
150:
151: /**
152: * Fetch a buffered image from the loader, if interactive is false then the loader will wait
153: * for the image to be available before returning, used by printers and file output
154: * renderers. If interactive is true and the image is ready then return, if image is not ready
155: * start loading it and return null. The renderer is responsible for finding an alternative
156: * to use.
157: *
158: * @param location the url of the image to be fetched
159: * @param interactive boolean to signal if the loader should wait for the image to be ready.
160: *
161: * @return the buffered image or null
162: */
163: public BufferedImage get(URL location, boolean interactive) {
164: if (images.containsKey(location)) {
165: LOGGER.finest("found it");
166:
167: return (BufferedImage) images.get(location);
168: } else {
169: if (!interactive) {
170: images.put(location, null);
171: }
172:
173: LOGGER.finest("adding " + location);
174: add(location, interactive);
175:
176: return (BufferedImage) images.get(location);
177: }
178: }
179:
180: /**
181: * Runs the loading thread
182: */
183: public void run() {
184: int myID = 0;
185: Image img = null;
186:
187: try {
188: img = Toolkit.getDefaultToolkit().createImage(location);
189: myID = imageID++;
190: tracker.addImage(img, myID);
191: } catch (Exception e) {
192: LOGGER.warning("Exception fetching image from " + location
193: + "\n" + e);
194: images.remove(location);
195: waiting = false;
196:
197: return;
198: }
199:
200: try {
201: while ((tracker.statusID(myID, true) & MediaTracker.LOADING) != 0) {
202: tracker.waitForID(myID, 500);
203: LOGGER.finest(myID + "loading - waiting....");
204: }
205: } catch (InterruptedException ie) {
206: LOGGER.warning(ie.toString());
207: }
208:
209: int state = tracker.statusID(myID, true);
210:
211: if (state == MediaTracker.ERRORED) {
212: LOGGER.finer("" + myID + " Error loading");
213: // images.remove(location);
214: waiting = false;
215:
216: return;
217: }
218:
219: if ((state & MediaTracker.COMPLETE) == MediaTracker.COMPLETE) {
220: LOGGER.finest("" + myID + "completed load");
221:
222: int iw = img.getWidth(obs);
223: int ih = img.getHeight(obs);
224: BufferedImage bi = new BufferedImage(iw, ih,
225: BufferedImage.TYPE_INT_ARGB);
226: Graphics2D big = bi.createGraphics();
227: big.drawImage(img, 0, 0, obs);
228: images.put(location, bi);
229:
230: waiting = false;
231:
232: return;
233: }
234:
235: LOGGER.finer("" + myID + " whoops - some other outcome "
236: + state);
237: waiting = false;
238:
239: return;
240: }
241:
242: /**
243: * Resets the image cache
244: */
245: public void reset() {
246: images.clear();
247: }
248: }
|