The Source for Java Technology Collaboration
User: Password:



Alexander Potochkin

Alexander Potochkin's Blog

Making Rainbow: Spotlight effect and soft clipping

Posted by alexfromsun on November 29, 2007 at 11:32 AM | Comments (6)

Few weeks ago I submitted a session for the next JavaOne and it reminded me the previous session which we presented with Kirill

Before J1, he published a nice teaser with links to blogs about his famous ghost effects and transition layout, so it's hight time for me drop a few lines about implementing a spotLight effect. You can test the whole application on the main page of the Rainbow project, here is a flash demo which I made specially for this blog (press the green button on the bottom left side)


The shortest version of the SpotLightPainter was about 30 lines of code. I slightly modified the original painter from the Rainbow project and included the SpotLightPainter and the SpotLightDemo to the demo direcotry for JXLayer.
The painter's code is quite straightforward, there are just two things I'd like to comment:

ForegroundPainter

SpotlightPainter has a layered structure: the scrollPane's view component and the tranclucent foreground with the "holes" for matched components. I divide the painting code between two painters - SpotLightPainter extends DefaultPainter, so the wrapped component is automatically painted when you call super.paint(g2, l), the foregroundPainter is responsible for painting the tranclucent foreground:
    @Override
    public void paint(Graphics2D g2, JXLayer<V> l) {
        super.paint(g2, l);
        foregroundPainter.paint(g2, l);
    }

The foregroundPainter extends AbstractBufferedPainter which means it is painted via the intermediate image, this is the best solution when it comes to translucency. I fill the whole area with the translucent color and then clip out the shapes over the matched components:

        this.foregroundPainter = new AbstractBufferedPainter<V>() {
            @Override
            protected void paintToBuffer(Graphics2D g2, JXLayer<V> l) {
                
                // cleaning up
                g2.setComposite(AlphaComposite.Clear);
                g2.fillRect(0, 0, l.getWidth(), l.getHeight());
                
                // filling with overlayColor
                g2.setComposite(AlphaComposite.SrcOver);
                g2.setColor(overlayColor);
                g2.fillRect(0, 0, l.getWidth(), l.getHeight());
                
                // clipping out the "holes" for matched components
                for (Shape shape : clipList) {
                    g2.setClip(shape);
                    g2.setComposite(AlphaComposite.Clear);
                    g2.fill(shape);
                    softClipping(g2, shape);
                }
            }
        };

The important thing that by default, AbstractBufferedPainter.isIncrementalUpdate() returns false, it means that this painter will be updated only if the whole JXLayer is repainted, e.g. when the frame is resized or when SpotLightPainter.fireLayerItemChanged() is called. This optimization allows reusing the foreground image for faster painting, it doesn't slow down the transition effect animation because when you move the mouse over a button, foregroundPainter paints the cached image.

Soft clipping

When we prepared our session, Kirill drew my attention to the fact that spotlights didn't look smooth, here the zoomed out screenshot which shows how it looked like:
Initial clipping

as the result it was possible to see the jaggies on the spotlight's borders and that made this effect look unprofessional. The remedy for this problem is called "soft clipping" and the pefect article by Chris Cambell is the first thing that came to my mind when I thought about that.

The trick from Chris produces the very good result but it works for fixed shapes only, I mean you should know the exact shape in advance to create a mask for it. I decided to find a way how to make soft clipping for arbitrary shapes and I actually found it, but... a few minutes ago I came across the next Chris's blog Light and Shadow (I missed it for some reason) where he described exactly what I thought was invented by me! :-)
In the comments for that blog there are a few more reports from people who came to this solution even earlier.

The idea is to mix in the alpha value to the pixels on the shape's border by drawing the same shape with a set BasicStroke. For the Rainbow project I painted it once with the 50% alpha and doubled basicStroke width.

Initial clipping

For the JXLayer's demo I updated SpotLightPainter to be able to set the custom width for the soft clipping, and make it as smooth as you want:

    private void softClipping(Graphics2D g2, Shape shape) {
        g2.setComposite(AlphaComposite.Src);
        for (int i = 0; i < softClipWidth; i++) {
            int alpha = (i + 1) * overlayColor.getAlpha()
                    / (softClipWidth + 1);
            Color temp = new Color(
                    overlayColor.getRed(),
                    overlayColor.getGreen(), 
                    overlayColor.getBlue(), alpha);
            g2.setColor(temp);
            g2.setStroke(new BasicStroke(softClipWidth - i));
            g2.draw(shape);
        }
    }

Spotlight demo

MouseEvents filtering

Components which are outside the spotlights shouldn't react on the mouse events and this was the simplest task for the SpotLightPainter
    @Override
    public boolean contains(int x, int y, JXLayer<V> l) {
        for (Shape shape : clipList) {
            if (shape.contains(x, y)) {
                return true;
            }
        }
        return false;
    }

Conclusion

To add this functionality to your application you just need to wrap you component with JXLayer:
JXLayer<JComponent> layer = 
     new JXLayer<JComponent>(myComponent, spotLightPainter)

add(new JScrollPane(layer));

All source and binary files you can find on the SwingHelper project site

Thanks
alexp


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Hi Alex,
    Was just browsing through swing labs when i found jxlayer listed there, which is great...congrats.... The jxlayer seems a well designed and a slightly complex api but which solves many problems very easily... Thanks for such a good api !

    Posted by: psychostud on December 28, 2007 at 09:51 AM

  • Hello Psychostud

    I appreciate it your comment, thank you !
    Making a well designed and easy-to-use API is a difficult task
    and I am happy that you find JXLayer suitable

    By the way I already started working on next verison of JXLayer
    which will be even more powerful, stay tuned !
    Happy New Year !
    alexp

    Posted by: alexfromsun on December 28, 2007 at 10:58 AM

  • Would it be possible to provide WebStart'able demo as well? I'm wary of installing Flash plugin in Firefox, and launching IE just to see the pretty animation... Well, to be honest, it was worth it :)

    Posted by: denka on January 16, 2008 at 09:52 AM

  • Hello Denka

    The webstart demo is under the yellow buttons titled "Launch"
    right on the top of the last screenshot

    Thanks for your interest
    alexp

    Posted by: alexfromsun on January 16, 2008 at 09:56 AM

  • This is a different demo

    Posted by: denka on January 16, 2008 at 10:01 AM

  • Hello Denka

    If you a looking for the Rainbow demo, please visit rainbow.dev.java.net
    and press the webstart button there

    Thanks
    alexp

    Posted by: alexfromsun on January 16, 2008 at 10:19 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds