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 ? } } } }