具有4位调色板的奇怪BufferedImage行为

时间:2014-05-17 04:57:41

标签: java image png bufferedimage

[见related]

以下代码打开一个微小的PNG图像,具有4位调色板和透明度(TRNS块)并打印像素(1,1)和(1,2)的值。然后它将图像从自动类型转换为TYPE_4BYTE_ABGR并打印相同的像素值。

  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);
  }

我得到的结果是

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)

(当选择JAI的阅读器时,选择TYPE_BYTE_INDEXED,但像素值相同)。

我在这里有两个问题:

  1. 根据docs,TYPE_BYTE_BINARY"表示不透明的字节打包1,2或4位图像。该图像具有没有alpha"的IndexColorModel。这似乎与结果相矛盾,结果显示(正确)两个像素的alpha值。

  2. 原始结果(128,226,95,177)是正确的(可以使用任何图像查看器进行验证,或者更好,使用http://entropymine.com/jason/tweakpng/进行验证)。为什么传递给TYPE_4BYTE_ABGR引入了这个(小)错误?

  3. 图像(4x3,右半部分完全透明)位于:https://dl.dropboxusercontent.com/u/1608708/tech/04ptx.png

    我的Java是

    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)
    

2 个答案:

答案 0 :(得分:1)

我可以尝试回答第1项;文档有点不经常,我认为的意图是说图像有IndexColorModel和没有离散的 alpha通道。即:颜色图中只有一个带索引的通道。这与IndexColorModel本身的颜色可能(半)透明的事实并不矛盾。这种解释符合我的经验,但当然,我没有编写API或文档...; - )

第2项看起来有点奇怪。误差非常小+/- 1,所以在实践中它可能没什么大不了的(大多数人看不见)。完整的推测,但我猜想有一些使用的优化循环,这会牺牲一些速度的准确性。您是否尝试使用setRGB()/getRGB()来查看是否会产生不同的结果?

答案 1 :(得分:0)

我同意haraldK的第1点。

对于第2点。简要介绍BufferedImage的源代码,表明TYPE_4BYTE_ABGR使用了ICC配置文件和颜色空间,因此这可能是引入差异的地方。

回答haraldK对这个问题的评论,TYPE_INT_ARGB只使用RGB颜色模型,所以我假设这就是为什么没有错误。

来自BufferedImage的两个相关案例:

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

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

    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;

和ColorSpace中的getInstance方法:

    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;