JDK-4656879 : more details for AlphaComposite are needed
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: solaris_2.6
  • CPU: sparc
  • Submitted: 2002-03-22
  • Updated: 2006-08-28
  • Resolved: 2006-08-28
Related Reports
Duplicate :  
Description
Name: atR10191			Date: 03/22/2002


Precision in methods using floating point calculations is unspecified.
It makes impossible to create tests for them.

Let's consider drawing functions of class Graphics2D.
Such as drawImage, drawString, draw, and fill.
They are using Composite. It specifies how new pixels are to be combined with
the existing pixels on the graphics device during the rendering
process.

See for example excerpt from the specification for field
public static final int SRC_OVER of class AlphaComposite:

" public static final int SRC_OVER
  Porter-Duff Source Over Destination rule. The source is composited over the destination.
  Fs = 1 and Fd = (1-As), thus:

        Cd = Cs + Cd*(1-As)
        Ad = As + Ad*(1-As)
"

Usually it's possible to achive the same result using diferent
approaches. When floating point calculations are involved, the 'same'
result could be not the same in fact.
For the above equations we can write this:

   float alpha = composite.getAlpha();
   float[] rgbaSrc = cs.getRGBComponents(null);
   float[] rgbaDst = cd.getRGBComponents(null);

   float alphasource = rgbaSrc[3] * alpha;
   float alphadest = rgbaDst[3];
        
   float r = rgbaSrc[0]*alphasource + rgbaDst[0]*alphadest*(1-alphasource);
   float g = rgbaSrc[1]*alphasource + rgbaDst[1]*alphadest*(1-alphasource);
   float b = rgbaSrc[2]*alphasource + rgbaDst[2]*alphadest*(1-alphasource);
   float a = alphasource + alphadest*(1-alphasource);

   Color resultColor = new Color(r, g, b, a);

or this:

   float alpha = composite.getAlpha();
   float alphasource = (cs.getAlpha()/255.0f) * alpha;
   float alphadest = (cd.getAlpha()/255.0f);

   int r = (int) ( cs.getRed()*alphasource + cd.getRed()*alphadest*(1-alphasource));
   int g = (int) ( cs.getGreen()*alphasource + cd.getGreen()*alphadest*(1-alphasource));
   int b = (int) ( cs.getBlue()*alphasource + cd.getBlue()*alphadest*(1-alphasource));
   float a = alphasource + (cd.getAlpha()/255.0f)*(1-alphasource);
   int alpha1= (int)(a*255);

   Color resultColor = new Color(r, g, b, alpha1);

They will produce different results.

 So for such methods should be specified exact algoritm for calculations,
or acceptable fault.

If specification had defined permissible fault in floating point
calculations, e.g. 1 percent (or 0.5 percent) it would be possible to
test assertions such as above.
Here by "fault" I meant acceptable deviation from precise mathematics
value.

Here is sample program demonstrating the problem:

============ Test115x.java ==============================================
import java.awt.*;
import java.awt.image.*;

public class Test115x {

    public static void main(String[] args) {
        new Test115x().testRun();
    }

    void testRun() {
        GraphicsEnvironment lge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsConfiguration gc = lge.getDefaultScreenDevice().getDefaultConfiguration();
        BufferedImage bi = gc.createCompatibleImage(200, 200);
        final Graphics2D g2 = bi.createGraphics();

        Color colors[] =  {
            new Color(Integer.MIN_VALUE + 1, true),
            new Color(Integer.MAX_VALUE - 1, true),
            new Color(Integer.MAX_VALUE, true),
            new Color(0.9f, 0.9f, 0.0f),
            new Color(0.5f, 0.1f, 0.6f, 0.5f)};

        g2.setComposite(AlphaComposite.SrcOver);

        for (int i = 0 ; i < colors.length; i++) {
            g2.setColor(colors[i]);
            g2.fill3DRect(1, 0, 100, 50, true);
            Color c_after = new Color(bi.getRGB(50, 25), true);
    
            System.out.println("Returned color: "+c_after);
        }
        System.exit(0);
    }
}
======== end of Test115x.java ==========================================
======== output under personal basis profile b30 ====================
Returned color: java.awt.Color[r=0,g=0,b=0]
Returned color: java.awt.Color[r=127,g=127,b=126]
Returned color: java.awt.Color[r=190,g=190,b=190]
Returned color: java.awt.Color[r=229,g=229,b=0]
Returned color: java.awt.Color[r=178,g=127,b=76]
======== end of output under personal basis profile b30 =============
======== output under jdk 1.4 ====================
Returned color: java.awt.Color[r=0,g=0,b=1]
Returned color: java.awt.Color[r=127,g=127,b=128]
Returned color: java.awt.Color[r=191,g=191,b=191]
Returned color: java.awt.Color[r=230,g=230,b=0]
Returned color: java.awt.Color[r=179,g=128,b=77]
======== end of output under jdk 1.4 =============
Now we do not have a criterion do decide if output under pbp is valid.
======================================================================

Comments
EVALUATION The fix for 4654826 included a section on the expected accuracy in the "Implementation Caveats" which discusses issues left to the implementation which may affect accuracy as well as the rule that the results need to be compared in the premultiplied form. Note that the particular interface used to extract the data from the image needs to be consulted to see whether it is returning data that is premultiplied or not. Some methods return data in the form specified by the image, some methods return data that is always non-premultiplied (and I don't think we currently have any methods that return data that is always premultiplied, but I haven't done an exhaustive search). As such, I am closing this bug as a duplicate of 4654826. If there are any more specific questions about testing accuracy they should probably be opened in a more specific bug report that refers to the newer docuentation.
28-08-2006