package net.bull.javamelody.swing.util;
import java.awt.AlphaComposite;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import javax.swing.JComponent;
import javax.swing.JPanel;
/**
* @author Alexander Potochkin
*
* https://swinghelper.dev.java.net/
* http://weblogs.java.net/blog/alexfromsun/
*/
public class JXLayer extends JPanel {
private static final long serialVersionUID = -180838202361369295L;
// layering related properties
private final JComponent contentPane;
private final JComponent glassPane = new JXGlassPane();
// painting related properties
private float alpha = 1;
private transient BufferedImageOp bio;
private transient BufferedImage tempSrc;
private transient BufferedImage tempDst;
private static class JXGlassPane extends JPanel {
private static final long serialVersionUID = -6311466609547970582L;
/**
* Constructeur.
*/
JXGlassPane() {
super();
setOpaque(false);
}
/** {@inheritDoc} */
@Override
public boolean contains(int x, int y) {
if (getMouseListeners().length == 0 && getMouseMotionListeners().length == 0
&& getMouseWheelListeners().length == 0 && !isCursorSet()) {
return false;
}
return super.contains(x, y);
}
}
/**
* Constructeur.
* @param c JComponent
*/
public JXLayer(JComponent c) {
super(null);
super.addImpl(glassPane, null, 0);
super.addImpl(c, null, 1);
this.contentPane = c;
}
/** {@inheritDoc} */
@Override
public void doLayout() {
if (contentPane != null) {
setPreferredSize(contentPane.getPreferredSize());
contentPane.setLocation(0, 0);
contentPane.setSize(getWidth(), getHeight());
}
if (glassPane != null) {
glassPane.setLocation(0, 0);
glassPane.setSize(getWidth(), getHeight());
}
}
/** {@inheritDoc} */
@Override
public boolean isOptimizedDrawingEnabled() {
return false;
}
/** {@inheritDoc} */
@Override
protected void addImpl(Component comp, Object constraints, int index) {
contentPane.add(comp, constraints, index);
doLayout();
}
/** {@inheritDoc} */
@Override
public void remove(Component comp) {
contentPane.remove(comp);
}
/** {@inheritDoc} */
@Override
public void removeAll() {
contentPane.removeAll();
}
/** {@inheritDoc} */
@Override
public void setLayout(LayoutManager mgr) {
if (contentPane != null) {
contentPane.setLayout(mgr);
}
}
/** {@inheritDoc} */
@Override
public LayoutManager getLayout() {
return contentPane.getLayout();
}
/** {@inheritDoc} */
@Override
public void setPreferredSize(Dimension preferredSize) {
contentPane.setPreferredSize(preferredSize);
}
/** {@inheritDoc} */
@Override
public Dimension getPreferredSize() {
return contentPane.getPreferredSize();
}
/** {@inheritDoc} */
@Override
public Dimension getMaximumSize() {
return contentPane.getMaximumSize();
}
/** {@inheritDoc} */
@Override
public void setMaximumSize(Dimension maximumSize) {
contentPane.setMaximumSize(maximumSize);
}
/** {@inheritDoc} */
@Override
public Dimension getMinimumSize() {
return contentPane.getMinimumSize();
}
/** {@inheritDoc} */
@Override
public void setMinimumSize(Dimension minimumSize) {
contentPane.setMinimumSize(minimumSize);
}
// painting
/**
* @return BufferedImageOp
*/
public BufferedImageOp getBufferedImageOp() {
return bio;
}
/**
* @param bufferedImageOp BufferedImageOp
*/
public void setBufferedImageOp(BufferedImageOp bufferedImageOp) {
if (bufferedImageOp instanceof AffineTransformOp) {
throw new IllegalArgumentException("AffineTransformOp is not supported");
}
this.bio = bufferedImageOp;
repaint();
}
/**
* @return float
*/
public float getAlpha() {
return alpha;
}
/**
* @param alpha float
*/
public void setAlpha(float alpha) {
if (alpha < 0 || alpha > 1) {
throw new IllegalArgumentException();
}
this.alpha = alpha;
repaint();
}
/** {@inheritDoc} */
@Override
public void paint(Graphics g) {
if (bio == null && alpha == 1 || !(g instanceof Graphics2D)) {
super.paint(g);
return;
}
final Graphics2D g2 = (Graphics2D) g.create();
Rectangle clipBounds = g2.getClipBounds();
if (clipBounds == null) {
clipBounds = new Rectangle(getSize());
}
if (clipBounds.isEmpty()) {
return;
}
final boolean isConvolveOp = bio instanceof ConvolveOp;
if (isConvolveOp) {
final ConvolveOp cop = (ConvolveOp) bio;
final Kernel kernel = cop.getKernel();
clipBounds.grow(kernel.getWidth() / 2, kernel.getHeight() / 2);
}
createTempImagesIfNecessary(clipBounds);
final Graphics2D bufg = (Graphics2D) tempSrc.getGraphics();
bufg.translate(-clipBounds.x, -clipBounds.y);
bufg.setClip(clipBounds);
super.paint(bufg);
bufg.dispose();
applyFilter(g2, clipBounds, isConvolveOp);
}
private void applyFilter(Graphics2D g2, Rectangle clipBounds, boolean isConvolveOp) {
if (isConvolveOp) {
tempDst = bio.filter(tempSrc, tempDst);
} else {
tempDst = bio.filter(tempSrc, tempSrc);
}
if (isOpaque()) {
g2.clearRect(clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height);
}
final Composite oldComposite = g2.getComposite();
if (alpha != 1) {
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
}
g2.drawImage(tempDst, clipBounds.x, clipBounds.y, null);
g2.setComposite(oldComposite);
g2.dispose();
}
private void createTempImagesIfNecessary(Rectangle clipBounds) {
if (tempSrc == null || tempSrc.getWidth() != clipBounds.width
|| tempSrc.getHeight() != clipBounds.height) {
tempSrc = getGraphicsConfiguration().createCompatibleImage(clipBounds.width,
clipBounds.height);
// TYPE_4BYTE_ABGR the only type which works properly on Linux and Solaris ?
// tempSrc = new BufferedImage(clipBounds.width, clipBounds.height, BufferedImage.TYPE_4BYTE_ABGR);
if (bio instanceof ConvolveOp) {
tempDst = getGraphicsConfiguration().createCompatibleImage(clipBounds.width,
clipBounds.height);
// idem ?
}
}
}
}