org.jdesktop.jxlayer.plaf.AbstractBufferedLayerUI Maven / Gradle / Ivy
/**
* Copyright (c) 2006-2008, Alexander Potochkin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of the JXLayer project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jdesktop.jxlayer.plaf;
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.effect.*;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.lang.ref.SoftReference;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
/**
* The {@code AbstractBufferedLayerUI} paitns the {@link JXLayer}
* to the {@code BufferedImage} and then paints this image to the {@code Graphics}
* passed to its {@code paint} method.
*
* The main feature provided by {@code AbstractBufferedLayerUI}
* is the ability to apply various {@link org.jdesktop.jxlayer.plaf.effect.LayerEffect}s to its content,
* the most popular effect is the {@link BufferedImageOpEffect}
* which uses {@code BufferedImageOp} to filter the buffer
*
* This class introduces the {@link #isIncrementalUpdate(JXLayer)} method.
*
* If it returns {@code false} and {@link #isDirty()} returns {@code false}
* and the cached image exists and matches the size of painted {@code JXLayer}
* then the existing image will be used during the painting.
* It helps to skip unnecessary painting
* and save a lot of time, especially if {@link BufferedImageOpEffect}s are used.
* If {@code isIncrementalUpdate(JXLayer)} returns {@code true}
* the cache image will be updated on every painting.
*
* For custom painting, override {@link #paintLayer(Graphics2D,JXLayer)} as usual.
*
* If you want to apply {@code Effect}s, override {@link #getLayerEffects(JXLayer)} methods
* or use more flexible {@link BufferedLayerUI}.
*
* Note: The {@code AbstractBufferedLayerUI} is not shareable and
* can be set to single {@link JXLayer} instance.
* The current {@code JXLayer} can be obtained with {@link #getLayer()} method
*
* @see org.jdesktop.jxlayer.plaf.effect.LayerEffect
* @see BufferedImageOpEffect
* @see BufferedLayerUI
* @see #isIncrementalUpdate(JXLayer)
*/
public class AbstractBufferedLayerUI
extends AbstractLayerUI implements PropertyChangeListener {
private JXLayer layer;
private transient SoftReference cachedBuffer;
private static final LayerEffect[] emptyEffectArray = new LayerEffect[0];
/**
* {@inheritDoc}
*
* This implementation saves the passed {@code JXLayer} instance
* and checks that it set to one layer only
*
* @throws IllegalStateException if this {@code BufferedLayerUI}
* is set to multiple {@code JXLayer}s
* @see #uninstallUI(JComponent)
* @see #getLayer()
*/
@SuppressWarnings("unchecked")
public void installUI(JComponent c) {
if (layer != null) {
throw new IllegalStateException(
"BufferedLayerUI can't be shared between multiple layers");
}
layer = (JXLayer) c;
c.addPropertyChangeListener(this);
super.installUI(c);
}
/**
* {@inheritDoc}
*/
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
c.removePropertyChangeListener(this);
layer = null;
}
/**
* This method is public as an implementation side effect.
* {@code AbstractBufferedLayerUI} listens property changes of its {@link JXLayer}
* and marks itself as dirty if the {@code JXLayer}'s view component has been changed.
*
* @param evt the PropertyChangeEvent
*
* @see JXLayer#setView(JComponent)
* @see #setDirty(boolean)
*/
public void propertyChange(PropertyChangeEvent evt) {
if ("view".equals(evt.getPropertyName())) {
setDirty(true);
}
}
/**
* Mark {@code AbstractBufferedLayerUI} as dirty if the LookAndFeel was changed
*
* @param l the {@code JXLayer} this {@code AbstractBufferedLayerUI} is set to
*/
public void updateUI(JXLayer l) {
setDirty(true);
}
/**
* The {@code JXLayer} this {@code AbstractBufferedLayerUI} is set to.
*
* @return the {@code JXLayer} this {@code AbstractBufferedLayerUI} is set to
*/
public JXLayer getLayer() {
return layer;
}
/**
* Returns the current cached image.
*
* The implementation of this LayerUI may use SoftReference to keep
* this image, so this method may return {@code null} at any time.
*
* However it is guaranteed that is safe to call this method inside
* {@link #paintLayer(Graphics2D,JXLayer)} method, because a strong reference
* to the buffer is kept during painting process and you'll get the actual
* BufferedImage which you are free to use withing {@code paintLayer}.
*
* @return the current cached image.
* @see #setBuffer(BufferedImage)
*/
protected BufferedImage getBuffer() {
return cachedBuffer == null ? null : cachedBuffer.get();
}
/**
* Sets the current cached image.
*
* @param buffer the {@code BufferedImage} to be used as the cache
* @see #getBuffer()
*/
protected void setBuffer(BufferedImage buffer) {
cachedBuffer = new SoftReference(buffer);
}
/**
* Returns the array of {@link LayerEffect} to be used during painting of this {@code JXLayer},
* the default implementation returns constant empty array.
*
* @param l the {@code JXLayer} being painted
* @return the array of {@link LayerEffect} to be used during painting of the {@code JXLayer}
*/
protected LayerEffect[] getLayerEffects(JXLayer l) {
return emptyEffectArray;
}
/**
* If this method returns {@code false} and {@link #isDirty} returns {@code false}
* and the cached image exists and matches the size of painted {@code JXLayer}
* then the existing image will be used during the painting.
*
* It helps to skip unnecessary painting and save a lot of time,
* especially if {@link BufferedImageOpEffect}s are used.
*
* If this method returns {@code true} the cache image will be updated on every painting.
*
* The default implementation returns {@code true}
*
*
* @param l the {@code JXLayer} being painted
* @return {@code true} if the cache image should be updated on every painting,
* otherwise returns {@code false}
* @see #getBuffer()
*/
protected boolean isIncrementalUpdate(JXLayer l) {
return true;
}
/**
* {@inheritDoc}
*
* This method paints the paitns the {@link JXLayer} to the {@code BufferedImage}
* and then paints this image to the passed {@code Graphics}.
* It also manages the state of the existing cached image
* and applies the existing {@link LayerEffect}s to the image.
*
* @see #paintLayer(Graphics2D,JXLayer)
* @see #isBufferFormatValid(JXLayer)
* @see #isBufferContentValid(JXLayer,Shape)
* @see #getLayerEffects(JXLayer)
*/
@SuppressWarnings("unchecked")
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g;
JXLayer l = (JXLayer) c;
configureGraphics(g2, l);
Shape clip = g2.getClip();
// temporary strong reference to prevent the cachedBuffer softReference
// from being cleaned up by the garbage collector during painting
BufferedImage buffer = getBuffer();
boolean isBufferFormatValid = isBufferFormatValid(l);
if (!isBufferFormatValid || !isBufferContentValid(l, clip)) {
if (!isBufferFormatValid) {
buffer = createBuffer(l.getWidth(), l.getHeight());
setBuffer(buffer);
}
Graphics2D bufg = buffer.createGraphics();
if (isIncrementalUpdate(l)) {
bufg.setClip(clip);
}
paintLayer(bufg, l);
applyLayerEffects(l, bufg.getClip());
bufg.dispose();
}
g2.drawImage(buffer, 0, 0, null);
setDirty(false);
}
/**
* Defines if the cached image has the valid format
* for the current painting painting operation
* and there is no need to recreate it.
*
* The default implementation returns {@code true}
* if the cached image is not null and its size matches
* the size of the {@code JXLayer} being painted,
* otherwise it returns {@code true}.
*
* @param l the {@code JXLayer} being painted
* @return {@code true} if the format of existing cache image
* is valid, otherwise returns {@code false}
* @see #getBuffer()
*/
protected boolean isBufferFormatValid(JXLayer l) {
BufferedImage buffer = getBuffer();
return buffer != null &&
buffer.getWidth() == l.getWidth() &&
buffer.getHeight() == l.getHeight();
}
/**
* Defines if the cached image has the valid content
* for the current painting painting operation
* and there is no need to repaint it.
*
* The default implementation returns {@code true}
* if this {@code AbstractBufferedLayerUI} hasn't been marked as dirty
* and incremental update is disabled.
*
* @param l the {@code JXLayer} being painted
* @param clip the current clipping shape
* @return {@code true} if the content of existing cache image
* is valid, otherwise returns {@code false}
* @see #isDirty()
* @see #isIncrementalUpdate(JXLayer)
*/
protected boolean isBufferContentValid(JXLayer l, Shape clip) {
return !isDirty() && !isIncrementalUpdate(l);
}
/**
* Creates the {@code BufferedImage} to be used as the cached image.
* This method must never return {@code null}.
*
* @param width the width of the image
* @param height the height of the image
* @return an off-screen {@code BufferedImage},
* which can be used for double buffering.
*/
protected BufferedImage createBuffer(int width, int height) {
return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
}
/**
* Iterates through the current array of {@link LayerEffect}s
* and applies each enabled one to the buffer.
*
* @param l the {@code JXLayer} being painted
* @param clip the current clipping shape
* @see #getLayerEffects(JXLayer)
* @see org.jdesktop.jxlayer.plaf.effect.LayerEffect#isEnabled()
*/
protected void applyLayerEffects(JXLayer l, Shape clip) {
if (getBuffer() == null) {
throw new IllegalStateException("Buffer is null");
}
for (LayerEffect e : getLayerEffects(l)) {
if (e.isEnabled()) {
e.apply(getBuffer(), clip);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy