mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
fix src folder structure
This commit is contained in:
441
src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java
Normal file
441
src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* $Id: AbstractPainter.java 4082 2011-11-15 18:39:43Z kschaefe $
|
||||
*
|
||||
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
|
||||
* Santa Clara, California 95054, U.S.A. All rights reserved.
|
||||
*
|
||||
* 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; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package org.jdesktop.swingx.painter;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
import org.jdesktop.beans.AbstractBean;
|
||||
import org.jdesktop.swingx.util.GraphicsUtilities;
|
||||
|
||||
/**
|
||||
* <p>A convenient base class from which concrete {@link Painter} implementations may
|
||||
* extend. It extends {@link org.jdesktop.beans.AbstractBean} as a convenience for
|
||||
* adding property change notification support. In addition, <code>AbstractPainter</code>
|
||||
* provides subclasses with the ability to cacheable painting operations, configure the
|
||||
* drawing surface with common settings (such as antialiasing and interpolation), and
|
||||
* toggle whether a subclass paints or not via the <code>visibility</code> property.</p>
|
||||
*
|
||||
* <p>Subclasses of <code>AbstractPainter</code> generally need only override the
|
||||
* {@link #doPaint(Graphics2D, Object, int, int)} method. If a subclass requires more control
|
||||
* over whether caching is enabled, or for configuring the graphics state, then it
|
||||
* may override the appropriate protected methods to interpose its own behavior.</p>
|
||||
*
|
||||
* <p>For example, here is the doPaint method of a simple <code>Painter</code> that
|
||||
* paints an opaque rectangle:
|
||||
* <pre><code>
|
||||
* public void doPaint(Graphics2D g, T obj, int width, int height) {
|
||||
* g.setPaint(Color.BLUE);
|
||||
* g.fillRect(0, 0, width, height);
|
||||
* }
|
||||
* </code></pre></p>
|
||||
*
|
||||
* @author rbair
|
||||
* @param <T> an optional configuration parameter
|
||||
*/
|
||||
@SuppressWarnings("nls")
|
||||
public abstract class AbstractPainter<T> extends AbstractBean implements Painter<T> {
|
||||
/**
|
||||
* An enum representing the possible interpolation values of Bicubic, Bilinear, and
|
||||
* Nearest Neighbor. These map to the underlying RenderingHints,
|
||||
* but are easier to use and serialization safe.
|
||||
*/
|
||||
public enum Interpolation {
|
||||
/**
|
||||
* use bicubic interpolation
|
||||
*/
|
||||
Bicubic(RenderingHints.VALUE_INTERPOLATION_BICUBIC),
|
||||
/**
|
||||
* use bilinear interpolation
|
||||
*/
|
||||
Bilinear(RenderingHints.VALUE_INTERPOLATION_BILINEAR),
|
||||
/**
|
||||
* use nearest neighbor interpolation
|
||||
*/
|
||||
NearestNeighbor(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
|
||||
|
||||
private Object value;
|
||||
|
||||
Interpolation(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private void configureGraphics(Graphics2D g) {
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, value);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------- Instance Variables
|
||||
/**
|
||||
* The cached image, if shouldUseCache() returns true
|
||||
*/
|
||||
private transient SoftReference<BufferedImage> cachedImage;
|
||||
private boolean cacheCleared = true;
|
||||
private boolean cacheable = false;
|
||||
private boolean dirty = false;
|
||||
private BufferedImageOp[] filters = new BufferedImageOp[0];
|
||||
private boolean antialiasing = true;
|
||||
private Interpolation interpolation = Interpolation.NearestNeighbor;
|
||||
private boolean visible = true;
|
||||
private boolean inPaintContext;
|
||||
|
||||
/**
|
||||
* Creates a new instance of AbstractPainter.
|
||||
*/
|
||||
public AbstractPainter() { }
|
||||
|
||||
/**
|
||||
* Creates a new instance of AbstractPainter.
|
||||
* @param cacheable indicates if this painter should be cacheable
|
||||
*/
|
||||
public AbstractPainter(boolean cacheable) {
|
||||
setCacheable(cacheable);
|
||||
}
|
||||
|
||||
/**
|
||||
* A defensive copy of the Effects to apply to the results
|
||||
* of the AbstractPainter's painting operation. The array may
|
||||
* be empty but it will never be null.
|
||||
* @return the array of filters applied to this painter
|
||||
*/
|
||||
public final BufferedImageOp[] getFilters() {
|
||||
BufferedImageOp[] results = new BufferedImageOp[filters.length];
|
||||
System.arraycopy(filters, 0, results, 0, results.length);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A convenience method for specifying the filters to use based on
|
||||
* BufferedImageOps. These will each be individually wrapped by an ImageFilter
|
||||
* and then setFilters(Effect... filters) will be called with the resulting
|
||||
* array</p>
|
||||
*
|
||||
*
|
||||
* @param effects the BufferedImageOps to wrap as filters
|
||||
*/
|
||||
public void setFilters(BufferedImageOp ... effects) {
|
||||
if (effects == null) effects = new BufferedImageOp[0];
|
||||
BufferedImageOp[] old = getFilters();
|
||||
this.filters = new BufferedImageOp[effects.length];
|
||||
System.arraycopy(effects, 0, this.filters, 0, this.filters.length);
|
||||
setDirty(true);
|
||||
firePropertyChange("filters", old, getFilters());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if antialiasing is turned on or not. The default value is true.
|
||||
* This is a bound property.
|
||||
* @return the current antialiasing setting
|
||||
*/
|
||||
public boolean isAntialiasing() {
|
||||
return antialiasing;
|
||||
}
|
||||
/**
|
||||
* Sets the antialiasing setting. This is a bound property.
|
||||
* @param value the new antialiasing setting
|
||||
*/
|
||||
public void setAntialiasing(boolean value) {
|
||||
boolean old = isAntialiasing();
|
||||
antialiasing = value;
|
||||
if (old != value) setDirty(true);
|
||||
firePropertyChange("antialiasing", old, isAntialiasing());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current interpolation setting. This property determines if interpolation will
|
||||
* be used when drawing scaled images. @see java.awt.RenderingHints.KEY_INTERPOLATION.
|
||||
* @return the current interpolation setting
|
||||
*/
|
||||
public Interpolation getInterpolation() {
|
||||
return interpolation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value for the interpolation setting. This setting determines if interpolation
|
||||
* should be used when drawing scaled images. @see java.awt.RenderingHints.KEY_INTERPOLATION.
|
||||
* @param value the new interpolation setting
|
||||
*/
|
||||
public void setInterpolation(Interpolation value) {
|
||||
Object old = getInterpolation();
|
||||
this.interpolation = value == null ? Interpolation.NearestNeighbor : value;
|
||||
if (old != value) setDirty(true);
|
||||
firePropertyChange("interpolation", old, getInterpolation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the visible property. This controls if the painter should
|
||||
* paint itself. It is true by default. Setting visible to false
|
||||
* is good when you want to temporarily turn off a painter. An example
|
||||
* of this is a painter that you only use when a button is highlighted.
|
||||
*
|
||||
* @return current value of visible property
|
||||
*/
|
||||
public boolean isVisible() {
|
||||
return this.visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the visible property. This controls if the painter should
|
||||
* paint itself. It is true by default. Setting visible to false
|
||||
* is good when you want to temporarily turn off a painter. An example
|
||||
* of this is a painter that you only use when a button is highlighted.</p>
|
||||
*
|
||||
* @param visible New value of visible property.
|
||||
*/
|
||||
public void setVisible(boolean visible) {
|
||||
boolean old = isVisible();
|
||||
this.visible = visible;
|
||||
if (old != visible) setDirty(true); //not the most efficient, but I must do this otherwise a CompoundPainter
|
||||
//or other aggregate painter won't know that it is now invalid
|
||||
//there might be a tricky solution but that is a performance optimization
|
||||
firePropertyChange("visible", old, isVisible());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets whether this <code>AbstractPainter</code> can be cached as an image.
|
||||
* If caching is enabled, then it is the responsibility of the developer to
|
||||
* invalidate the painter (via {@link #clearCache}) if external state has
|
||||
* changed in such a way that the painter is invalidated and needs to be
|
||||
* repainted.</p>
|
||||
*
|
||||
* @return whether this is cacheable
|
||||
*/
|
||||
public boolean isCacheable() {
|
||||
return cacheable;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether this <code>AbstractPainter</code> can be cached as an image.
|
||||
* If true, this is treated as a hint. That is, a cacheable may or may not be used.
|
||||
* The {@link #shouldUseCache} method actually determines whether the cacheable is used.
|
||||
* However, if false, then this is treated as an absolute value. That is, no
|
||||
* cacheable will be used.</p>
|
||||
*
|
||||
* <p>If set to false, then #clearCache is called to free system resources.</p>
|
||||
*
|
||||
* @param cacheable the cache flag
|
||||
*/
|
||||
public void setCacheable(boolean cacheable) {
|
||||
boolean old = isCacheable();
|
||||
this.cacheable = cacheable;
|
||||
firePropertyChange("cacheable", old, isCacheable());
|
||||
if (!isCacheable()) {
|
||||
clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Call this method to clear the cacheable. This may be called whether there is
|
||||
* a cacheable being used or not. If cleared, on the next call to <code>paint</code>,
|
||||
* the painting routines will be called.</p>
|
||||
*
|
||||
* <p><strong>Subclasses</strong>If overridden in subclasses, you
|
||||
* <strong>must</strong> call super.clearCache, or physical
|
||||
* resources (such as an Image) may leak.</p>
|
||||
*/
|
||||
public void clearCache() {
|
||||
BufferedImage cache = cachedImage == null ? null : cachedImage.get();
|
||||
if (cache != null) {
|
||||
cache.flush();
|
||||
}
|
||||
cacheCleared = true;
|
||||
if (!isCacheable()) {
|
||||
cachedImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Only made package private for testing. Don't call this method outside
|
||||
// * of this class! This is NOT a bound property
|
||||
// */
|
||||
// private boolean isCacheCleared() {
|
||||
// return cacheCleared;
|
||||
// }
|
||||
|
||||
/**
|
||||
* <p>Called to allow <code>Painter</code> subclasses a chance to see if any state
|
||||
* in the given object has changed from the last paint operation. If it has, then
|
||||
* the <code>Painter</code> has a chance to mark itself as dirty, thus causing a
|
||||
* repaint, even if cached.</p>
|
||||
*
|
||||
* @param object the object to validate
|
||||
*/
|
||||
protected void validate(T object) { /* do nothing */ }
|
||||
|
||||
/**
|
||||
* Ye olde dirty bit. If true, then the painter is considered dirty and in need of
|
||||
* being repainted. This is a bound property.
|
||||
*
|
||||
* @return true if the painter state has changed and the painter needs to be
|
||||
* repainted.
|
||||
*/
|
||||
protected boolean isDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dirty bit. If true, then the painter is considered dirty, and the cache
|
||||
* will be cleared. This property is bound.
|
||||
*
|
||||
* @param d whether this <code>Painter</code> is dirty.
|
||||
*/
|
||||
protected void setDirty(boolean d) {
|
||||
boolean old = isDirty();
|
||||
this.dirty = d;
|
||||
firePropertyChange("dirty", old, isDirty());
|
||||
if (isDirty()) {
|
||||
clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isInPaintContext() {
|
||||
return inPaintContext;
|
||||
}
|
||||
|
||||
void setInPaintContext(boolean inPaintContext) {
|
||||
this.inPaintContext = inPaintContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns true if the painter should use caching. This method allows subclasses to
|
||||
* specify the heuristics regarding whether to cache or not. If a <code>Painter</code>
|
||||
* has intelligent rules regarding painting times, and can more accurately indicate
|
||||
* whether it should be cached, it could implement that logic in this method.</p>
|
||||
*
|
||||
* @return whether or not a cache should be used
|
||||
*/
|
||||
protected boolean shouldUseCache() {
|
||||
return isCacheable() || filters.length > 0; //NOTE, I can only do this because getFilters() is final
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method is called by the <code>paint</code> method prior to
|
||||
* any drawing operations to configure the drawing surface. The default
|
||||
* implementation sets the rendering hints that have been specified for
|
||||
* this <code>AbstractPainter</code>.</p>
|
||||
*
|
||||
* <p>This method can be overridden by subclasses to modify the drawing
|
||||
* surface before any painting happens.</p>
|
||||
*
|
||||
* @param g the graphics surface to configure. This will never be null.
|
||||
* @see #paint(Graphics2D, Object, int, int)
|
||||
*/
|
||||
protected void configureGraphics(Graphics2D g) {
|
||||
//configure antialiasing
|
||||
if(isAntialiasing()) {
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
} else {
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_OFF);
|
||||
}
|
||||
|
||||
getInterpolation().configureGraphics(g);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method and perform custom painting operations
|
||||
* here.
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param g The Graphics2D object in which to paint
|
||||
* @param object an optional configuration parameter
|
||||
*/
|
||||
protected abstract void doPaint(Graphics2D g, T object, int width, int height);
|
||||
|
||||
@Override
|
||||
public final void paint(Graphics2D g, T obj, int width, int height) {
|
||||
if (g == null) {
|
||||
throw new NullPointerException("The Graphics2D must be supplied");
|
||||
}
|
||||
|
||||
if(!isVisible() || width < 1 || height < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
configureGraphics(g);
|
||||
|
||||
//paint to a temporary image if I'm caching, or if there are filters to apply
|
||||
if (shouldUseCache() || filters.length > 0) {
|
||||
validate(obj);
|
||||
BufferedImage cache = cachedImage == null ? null : cachedImage.get();
|
||||
boolean invalidCache = null == cache ||
|
||||
cache.getWidth() != width ||
|
||||
cache.getHeight() != height;
|
||||
|
||||
if (cacheCleared || invalidCache || isDirty()) {
|
||||
//rebuild the cacheable. I do this both if a cacheable is needed, and if any
|
||||
//filters exist. I only *save* the resulting image if caching is turned on
|
||||
if (invalidCache) {
|
||||
cache = GraphicsUtilities.createCompatibleTranslucentImage(width, height);
|
||||
}
|
||||
if (cache != null) {
|
||||
Graphics2D gfx = cache.createGraphics();
|
||||
|
||||
try {
|
||||
gfx.setClip(0, 0, width, height);
|
||||
|
||||
if (!invalidCache) {
|
||||
// If we are doing a repaint, but we didn't have to
|
||||
// recreate the image, we need to clear it back
|
||||
// to a fully transparent background.
|
||||
Composite composite = gfx.getComposite();
|
||||
gfx.setComposite(AlphaComposite.Clear);
|
||||
gfx.fillRect(0, 0, width, height);
|
||||
gfx.setComposite(composite);
|
||||
}
|
||||
|
||||
configureGraphics(gfx);
|
||||
doPaint(gfx, obj, width, height);
|
||||
} finally {
|
||||
gfx.dispose();
|
||||
}
|
||||
|
||||
if (!isInPaintContext()) {
|
||||
for (BufferedImageOp f : getFilters()) {
|
||||
cache = f.filter(cache, null);
|
||||
}
|
||||
}
|
||||
|
||||
//only save the temporary image as the cacheable if I'm caching
|
||||
if (shouldUseCache()) {
|
||||
cachedImage = new SoftReference<BufferedImage>(cache);
|
||||
cacheCleared = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.drawImage(cache, 0, 0, null);
|
||||
} else {
|
||||
//can't use the cacheable, so just paint
|
||||
doPaint(g, obj, width, height);
|
||||
}
|
||||
|
||||
//painting has occured, so restore the dirty bit to false
|
||||
setDirty(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user