JDK-8017626 : [OGL] Translucent VolatileImages don't paint correctly
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 7u6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2013-06-24
  • Updated: 2019-10-17
  • Resolved: 2014-06-04
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9 Other
8u20Fixed 9 b22Fixed openjdk7uFixed
Description
FULL PRODUCT VERSION :
java version  " 1.7.0_40-ea " 
Java(TM) SE Runtime Environment (build 1.7.0_40-ea-b29)
Java HotSpot(TM) 64-Bit Server VM (build 24.0-b48, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Mac 10.8.3

EXTRA RELEVANT SYSTEM CONFIGURATION :
Graphics Card: Intel HD Graphics 3000.
This has also happened with other Macs, regardless of Graphics Card.

A DESCRIPTION OF THE PROBLEM :
When drawing a translucent VolatileImage to a BufferedImage (or calling VolatileImage.getSnapshot()) and then drawing that BufferedImage to the screen graphics, the translucent pixels are rendered incorrectly.  Instead of being rendered as translucent, they are rendered with a black background.

If the VolatileImage is drawn right to the screen graphics, this problem does not occur.

REGRESSION.  Last worked in version 6u45

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a translucent VolatileImage
2. Clear it
3. Fill with a half opaque color (such as rgba(255, 255, 255, 128))

4a. Create a BufferedImage of type INT_ARGB
4b. Draw the VolatileImage to the BufferedImage's graphics

-- or instead of doing steps 4a and 4b, do:
4. Create a BufferedImage by calling VolatileImage.getSnapshot()
---

5. Draw the BufferedImage obtained in steps 4a/4b or 4 to the screen graphics.



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I expect the BufferedImage created in steps 4 or 4a/4b to look exactly as if I had created a BufferedImage and filled it with the half opaque color and then painted it to the screen graphics.

I also expect the BufferedImage created in steps 4 or 4a/4b to look exactly like the VolatileImage when the VolatileImage is painted directly to the screen graphics.
ACTUAL -
The BufferedImage created in steps 4 or 4a/4b has a black tinge to it where the pixels are supposed to be translucent.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/**
 * Shows the difference between the painting of a translucent BufferedImage and a translucent
 * VolatileImage.
 *
 *
 * On Java 6, this works fine.  On Java 7, the VolatileImages are not painted correctly, when painted
 * onto a BufferedImage prior to being painted to screen graphics.
 *
 * @author jfinley
 *
 */
public class PaintTest {

public PaintTest() {

JPanel bufferedImagePanel = new JPanel() {
protected void paintComponent(java.awt.Graphics g) {
super.paintComponent(g);
BufferedImage image = getBufferedImage();
g.drawImage(image, 0, 0, null);
};
};

JPanel bufferedImagePanel2 = new JPanel() {
protected void paintComponent(java.awt.Graphics g) {
super.paintComponent(g);
BufferedImage bImage = getBufferedImage();

BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = image.createGraphics();

//not really necessary, but in there for completeness
g2.setComposite(AlphaComposite.Clear);
g2.setColor(new Color(255, 255, 255, 0));
g2.fillRect(0,0, image.getWidth(), image.getHeight());

g2.setComposite(AlphaComposite.SrcOver);
g2.drawImage(bImage,  0, 0, null);
g2.dispose();

g.drawImage(image, 0, 0, null);
};
};

JPanel volatileImagePanel = new JPanel() {
protected void paintComponent(java.awt.Graphics g) {
super.paintComponent(g);
VolatileImage vImage = getVolatileImage();

/**
 * If we draw the VolatileImage directly to g, it works fine... but
 * if we draw VolatileImage.getSnapshot(), or otherwise convert the VolatileImage
 * to a buffered image, we get the black halo funk of death.
 */
g.drawImage(vImage, 0, 0, null); //works
//g.drawImage(vImage.getSnapshot(), 0, 0, null); //black halo funk of death
};
};

JPanel volatileImagePanel2 = new JPanel() {
protected void paintComponent(java.awt.Graphics g) {
super.paintComponent(g);
VolatileImage vImage = getVolatileImage();

/**
 * If we draw the VolatileImage directly to g, it works fine... but
 * if we draw VolatileImage.getSnapshot(), or otherwise convert the VolatileImage
 * to a buffered image, we get the black halo funk of death.
 */
//g.drawImage(vImage, 0, 0, null); //works
g.drawImage(vImage.getSnapshot(), 0, 0, null); //black halo funk of death
};
};

JPanel volatileImagePanel3 = new JPanel() {
protected void paintComponent(java.awt.Graphics g) {
super.paintComponent(g);
VolatileImage vImage = getVolatileImage();

/**
 * If we draw the VolatileImage directly to g, it works fine... but
 * if we draw VolatileImage.getSnapshot(), or otherwise convert the VolatileImage
 * to a buffered image, we get the black halo funk of death.
 */
//g.drawImage(vImage, 0, 0, null); //works
//g.drawImage(vImage.getSnapshot(), 0, 0, null); //black halo funk of death

BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = image.createGraphics();

//not really necessary, but in there for completeness
g2.setComposite(AlphaComposite.Clear);
g2.setColor(new Color(255, 255, 255, 0));
g2.fillRect(0,0, image.getWidth(), image.getHeight());

g2.setComposite(AlphaComposite.SrcOver);
g2.drawImage(vImage,  0, 0, null);
g2.dispose();

g.drawImage(image, 0, 0, null);
};
};

final JPanel mainPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints(0, 1, 1, 1, 0, 0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);

mainPanel.add(new JLabel( " Direct to g " ), gbc);
gbc.gridy++;
mainPanel.add(new JLabel( " BufferImage Proxy " ), gbc);
gbc.gridy++;
mainPanel.add(new JLabel( " BufferImage Proxy 2 " ), gbc);

gbc.gridx = 1; gbc.gridy = 0;
mainPanel.add(new JLabel( " BufferedImages " ), gbc);
gbc.gridx++;
mainPanel.add(new JLabel( " VolatileImages " ), gbc);

gbc.gridx = 1; gbc.gridy = 1; gbc.weightx = 1; gbc.weighty = 1;
mainPanel.add(bufferedImagePanel, gbc);
gbc.gridy++;
mainPanel.add(bufferedImagePanel2, gbc);
gbc.gridx = 2; gbc.gridy = 1;
mainPanel.add(volatileImagePanel, gbc);
gbc.gridy++;
mainPanel.add(volatileImagePanel2, gbc);
gbc.gridy++;
mainPanel.add(volatileImagePanel3, gbc);


JButton repaintButton = new JButton( " Repaint " );
repaintButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
mainPanel.repaint();
}
});

gbc.gridx = 1; gbc.gridy++; gbc.gridwidth = 2; gbc.weighty = 0;
mainPanel.add(repaintButton, gbc);


JFrame mainFrame = new JFrame( " O.M.G. " );
mainFrame.getContentPane().setLayout(new BorderLayout());
mainFrame.getContentPane().add(mainPanel, BorderLayout.CENTER);
mainFrame.setPreferredSize(new Dimension(400, 400));
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.pack();
mainFrame.setVisible(true);

}

private BufferedImage getBufferedImage() {
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TRANSLUCENT);
Graphics2D g = image.createGraphics();

//not really necessary, in here for completeness
g.setComposite(AlphaComposite.Clear);
g.setColor(new Color(255, 255, 255, 0));
g.fillRect(0,0, image.getWidth(), image.getHeight());

g.setComposite(AlphaComposite.SrcOver);
g.setColor(new Color(255, 0, 0, 128));
g.fillRect(10, 10, image.getWidth()-20, image.getHeight()-20);

g.dispose();
return image;
}

private VolatileImage getVolatileImage() {
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
VolatileImage vImage = gc.createCompatibleVolatileImage(100, 100, Transparency.TRANSLUCENT);
Graphics2D g = vImage.createGraphics();

//necessary, as VolatileImages start out filled opaque white
g.setComposite(AlphaComposite.Clear);
g.setColor(new Color(255, 255, 255, 0));
g.fillRect(0,0, vImage.getWidth(), vImage.getHeight());

g.setComposite(AlphaComposite.SrcOver);
g.setColor(new Color(255, 0, 0, 128));
g.fillRect(10, 10, vImage.getWidth()-20, vImage.getHeight()-20);

g.dispose();
return vImage;
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new PaintTest();
}
});
}
}
---------- END SOURCE ----------
Comments
I can reproduce the problem on windows as well, if opengl pipeline is enabled.
29-04-2014

These are all approved for deferral to JDK 9 so you can update the FixVersion to state JDK 9. Kind regards, Mathias
29-08-2013

These are all approved for deferral to JDK 9 so you can update the FixVersion to state JDK 9. Kind regards, Mathias
29-08-2013

These are all approved for deferral to JDK 9 so you can update the FixVersion to state JDK 9. Kind regards, Mathias
29-08-2013

Converted "8-client-defer-candidate" label to "8-defer-request" by SQE' OK.
15-08-2013

*This is anti-deferral criteria list*: - P2 -------------- Engineering's Criteria ------------------------------------- - tck-red labeled - conformance labeled - P3 regressions reported/labeled against jdk8 - findbugs, parfait, eht labeled bugs - CAP <1 year reported - netbeans <1 year reported Victor ----------------- SQE's OK --------------------------------- Yes, we are ok with that thanks, Mikhail
15-08-2013

8-defer-request: the issue is reproduced from 7GA (7u6 on Mac OS X)
11-07-2013

is it affected 7u6?
11-07-2013

is it critical to 7u40?
27-06-2013