Search code examples
javaswingrotationtransparencypaintcomponent

Rotation of Transparent Image Over Transparent Panel


I've been working on a program that involves the rotation of a partially transparent image over a transparent form. The drawing of the image originally worked fine, I also set my custom panel's background color to a transparent light blue this worked fine as well. My problems started when I tried rotating my image.

In order to rotate it I had to convert the panel.getGraphics() over to a Graphics2D. When I did this the transparency went away, So I finished up on my rotation code and then read up on transparency. I found that I could set the composite of the Graphics2D and that is exactly what I did as seen here:

@Override
public void paintComponent(Graphics g) 
{
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D)g;

    g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
    g2d.setColor(new Color(0, 0, 200, 90));
    g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
    g2d.rotate(radians);
    g2d.drawImage(img, 0, 0, null);

    repaint();
}

When I run this I get the form as follows (Please note that this is not my normal image):

Before Rotation

This is almost what I want except for it doesn't show the transparent blue background. However if I rotate the image the blue shows:

After Rotation


Solution

  • Problem is partially in the composite you specify: AlphaComposite.SRC
    I don't really know what for did you use it but it overwrites source pixels data. That is why panel background gets overwrited when image is painted over it.

    I suggest you to read about composite in graphics if you didn't read it yet: http://docs.oracle.com/javase/tutorial/2d/advanced/compositing.html

    Anyway, see the example how something similar could be done:
    (this is just one of possibilities - you could do it in ten other ways)

    public class SmileyTest
    {
        private static Color bg = new Color ( 0, 0, 255, 128 );
        private static float angle = 0f;
    
        public static void main ( String[] args )
        {
            final ImageIcon icon = new ImageIcon ( SmileyTest.class.getResource ( "icons/smiley.png" ) );
    
            JDialog frame = new JDialog ();
            frame.setLayout ( new BorderLayout () );
    
            // We should not use default background and opaque panel - that might cause repaint problems
            // This is why we use JPanel with transparent background painted and opacity set to false
            JPanel transparentPanel = new JPanel ( new BorderLayout () )
            {
                protected void paintComponent ( Graphics g )
                {
                    super.paintComponent ( g );
                    g.setColor ( bg );
                    g.fillRect ( 0, 0, getWidth (), getHeight () );
                }
            };
            transparentPanel.setOpaque ( false );
            frame.add ( transparentPanel );
    
            // Image in another component
            final JComponent component = new JComponent ()
            {
                protected void paintComponent ( Graphics g )
                {
                    super.paintComponent ( g );
    
                    Graphics2D g2d = ( Graphics2D ) g;
    
                    // For better image quality when it is rotated
                    g2d.setRenderingHint ( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
    
                    // Rotating area using image middle as rotation center
                    g2d.rotate ( angle * Math.PI / 180, getWidth () / 2, getHeight () / 2 );
    
                    // Transparency for image
                    g2d.setComposite ( AlphaComposite.getInstance ( AlphaComposite.SRC_OVER, 0.5f ) );
    
                    // Draing image
                    g2d.drawImage ( icon.getImage (), 0, 0, null );
                }
            };
            transparentPanel.add ( component );
    
            // Rotation animation (24 frames per second)
            new Timer ( 1000 / 48, new ActionListener ()
            {
                public void actionPerformed ( ActionEvent e )
                {
                    angle += 0.5f;
                    component.repaint ();
                }
            } ).start ();
    
            frame.setUndecorated ( true );
            AWTUtilities.setWindowOpaque ( frame, false );
            frame.setSize ( icon.getIconWidth (), icon.getIconHeight () );
            frame.setLocationRelativeTo ( null );
            frame.setVisible ( true );
        }
    }
    

    Just run this and see the result: enter image description here

    There are also a few comments over the code why you should or shouldn't do something.
    Make sure you read them carefully.