AlphaComposite.SRC_OVER operation has unexpected result on 8 bit display

XMLWordPrintable

    • Type: Bug
    • Resolution: Fixed
    • Priority: P3
    • 6
    • Affects Version/s: 1.4.1
    • Component/s: client-libs
    • None
    • 2d
    • beta
    • sparc
    • solaris_7

      When doing alpha composite in a buffered image with AlphaComposite.SRC_OVER
      rule on a 8 bit display, the output of the pixel values are sometimes
      unexpected.

      Here is a test program:

      import java.awt.*;
      import java.awt.image.*;

      public class SrcOverTest {
          public static final BufferedImage bi;
          public static final Graphics2D g2;
          protected static int colorDepth, maxError;

          public static final Color colors[] = { new Color(255, 0, 255),
              Color.black, Color.red, Color.pink, Color.orange, Color.yellow,
              Color.green, Color.magenta, Color.cyan, Color.blue};

          static {
              GraphicsEnvironment lge =
                  GraphicsEnvironment.getLocalGraphicsEnvironment();
              GraphicsConfiguration gc =
                  lge.getDefaultScreenDevice().getDefaultConfiguration();
              bi = gc.createCompatibleImage(200, 200);
              g2 = bi.createGraphics();
              g2.setColor(new Color(0, 255, 0));
              g2.fill3DRect(1, 0, 100, 50, true);
              
          }
              
          public static Color accountForColorConversion(Color c) {
               BufferedImage bi2 = GraphicsEnvironment.
                  getLocalGraphicsEnvironment().getDefaultScreenDevice().
                  getDefaultConfiguration().createCompatibleImage(1,1);
              int x = 0;
              int y = 0;

              // Set a pixel with the input color
              int rgb = c.getRGB();
              bi2.setRGB(x, y, rgb);

              // Read the pixel we just set, BufferedImage should take care
              // of the color conversion
              rgb = bi2.getRGB(x,y);

              // Return the converted color
              return new Color(rgb);
          }

          public static Color calcSrcOver(Color cb, Color c, float alpha, ColorModel colorModel) {
              boolean hasAlpha = colorModel.hasAlpha();
              float[] rgbaSrc = c.getRGBComponents(null);
              float[] rgbaDst = cb.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);

              if (hasAlpha) {
                  return new Color(r, g, b, a);
              } else if (a != 0.0f) {
                  return new Color(r / a, g / a, b / a, 1.0f);
              } else {
                  return new Color(0.0f, 0.0f, 0.0f, 1.0f);
              }
          }
          
          public static void main(String[] args) {
              float alphas[] = {0.0f, 0.5f, 1.0f};
              for (int j = 0; j < alphas.length; j++) {
                  for (int i = 0; i < colors.length; i++) {
                      Color c_before = new Color(bi.getRGB(50, 25), true);
                      AlphaComposite composite =
                          AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alphas[j]);
                      g2.setComposite(composite);
                      Color c = accountForColorConversion(colors[i]);
                      g2.setColor(c);
                      g2.fill3DRect(1, 0, 100, 50, true);
                      Color c_after = new Color(bi.getRGB(50, 25), true);
          
                      Color c_expected = calcSrcOver(c_before, c, alphas[j],
                                                     bi.getColorModel());
                      System.out.println("ColorModel has alpha: " + bi.getColorModel().hasAlpha());
                      System.out.println("alpha of the composite: " + composite.getAlpha());
                      System.out.println("Source Color: " + c + ", alpha: " + c.getAlpha());
                      System.out.println("Dest Color: " + c_before + ", alpha: " + c_before.getAlpha());
                      System.out.println("Returned color: "+ c_after +
                                             ", alpha: " + c_after.getAlpha());
                      System.out.println("Expected color: " + c_expected +
                                          ", alpha: " + c_expected.getAlpha());
                      System.out.println();
                  }
              }
          }
      }

      Attached is the output on 24 bit display and output on 8 bit display. It shows
      that the all resulting colors on 24 bit display are consistent with expected
      values. However, some resulting colors on 8 bit display are not within
      expectation, especially when alpha of blending is not 1.0, for example:

      ColorModel has alpha: false
      alpha of the composite: 0.0
      Source Color: java.awt.Color[r=255,g=0,b=255], alpha: 255
      Dest Color: java.awt.Color[r=0,g=255,b=0], alpha: 255
      Returned color: java.awt.Color[r=59,g=250,b=52], alpha: 255
      Expected color: java.awt.Color[r=0,g=255,b=0], alpha: 255
      ...

      ColorModel has alpha: false
      alpha of the composite: 0.5
      Source Color: java.awt.Color[r=0,g=0,b=0], alpha: 255
      Dest Color: java.awt.Color[r=197,g=98,b=139], alpha: 255
      Returned color: java.awt.Color[r=127,g=0,b=0], alpha: 255
      Expected color: java.awt.Color[r=99,g=49,b=70], alpha: 255

      It is acceptable that there might be some errors in
      floating point calculation, but some returned values are way off.
      For instance, in the first 8 bit output example, the alpha is 0, which means
      the resulting color should be the same as the original destination color.
      But the actual resulting color is totally unexpected.

            Assignee:
            Christopher Campbell (Inactive)
            Reporter:
            Xiaozhong Wang (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: