ch.randelshofer.quaqua.util.CachedPainter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Quaqua Show documentation
Show all versions of Quaqua Show documentation
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:
*
* - 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.
* - 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