JDK-8176795 : Wrong color drawn when painting translucent colors on volatile images using XRender.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 8,9,10
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux
  • CPU: x86_64
  • Submitted: 2017-03-14
  • Updated: 2021-11-19
  • Resolved: 2018-01-18
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 11 Other
11 b01Fixed openjdk8uResolved
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux osboxes 4.8.0-41-generic #44-Ubuntu SMP Fri Mar 3 15:27:17 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
Current version of Compiz installed.

EXTRA RELEVANT SYSTEM CONFIGURATION :
VM running in VirtualBox.

A DESCRIPTION OF THE PROBLEM :
When painting translucent colors on a volatile image on Linux we get an unexpected result color value.
Eg.:
- first filling volatile image with solid black
- then filling with 50% white (0x80ffffff)
- we would expect medium gray ff808080
Expected color is what we see on Windows OS but not on Linux with Java 8.
There we get ff404040 (see test case).
We could verify with jdk1.8.0_05, jdk1.8.0_60, jdk1.8.0_91, jdk1.8.0_92, jdk1.8.0_121.
Bug does not seem to occur with Java 7, verified with jdk1.7.0_67.

REGRESSION.  Last worked in version 7u76

ADDITIONAL REGRESSION INFORMATION: 
java version "1.7.0_67"
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Please run the test case.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
ff808080
ACTUAL -
ff404040

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package de.centigrade.cezanne.bugs;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;

public class Bug0029487TestCase {

    public static void main(String[] args) throws Exception {

        System.out.println("Java " + System.getProperty("java.version"));
        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsConfiguration translucentGC = null;
        SCREENS: for (GraphicsDevice screen : env.getScreenDevices()) {
            for (GraphicsConfiguration gc : screen.getConfigurations()) {
                if (gc.isTranslucencyCapable()) {
                    translucentGC = gc;
                    break SCREENS;
                }
            }
        }
        if (translucentGC == null) {
            throw new Exception("No suitable gc found.");
        }
        int width = 10;
        int height = 10;
        VolatileImage image = translucentGC.createCompatibleVolatileImage(width, height);
        Graphics2D g = image.createGraphics();
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, width, height);
        g.setColor(new Color(0x80ffffff, true));
        g.fillRect(0, 0, width, height);
        g.dispose();
        // You could also paint image here to verify color value printed below.
        BufferedImage snapshot = image.getSnapshot();
        int argb = snapshot.getRGB(width / 2, height / 2);
        System.out.println(Integer.toHexString(argb));
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
None known for translucent volatile images.


Comments
Summary: Initially i got to know that if disable conversion from non-pre color values to pre color value at XRSolidSrcPict.prepareSrcPict() the problem will disappear but XRender expects pre color values so thought disabling color conversion here would be wrong change. Since there was no problem with color conversion debugged by changing composition rule at different levels of XRenderRectangle and XRenderComposite, that also didn't yield required results. After thorough debugging by adding lot of checks at native XRender code got to know that we are passing wrong color to XRender code when we are preparing the translucent white color for composing it with opaque black color. Even before composition, translucent white RGB color was getting reduced by two times. When we call Graphics2D.setColor() it internally calls validateColor() where we pick the pixel color value using SurfaceData. At this stage itself we convert the non-pre color to pre colors. So there is no need for us to again convert this already converted color values to pre colors in XRSolidSrcPict.prepareSrcPict(). To fix this issue we should remove non-pre to pre color conversion in XRSolidSrcPict.prepareSrcPict(). Also since we do this operation of non-pre to pre color conversion only when we call XRColor.setColorValues() from XRSolidSrcPict.prepareSrcPict() we can completely remove the logic of non-pre to pre color conversion since it is not used in any other calls coming to XRColor.setColorValues().
27-07-2018

URL: http://hg.openjdk.java.net/jdk/jdk/rev/e4b03365ddbf User: prr Date: 2018-01-19 18:52:17 +0000
19-01-2018

URL: http://hg.openjdk.java.net/jdk/client/rev/e4b03365ddbf User: jdv Date: 2018-01-18 05:56:55 +0000
18-01-2018

Dead code removal after changes in XRSolidSrcPict.prepareSrcPict() will be in done in separate bug JDK-8195131.
16-01-2018

The BufferedImage type of snapshot is TYPE_INT_ARGB_PRE. Need to check whether we are considering conversion from premultiplied to non-premultiplied value in BufferedImage.getRGB()
27-11-2017

[~jdv] : Here are few points that I can think of. . VolatileImage.getSnapshot() does not mention about the type of the image returned (including alpha type). It will be interesting to check this image type first. . BufferedImage.getRGB(int x, int y) mentions that it- "Returns an integer pixel in the default RGB color model (TYPE_INT_ARGB)". By TYPE_INT_ARGB, I assume that the returned color value would be non pre-multiplied. . Observation on the above two might provide some clues to the root-cause.
14-11-2017

Important thing to note is XRender expects pre-multiplied alpha color values. When we try to fill the second color in the test case we are manipulating the source RGB values twice. At first it decreases from FFFFFF to 808080 and then to 404040. In the final XRenderFillRectangle call RGB values passed to XRender is 404040. After we draw when we try to retrieve the RGB values using snapshot do we have to convert the pre-multiplied alpha color values to non-premultiplied alpha color values? Have to do more study on XRender extension.
23-10-2017

There is no problem with other types of hardware accelerated surfaces in Unix like GLXVolatileSurface and X11VolatileSurfac, they are compositing colors properly. Also to the native XRenderFillRectangle call we are sending wrong colors to fill, so it is not an issue with XR library. Looks like the issue is with setting compositing rule in XRRender class.
20-10-2017

In case of Unix the GraphicsConfig that is selected is XRGraphicsConfig and it generates XRVolatileSurface which is accelerated. If we force the UnixSurfaceManager to use BufImgVolatileSurface which is not accelerated it is showing proper output. In case of Windows BufImgVolatileSurface is selected so we don't see any problems there.
10-10-2017

Was able to reproduce the issue in Ubuntu 14.04 on latest JDK9 build. We are seeing color ff404040 instead of ff808080.
17-03-2017

Checked this on Ubuntu Linux 16.0.4 LTS and could confirm the issue with JDK 8 and 9 ea. Works fine with Windows (checked in Windows 10, 64-bit) with both 8u121 as well 9 ea b159 and fails in Linux with 8-pool as well as 9 ea. On Linux, received correct value when checked with 7u80 and 7u141 b11. Result (Ubuntu Linux): ======================== 7u80: OK 7u141 b11: OK 8: FAIL 8u121: FAIL 9 ea b159: FAIL Output with 7u80, 8u121: ======================= 8u121: $ java Bug0029487TestCase Java 1.8.0_121 ff404040 7u80: $ java Bug0029487TestCase Java 1.7.0_80 ff808080 This seems a regression issue w.r.t 7 to 8 migration. To verify run the attached test case.
15-03-2017