All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.randelshofer.quaqua.util.CachedPainter Maven / Gradle / Ivy

Go to download

A Mavenisation of the Quaqua Mac OSX Swing Look and Feel (Java library) Quaqua Look and Feel (C) 2003-2010, Werner Randelshofer. Mavenisation by Matt Gumbley, DevZendo.org - for problems with Mavenisation, see Matt; for issues with Quaqua, see the Quaqua home page. For full license details, see http://randelshofer.ch/quaqua/license.html

The newest version!
/*
 * @(#)CachedPainter.java  1.1  2008-04-21
 *
 * Copyright (c) 2005-2010 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.quaqua.util;

import java.awt.*;
import java.awt.image.*;
import javax.swing.Icon;
import java.lang.ref.SoftReference;
import java.util.*;

/**
 * A base class used for icons or images that are expensive to paint.
 * A subclass will do the following:
 * 
    *
  1. Invoke paint when you want to paint the image, * if you are implementing Icon you'll invoke this from * paintIcon. * The args argument is useful when additional state is needed. *
  2. Override paintToImage to render the image. The code that * lives here is equivalent to what previously would go in * paintIcon, for an Icon. *
* This class has been derived from javax.swing.plaf.metal.CachedPainter 1.2 04/02/15 * * @author Werner Randelshofer * @version 2.0 2008-04-21 Only cache small images. *
1.0 September 7, 2005 Created. */ public abstract class CachedPainter { // CacheMap maps from class to Cache. private static final Map cacheMap = new HashMap(); private static Cache getCache(Object key) { synchronized (cacheMap) { Cache cache = (Cache) cacheMap.get(key); if (cache == null) { cache = new Cache(1); cacheMap.put(key, cache); } return cache; } } /** * Creates an instance of CachedPainter that will cache up * to cacheCount images of this class. * * @param cacheCount Max number of images to cache */ public CachedPainter(int cacheCount) { getCache(getClass()).setMaxCount(cacheCount); } /** * Renders the cached image to the the passed in Graphic. * If there is no cached image paintToImage will be invoked. * paintImage is invoked to paint the cached image. */ protected void paint(Component c, Graphics g, int x, int y, int w, int h, Object[] args) { if (w <= 0 || h <= 0) { return; } // If the area is larger than 20'000 pixels, render to the passed // in Graphics. 20'000 pixels is a bit larger than a rectangle of // 160*120 points. if (w * h > 20000) { g.translate(x, y); paintToImage(c, g, w, h, args); g.translate(-x, -y); return; } Object key = getClass(); GraphicsConfiguration config = c.getGraphicsConfiguration(); Cache cache = getCache(key); Image image = cache.getImage(key, config, w, h, args); int attempts = 0; do { boolean draw = false; if (image instanceof VolatileImage) { // See if we need to recreate the image switch (((VolatileImage) image).validate(config)) { case VolatileImage.IMAGE_INCOMPATIBLE: ((VolatileImage) image).flush(); image = null; break; case VolatileImage.IMAGE_RESTORED: draw = true; break; } } if (image == null) { // Recreate the image image = createImage(c, w, h, config); cache.setImage(key, config, w, h, args, image); draw = true; } if (draw) { // Render to the Image Graphics g2 = image.getGraphics(); paintToImage(c, g2, w, h, args); g2.dispose(); } // Render to the passed in Graphics paintImage(c, g, x, y, w, h, image, args); // If we did this 3 times and the contents are still lost // assume we're painting to a VolatileImage that is bogus and // give up. Presumably we'll be called again to paint. } while ((image instanceof VolatileImage) && ((VolatileImage) image).contentsLost() && ++attempts < 3); } /** * Paints the representation to cache to the supplied Graphics. * * @param c Component painting to * @param g Graphics to paint to * @param w Width to paint to * @param h Height to paint to * @param args Arguments supplied to paint */ protected abstract void paintToImage(Component c, Graphics g, int w, int h, Object[] args); /** * Paints the image to the specified location. * * @param c Component painting to * @param g Graphics to paint to * @param x X coordinate to paint to * @param y Y coordinate to paint to * @param w Width to paint to * @param h Height to paint to * @param image Image to paint * @param args Arguments supplied to paint */ protected void paintImage(Component c, Graphics g, int x, int y, int w, int h, Image image, Object[] args) { g.drawImage(image, x, y, null); } /** * Creates the image to cache. This returns an opaque image, subclasses * that require translucency or transparency will need to override this * method. * * @param c Component painting to * @param w Width of image to create * @param h Height to image to create * @param config GraphicsConfiguration that will be * rendered to, this may be null. */ protected Image createImage(Component c, int w, int h, GraphicsConfiguration config) { if (config == null) { return new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); } return config.createCompatibleVolatileImage(w, h); } /** * Cache is used to cache an image based on a set of arguments. */ private static class Cache { // Maximum number of entries to cache private int maxCount; // The entries. private java.util.List entries; Cache(int maxCount) { this.maxCount = maxCount; entries = new ArrayList(maxCount); } synchronized void setMaxCount(int maxCount) { this.maxCount = maxCount; } private Entry getEntry(Object key, GraphicsConfiguration config, int w, int h, Object[] args) { synchronized (this) { Entry entry; for (int counter = entries.size() - 1; counter >= 0; counter--) { entry = (Entry) ((SoftReference) entries.get(counter)).get(); if (entry == null) { // SoftReference was invalidated, remove the entry entries.remove(counter); } else if (entry.equals(config, w, h, args)) { // Found the entry, return it. return entry; } } // Entry doesn't exist entry = new Entry(config, w, h, args); if (entries.size() == maxCount) { entries.remove(0); } entries.add(new SoftReference(entry)); return entry; } } /** * Returns the cached Image, or null, for the specified arguments. */ public Image getImage(Object key, GraphicsConfiguration config, int w, int h, Object[] args) { Entry entry = getEntry(key, config, w, h, args); return entry.getImage(); } /** * Sets the cached image for the specified constraints. */ public void setImage(Object key, GraphicsConfiguration config, int w, int h, Object[] args, Image image) { Entry entry = getEntry(key, config, w, h, args); entry.setImage(image); } /** * Caches set of arguments and Image. */ private static class Entry { private GraphicsConfiguration config; private Object[] args; private Image image; private int w; private int h; Entry(GraphicsConfiguration config, int w, int h, Object[] args) { this.config = config; this.args = args; this.w = w; this.h = h; } public void setImage(Image image) { this.image = image; } public Image getImage() { return image; } @Override public String toString() { StringBuilder value = new StringBuilder(super.toString() + "[ graphicsConfig=" + config + ", image=" + image + ", w=" + w + ", h=" + h); if (args != null) { for (int counter = 0; counter < args.length; counter++) { value.append(", "); value.append(args[counter]); } } value.append("]"); return value.toString(); } public boolean equals(GraphicsConfiguration config, int w, int h, Object[] args) { if (this.w == w && this.h == h && ((this.config != null && this.config.equals(config)) || (this.config == null && config == null))) { if (this.args == null && args == null) { return true; } if (this.args != null && args != null && this.args.length == args.length) { for (int counter = args.length - 1; counter >= 0; counter--) { if (!this.args[counter].equals(args[counter])) { return false; } } return true; } } return false; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy