/* * Created on Jan 20, 2006 * */ package net.atlanticbb.tantlinger.ui.text; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Shape; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.text.Element; import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.ImageView; /** * * An ImageView which caches images to a local directory after they're * initially loaded. After the images are cached, they're subsequently * loaded from the local directory, thereby reducing the overhead of * refetching them from their remote location. * * @author Bob Tantlinger * */ public class CachedImageView extends ImageView { public static final String USER_HOME = System.getProperty("user.home"); /** Directory where app properties are stored */ public static final String PROP_DIR = USER_HOME + File.separator + ".thinga"; public static final String IMG_CACHE_DIR = PROP_DIR + File.separator + "img_cache"; //flag which keeps track of if the image has been writen to disk private boolean hasCached; public CachedImageView(Element elem) { super(elem); } public void paint(Graphics g, Shape a) { super.paint(g, a); Image img = getImage(); //test if the image is completely loaded. int flags = Toolkit.getDefaultToolkit().checkImage( img, -1, -1, getContainer()); boolean done = ((flags & (ImageObserver.ALLBITS)) != 0); if(done && !hasCached) //If it is, write it out to the cache cacheImage(img); } /** * Writes an image out to the cache * @param image */ private void cacheImage(final Image image) { final File cImg = getCachedImg(); if(image == null || cImg == null || cImg.exists()) return; hasCached = true;//set the hasCached flag so we dont write it twice Runnable runner = new Runnable() { public void run() { String type = "png";//assume png for non-jpegs if(cImg.getAbsolutePath().endsWith("jpg")) type = "jpg"; BufferedImage bufImg = makeBufferedImage(image); try { ImageIO.write(bufImg, type, cImg); } catch(Exception ex) { ex.printStackTrace(); } } }; //System.err.println("CACHE " + cImg.getName()); Thread th = new Thread(runner, "Image Cacher"); th.setPriority(Thread.NORM_PRIORITY); th.start(); } /** * Gets the real image URL as specified in the src attribute of the image tag * @return */ private String srcAttribute() { return (String)getElement().getAttributes().getAttribute(HTML.Attribute.SRC); } /** * Return a URL for the image source, * or null if it could not be determined. * * If the image is cached, the local file URL is returned. * The superclass treats this the same as the actual url, but * the image is loaded from the local cache rather than redownloading it */ public URL getImageURL() { String src = srcAttribute(); if(src == null) return null; URL imgURL = null; File diskCachedImg = getCachedImg(); try { if(diskCachedImg.exists()) { imgURL = diskCachedImg.toURL(); } else { URL reference = ((HTMLDocument)getDocument()).getBase(); imgURL = new URL(reference, src); } } catch(MalformedURLException e) { } return imgURL; } private BufferedImage makeBufferedImage(Image image) { BufferedImage buffered; Graphics2D g2; buffered = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB); g2 = buffered.createGraphics(); g2.drawImage(image, null, null); g2.dispose(); return buffered; } /** * Returns the local file of the cached version of the image. * The local file is named with the absolute value of the hashcode for the * actual image url and a "png" or "jpg" file extension * @return */ private File getCachedImg() { String iUrl = srcAttribute(); if(iUrl == null) return null; String imgType = ".png"; if(iUrl.toLowerCase().endsWith(".jpg") || iUrl.toLowerCase().endsWith(".jpeg")) imgType = ".jpg"; int hc = Math.abs(iUrl.hashCode()); File cacheImg = new File(IMG_CACHE_DIR, hc + imgType); return cacheImg; } }