我在编写PNG解码器,遇到了一些奇怪的问题。通过PNG文件格式,我设法正确地解码了Color Index + tRNS(alpha)和True Color + tRNS(alpha)的PNG。我目前不确定为什么无法使用Alpha类型PNG解码具有True Color的PNG。我已经验证我的IDAT区块膨胀正确。这是大块的样子:

Width: 256
Height: 256
Bit Depth: 8
Color Type: 6
Compression: 0
Filter: 0
Interlace: 0

Length: 25
Type: tEXt
Data: 53 6f 66 74 77 61 72 65 00 41 64 6f 62 65 20 49 6d 61 67 65 52 65 61 64 79
CRC: 71c9653c

Length: 20690
Type: IDAT
Data: 78 da ec 7d 09 9c 1c 57 99 df f7 7a a6 e7 3e 5a a3 fb ...
CRC: 21550259


实际数据太长,无法在此处打印。这是我解码的逻辑,如果我错了,请纠正我:


膨胀IDAT块中给定的所有字节
取消过滤膨胀的块。在这种情况下,所有过滤器的类型均为0,因此,我们仅丢弃过滤器字节。
由于这是6种颜色,因此像素由RGBA通道表示,每个通道1字节。这意味着我们需要一次解释4个字节。使用以下代码:

ByteBuffer image = BufferUtil.getDirectByteBuffer(data.length, ByteOrder.BIG_ENDIAN);
int i = 0;
while(i < data.length){
int color = ( (data[i] & 0xff) << 24) | ( (data[i+1] & 0xff) << 16) | ( (data[i+2] & 0xff) << 8) | (data[i+3] & 0xff);
image.putInt(color);
i += 4;



奇怪的是,我得到的大多数数据都是RRGGBBAA = 0x00000000,导致图像清晰,色彩很少。

最佳答案

问题是您忽略了对每条扫描线的过滤。

根据提供的图像,解压缩后的数据看起来像

1 ffffffff 0 0 0 ...
2 0 0 0 0 0 0 ...
..

每行中的第一个值均符合使用的过滤方法[http://www.w3.org/TR/PNG/#9Filters]

扫描线的后期处理看起来像

ffffffff ffffffff ffffffff ...
ffffffff ffffffff ffffffff ...
...

这是一些处理方法0、1和2的示例代码。

private static void processScanLine(byte filterValue, byte[] scanLine, byte[] previousScanLine) {
    switch(filterValue){
        case 0:break;
        case 1:
            for (int i =4;i<scanLine.length;i++){
                scanLine[i] = (byte)(scanLine[i]+scanLine[i-4]);
            }
        break;
        case 2:
            for (int i =0;i<scanLine.length;i++){
                scanLine[i] = (byte)(scanLine[i]+previousScanLine[i]);
            }
        break;
    }
}

09-11 20:15