1

[See related]

The following code opens a tiny PNG image, with a 4-bits palette and transparency (TRNS chunk) and prints the value of the pixels (1,1) and (1,2). Then it converts the image from the automatic type to TYPE_4BYTE_ABGR and prints the same pixels values.

  public static void images() throws IOException {
    File png = new File("c:\\temp\\04ptx.png");

    BufferedImage bi1 = ImageIO.read(png);
    System.out.printf("%s is TYPE_BYTE_BINARY? %s (%d)\n",png, 
        String.valueOf(BufferedImage.TYPE_BYTE_BINARY==bi1.getType()),
        bi1.getType());
    int p1i1 = bi1.getRGB(1, 1);
    int p2i1 = bi1.getRGB(2, 1);
    System.out.printf("im1: p1=%08x %s  p2=%08x %s\n",
         p1i1,formatARGB(p1i1),p2i1,formatARGB(p2i1));

    BufferedImage bi2 = new BufferedImage(bi1.getWidth(), bi1.getHeight(), 
        BufferedImage.TYPE_4BYTE_ABGR);
    bi2.getGraphics().drawImage(bi1, 0, 0, null);
    int p1i2 = bi2.getRGB(1, 1);
    int p2i2 = bi2.getRGB(2, 1);
    System.out.printf("im2: p1=%08x %s  p2=%08x %s\n",
        p1i2,formatARGB(p1i2),p2i2,formatARGB(p2i2));
  }

  public static String formatARGB(int v) {
    return String.format("(%d,%d,%d,%d)", 
        (v>>24)&0xFF,(v>>16)&0xFF,(v>>8)&0xFF,v&0xFF);
  }

The result I get is

c:\temp\04ptx.png is TYPE_BYTE_BINARY? true (12)
im1: p1=80e25fb1 (128,226,95,177)  p2=00000000 (0,0,0,0)
im2: p1=80e160b1 (128,225,96,177)  p2=00000000 (0,0,0,0)

(When the reader from JAI gets selected, the TYPE_BYTE_INDEXED is chosen instead, but the pixel values are the same).

I have two questions here:

  1. According to docs, TYPE_BYTE_BINARY "represents an opaque byte-packed 1, 2, or 4 bit image. The image has an IndexColorModel without alpha". This seems in contradiction with the results, which shows (correctly) the alpha value for both pixels.

  2. The original result (128,226,95,177) is correct (one can verify with any image viewer, or, better, with http://entropymine.com/jason/tweakpng/). Why passing to TYPE_4BYTE_ABGR introduces this (small) error?

The image (4x3, right half is fully transparent) is here: https://dl.dropboxusercontent.com/u/1608708/tech/04ptx.png

My Java is

java version "1.7.0_17"
Java(TM) SE Runtime Environment (build 1.7.0_17-b02)
Java HotSpot(TM) Client VM (build 23.7-b01, mixed mode, sharing)

Community
  • 1
  • 1
leonbloy
  • 73,180
  • 20
  • 142
  • 190
  • How is (128,226,95,177) "correct" for a 4-bit image input? There are several ways of promoting a 4-bit value (range: 0..15) to 8 bits (range: 0..255). – Jongware May 17 '14 at 22:28
  • @Jongware: No, each pixel in the image is a 4-bit index that points to a palette of (up to) 16 colours; but the colours in the palette have 8 bits per component (alpha included) – leonbloy May 17 '14 at 23:07
  • Ah right -- you mention it has a palette in your post. Aplogies for misunderstanding. – Jongware May 17 '14 at 23:17
  • Combined with my ["fix" for the other issue](http://stackoverflow.com/a/23725014/1428606) and changing `bi2`'s type to `TYPE_INT_ARGB` gives equal values for me. Now, someone only needs to explain why *that* happens.. :-P – Harald K May 18 '14 at 18:19

2 Answers2

1

I can try to answer item 1; The docs is a little unprecise, and I think the intention is to say that the image has IndexColorModel and no discrete alpha channel. I.e.: There's just a single channel with indices in the color map. That is not in contradiction to the fact that the colors in the IndexColorModel themselves may be (semi-) transparent. This explanation fits with my experience, but of course, I didn't write the API or the docs... ;-)

Item 2 looks a little strange. The error is very small +/- 1, so it's probably no big deal in practice (invisible to most peoples eyes). Complete speculation, but I would guess that there's some kind of optimized loops being used, that sacrifices some accuracy for speed. Have you tried using setRGB()/getRGB() to see if that gives different results?

Harald K
  • 26,314
  • 7
  • 65
  • 111
  • The error is small, but it should not happen, there is no color model convertion or scaling involved. – leonbloy May 17 '14 at 12:37
0

I would agree with haraldK on point 1.

For point 2. A brief dig through the source code of BufferedImage indicates that TYPE_4BYTE_ABGR uses an ICC profile and colour space, so that is probably where the difference is introduced.

Answering haraldK's comment on the question, TYPE_INT_ARGB just uses a RGB colour model, so I'd assume that is why there is no error there.

The two relevant cases from BufferedImage:

    case TYPE_INT_ARGB:
        {
            colorModel = ColorModel.getRGBdefault();

            raster = colorModel.createCompatibleWritableRaster(width,
                                                               height);
        }
    break;

and

    case TYPE_4BYTE_ABGR:
        {
            ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
            int[] nBits = {8, 8, 8, 8};
            int[] bOffs = {3, 2, 1, 0};
            colorModel = new ComponentColorModel(cs, nBits, true, false,
                                                 Transparency.TRANSLUCENT,
                                                 DataBuffer.TYPE_BYTE);
            raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
                                                    width, height,
                                                    width*4, 4,
                                                    bOffs, null);
        }
    break;

and from the getInstance method in ColorSpace:

    case CS_sRGB:
        synchronized(ColorSpace.class) {
            if (sRGBspace == null) {
                ICC_Profile theProfile = ICC_Profile.getInstance (CS_sRGB);
                sRGBspace = new ICC_ColorSpace (theProfile);
            }

            theColorSpace = sRGBspace;
        }
        break;
Chris Kent
  • 862
  • 11
  • 18
  • Didn't see your answer before now, but `ColorModel.getRGBdefault()` will return a `DirectColorModel` backed by the same `ColorSpace` and ICC profile (sRGB) as the ones used by `TYPE_4BYTE_ABGR`, so there's no difference there. – Harald K Jun 10 '14 at 12:48