Skip to main content

Creating my own effects

5 replies [Last post]
stenew
Offline
Joined: 2003-08-21

Alex,

I have been playing with JXLayer for several days now, and must say it is a great addition.

I have some questions, though.

I want to take your LockableUI and change it slightly. What I'd like to do, is apply the BlurFilter to the underlying panel, but not block Mouse or Keyboard events.

I originally found an older version of your JXLayer library that used Painters. I was able to get that to do what I wanted by simply adding a variable to the ImageOpPainter class called doBlur, and setting that instead of the locking variable in the BufferedPainter class, and then in the paintComponent method, use the doBlur variable to determine whether to apply the effect.

I tried the same pattern with the new code, but things don't work the way I expected them to.

I created a class called BlurrableUI, modeled after the LockableUI class, and applied my changes there.

Here is that code:

<br />
package com.surfrecon.layers;</p>
<p>import java.awt.Component;<br />
import java.awt.Graphics2D;<br />
import java.awt.KeyboardFocusManager;<br />
import java.awt.event.FocusEvent;<br />
import java.awt.event.FocusListener;</p>
<p>import javax.swing.JComponent;<br />
import javax.swing.SwingUtilities;</p>
<p>import org.jdesktop.jxlayer.JXLayer;<br />
import org.jdesktop.jxlayer.plaf.BufferedLayerUI;</p>
<p>public class BlurrableUI< V extends JComponent > extends BufferedLayerUI< V ><br />
{<br />
//	private boolean isLocked;<br />
	private boolean blurEnabled;<br />
    private Component recentFocusOwner;<br />
	private final FocusListener focusListener = new FocusListener()<br />
	{<br />
		public void focusGained( FocusEvent e )<br />
		{<br />
			// we don't want extra repaintings<br />
			// when focus comes from another window<br />
			if ( e.getOppositeComponent() != null )<br />
			{<br />
				setDirty( true );<br />
			}<br />
		}</p>
<p>		public void focusLost( FocusEvent e )<br />
		{<br />
		}<br />
	};</p>
<p>	/**<br />
	 * {@inheritDoc}<br />
	 */<br />
	@Override<br />
	public void installUI( JComponent c )<br />
	{<br />
		super.installUI( c );<br />
		// we need to repaint the layer when it receives the focus<br />
		// otherwise the focused component will have it on the buffer image<br />
		c.addFocusListener( focusListener );<br />
	}</p>
<p>	/**<br />
	 * {@inheritDoc}<br />
	 */<br />
	@Override<br />
	public void uninstallUI( JComponent c )<br />
	{<br />
		super.uninstallUI( c );<br />
		c.removeFocusListener( focusListener );<br />
	}</p>
<p>	public void enableBlur( boolean blurEnabled )<br />
	{</p>
<p>		if ( blurEnabled != isBlurEnabled() )<br />
		{<br />
			if ( getLayer() != null )<br />
			{<br />
				Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();<br />
				boolean isFocusInsideLayer = focusOwner != null && SwingUtilities.isDescendingFrom( focusOwner, getLayer() );</p>
<p>				if ( blurEnabled )<br />
				{<br />
					if ( isFocusInsideLayer )<br />
					{<br />
						recentFocusOwner = focusOwner;<br />
						// setDirty() will be called from the layer's<br />
						// focusListener<br />
						// when focus already left layer's view and hiding it<br />
						// in the paintLayer() won't mess the focus up<br />
						getLayer().requestFocusInWindow();<br />
					}<br />
					else<br />
					{<br />
						setDirty( true );<br />
					}<br />
				}<br />
				else<br />
				{<br />
					// restore the focus if it is still in the layer<br />
					if ( isFocusInsideLayer && recentFocusOwner != null )<br />
					{<br />
						recentFocusOwner.requestFocusInWindow();<br />
					}<br />
					recentFocusOwner = null;<br />
					getLayer().getGlassPane().setCursor( null );<br />
				}<br />
			}<br />
			this.blurEnabled = blurEnabled;<br />
		}<br />
	}</p>
<p>	public boolean isBlurEnabled()<br />
	{<br />
		return blurEnabled;<br />
	}<br />
	// If it is locked, the buffer image will be updated<br />
	// only if the layer changes its size or setDirty(true) was called<br />
	@Override<br />
	protected boolean isIncrementalUpdate( JXLayer< V > l )<br />
	{<br />
		return !isBlurEnabled();<br />
	}</p>
<p>	@Override<br />
	protected void paintLayer( Graphics2D g2, JXLayer< V > l )<br />
	{<br />
		if ( isBlurEnabled() )<br />
		{<br />
			// Note: this code will be called only if layer changes its size,<br />
			// or setDirty(true) was called,<br />
			// otherwise the previously created buffer is used<br />
			l.getView().setVisible( true );<br />
			l.paint( g2 );<br />
			l.getView().setVisible( false );<br />
		}<br />
		else<br />
		{<br />
			// if not locked, paint as usual<br />
			l.paint( g2 );<br />
		}<br />
	}</p>
<p>	// Repaint, if the LookAndFeel was changed<br />
	@Override<br />
	public void updateUI( JXLayer< V > l )<br />
	{<br />
		if ( isBlurEnabled() )<br />
		{<br />
			setDirty( true );<br />
		}<br />
	}<br />
}</p>
<p>	public static class ImageOpUI< V extends JComponent > extends BlurrableUI< V ><br />
	{<br />
		private BufferedImageOpEffect[] blurringEffects;<br />
		private BufferedImageOp blurringEffect;</p>
<p>		/**<br />
		 * @param lockingEffect<br />
		 *            {@link BufferedImageOp} to be applied to the image of<br />
		 *            locked {@link JXLayer}.<br />
		 */<br />
		public ImageOpUI( BufferedImageOp blurringEffect )<br />
		{<br />
			this.blurringEffect = blurringEffect;<br />
			blurringEffects = new BufferedImageOpEffect[ 1 ];<br />
			blurringEffects[ 0 ] = new BufferedImageOpEffect( blurringEffect );<br />
		}</p>
<p>		// This method is overridden to return the specified effect<br />
		// when in locked state<br />
		@Override<br />
		protected LayerEffect[] getLayerEffects( JXLayer< V > l )<br />
		{<br />
			if ( isBlurEnabled() )<br />
			{<br />
				return blurringEffects;<br />
			}<br />
			return super.getLayerEffects( l );<br />
		}</p>
<p>		public void setBlurLevel( int blurLevel )<br />
		{<br />
			if( blurringEffect instanceof ScaledBoxBlurFilter )<br />
			{<br />
				( ( ScaledBoxBlurFilter ) blurringEffect ).setScaleFactor( blurLevel );<br />
				setDirty( true );<br />
			}<br />
		}<br />
	}<br />

Any suggestions?

Thanks,

Steve

Message was edited by: stenew

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
alexfromsun
Offline
Joined: 2005-09-05

Hello Steve

Congratulations on the first post to this forum!
:-)

Actually there is no need to use LockableUI to if you just need to apply a BufferedImageOp to a layer, BufferedLayerUI is the best choice for that:

[code]
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.BufferedLayerUI;
import org.jdesktop.jxlayer.plaf.effect.BufferedImageOpEffect;

import javax.swing.*;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.Arrays;

public class ForSteve {
private static void createGui() {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JPanel panel = new JPanel();
panel.add(new JButton("JButton"));
panel.add(new JCheckBox("JCheckBox"));
panel.add(new JTextField("JTextField"));

JXLayer l = new JXLayer(panel);
BufferedLayerUI bufUI = new BufferedLayerUI();

ConvolveOp op = getConvolveOp(5);
BufferedImageOpEffect blurEffect = new BufferedImageOpEffect(op);
bufUI.setLayerEffects(blurEffect);

l.setUI(bufUI);
frame.add(l);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

private static ConvolveOp getConvolveOp(int i) {
float[] kernel = new float[i * i];
Arrays.fill(kernel, 1f / (i * i));
return new ConvolveOp(new Kernel(i, i, kernel), ConvolveOp.EDGE_NO_OP, null);
}

public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ForSteve.createGui();
}
});
}
}
[/code]

This code works perfectly for a BufferedImageOp which change a pixel's color
and doesn't take into account color of its neighbor's pixels. I mean operation like color inversion, grayscale etc...

Blurring effect is another story, it mixes the colors of the neighbor pixels,
so when e.g. a button is repainted you actually need to repaint not only the button,
but some insets around it as well, to have a consistent blur effect for the whole layer.

Run the code I provided above and type something in the textfield,
you'll see visual artifacts because Swing repaints component partially which doesn't work well with the blur effect.

That's why I use blurring only for locked LockableUI, when its isIncrementalUpdate() method return false, see LockableUI.java for more info.
This method switches off incremental updates for the locked layer.

Making the blurring work for the live layer will require repainting it when any of its subcomponent is repainted, which is very inefficient,
another possibility is subclass BufferedImageOpEffect and ignore the current clip to filter the whole buffer every time, which is also quite expensive

[code]
BufferedImageOpEffect blurEffect = new BufferedImageOpEffect(op) {

public void apply(BufferedImage buffer, Shape clip) {
// ignore the current clip and filter the whole buffer every time,
// note: this may not be the best idea,
// because blurring is a time-consuming operation
super.apply(buffer, null);
}
};
[/code]

Best regards
alexp

stenew
Offline
Joined: 2003-08-21

Thanks Alex, that worked like a charm. I did have to override the BufferedImageOpEffect to apply blur to the entire buffer, as you suggested.

I have a follow-up question. One of the requirements I have is to provide a "blurable browser window". I have created a JPanel that includes a web browser component, (WebRenderer at the moment), and that is what I put in a JXLayer and blur. It seems to work well, that is, the blur is applied appropriately, [b]unless[/b] there is a flash component. The flash always appears unblurred. Is there anything I can do about that? Or is that just a limitation due to the way the browser component renders the flash?

Thanks again for your fine work.

Steve

alexfromsun
Offline
Joined: 2005-09-05

Hello Steve

From your description it seems that WebRenderer uses a heavyweight widget for drawing flash,

if so, there is nothing we can do about it, because it is not painted by Swing

It will work with the JWebPane (when it is available) since it is a lightweight component

Thanks

alexp

stenew
Offline
Joined: 2003-08-21

That's what I was afraid of. It's good news about JWebPane, unfortunately, I need a solution sooner that that will be available.

Thanks again for your help.

Steve

alexfromsun
Offline
Joined: 2005-09-05

Hello Steve

I would contact with WebRenderer support and ask them is it possible to have

a lightweight flash widgets. If not, I am afraid it is impossible to blur live flash animation

The only thing I can imagine, is taking a screenshot of a flash widget with help of awt.Robot,
somehow switch the animation off and show a static blurried image on its place

Thanks

alexp