/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2002 - 2008, Open Source Geospatial Foundation (OSGeo) * (C) 2008 - 2009, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.renderer.style; import java.awt.Canvas; import java.awt.Graphics2D; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * $Id$ * * @author Ian Turton * @module */ class ImageLoader implements Runnable { /** The logger for the rendering module. */ private static final Logger LOGGER = org.apache.sis.util.logging.Logging.getLogger("org.geotoolkit.renderer.style"); /** The images managed by the loader */ private static Map images = new HashMap(); /** A canvas used as the image observer on the tracker */ private static Canvas obs = new Canvas(); /** Used to track the images loading status */ private static MediaTracker tracker = new MediaTracker(obs); /** Currently loading image */ private static int imageID = 1; /** A maximum time to wait for the image to load */ private static long timeout = 10000; /** Location of the loading image */ private URL location; /** Still waiting for the image? */ private boolean waiting = true; /** * Returns the timeout for aborting an image loading sequence * * @return the timeout in milliseconds */ public static long getTimeout() { return timeout; } /** * Sets the maximum time to wait for getting an external image. Set it to -1 to wait * undefinitely * * @param newTimeout the new timeout value in milliseconds */ public static void setTimeout(final long newTimeout) { timeout = newTimeout; } /** * Add an image to be loaded by the ImageLoader * * @param location the image location * @param interactive if true the methods returns immediatly, otherwise waits for the image to * be loaded */ private void add(final URL location, final boolean interactive) { int imgId = imageID; this.location = location; LOGGER.finest("adding image, interactive? " + interactive); Thread t = new Thread(this); t.start(); if (interactive) { LOGGER.finest("fast return"); return; } else { waiting = true; long elapsed = 0; final long step = 500; while (waiting && (elapsed < timeout || timeout < 0)) { LOGGER.finest("waiting..." + waiting); try { Thread.sleep(step); elapsed += step; if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("Waiting for image " + location + ", elapsed " + elapsed + " milliseconds"); } } catch (InterruptedException e) { LOGGER.warning(e.toString()); } } if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest(imgId + " complete?: " + (isFlagUp(imgId, MediaTracker.COMPLETE))); LOGGER.finest(imgId + " abort?: " + (isFlagUp(imgId, MediaTracker.ABORTED))); LOGGER.finest(imgId + " error?: " + (isFlagUp(imgId, MediaTracker.ERRORED))); LOGGER.finest(imgId + " loading?: " + (isFlagUp(imgId, MediaTracker.LOADING))); LOGGER.finest(imgId + "slow return " + waiting); } return; } } /** * Checks the state of the current tracker against a flag * * @param id the image id * @param flag the flag to be checked * * @return true if the flag is up */ private boolean isFlagUp(final int id, final int flag) { return (tracker.statusID(id, true) & flag) == flag; } /** * Fetch a buffered image from the loader, if interactive is false then the loader will wait * for the image to be available before returning, used by printers and file output * renderers. If interactive is true and the image is ready then return, if image is not ready * start loading it and return null. The renderer is responsible for finding an alternative * to use. * * @param location the url of the image to be fetched * @param interactive boolean to signal if the loader should wait for the image to be ready. * * @return the buffered image or null */ public BufferedImage get(final URL location, final boolean interactive) { if (images.containsKey(location)) { LOGGER.finest("found it"); return (BufferedImage) images.get(location); } else { if (!interactive) { images.put(location, null); } LOGGER.finest("adding " + location); add(location, interactive); return (BufferedImage) images.get(location); } } /** * Runs the loading thread */ @Override public void run() { int myID = 0; Image img = null; try { img = Toolkit.getDefaultToolkit().createImage(location); myID = imageID++; tracker.addImage(img, myID); } catch (Exception e) { LOGGER.warning("Exception fetching image from " + location + "\n" + e); images.remove(location); waiting = false; return; } try { while ((tracker.statusID(myID, true) & MediaTracker.LOADING) != 0) { tracker.waitForID(myID, 500); LOGGER.finest(myID + "loading - waiting...."); } } catch (InterruptedException ie) { LOGGER.warning(ie.toString()); } int state = tracker.statusID(myID, true); if (state == MediaTracker.ERRORED) { LOGGER.finer("" + myID + " Error loading"); // images.remove(location); waiting = false; return; } if ((state & MediaTracker.COMPLETE) == MediaTracker.COMPLETE) { LOGGER.finest("" + myID + "completed load"); int iw = img.getWidth(obs); int ih = img.getHeight(obs); BufferedImage bi = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_ARGB); Graphics2D big = bi.createGraphics(); big.drawImage(img, 0, 0, obs); images.put(location, bi); waiting = false; return; } LOGGER.finer("" + myID + " whoops - some other outcome " + state); waiting = false; return; } /** * Resets the image cache */ public void reset() { images.clear(); } }