【问题标题】:Why is my uncompressed PNG pixel data inaccurate?为什么我未压缩的 PNG 像素数据不准确?
【发布时间】:2015-07-17 04:28:58
【问题描述】:

我正在尝试使用 jzlib 扩充 PNG 文件的 IDAT 数据,并以十六进制打印出 R、G、B 和 A 值。我有一个可以运行的程序(没有错误),但打印出来的值似乎不正确。

我的图像是一个 2x2 完全白色的 PNG 文件,没有压缩(来自 Photoshop)。

这是我的代码:

import com.jcraft.jzlib.GZIPException;
import com.jcraft.jzlib.Inflater;
import com.jcraft.jzlib.JZlib;

import java.io.*;

public class main {

    private static final int imageWidth = 2;
    private static final int imageHeight = 2;

    public static void main(String[] args) {
        String pngPath = args[0];
        File pngFile = new File(pngPath);

        try {
            DataInputStream stream = new DataInputStream(new FileInputStream(pngFile));
            byte[] data = new byte[8];
            stream.readFully(data); //Read PNG Header

            while (true) {
                data = new byte[4];
                stream.readFully(data); //Read Length
                int length = ((data[0] & 0xFF) << 24)
                        | ((data[1] & 0xFF) << 16)
                        | ((data[2] & 0xFF) << 8)
                        | (data[3] & 0xFF); //Byte array to int
                stream.readFully(data); //Read Name
                String name = new String(data); //Byte array to String
                if (name.equals("IDAT")) {
                    data = new byte[length];
                    stream.readFully(data); //Read Data

                    int maxInflateBuffer = 4 * (imageWidth + 1) * imageHeight;
                    byte[] outputBuffer = new byte[maxInflateBuffer];

                    long inflatedSize = inflate(data, outputBuffer, maxInflateBuffer);

                    int index = 0;
                    byte r, g, b, a;

                    for (int y = 0; y < imageHeight; y++) {
                        index++;
                        for (int x = 0; x < imageWidth; x++) {
                            r = outputBuffer[index];
                            g = outputBuffer[index + 1];
                            b = outputBuffer[index + 2];
                            a = outputBuffer[index + 3];

                            System.out.println(byteToHex(r) + " : " + byteToHex(g) + " : " + byteToHex(b) + " : " + byteToHex(a));
                            System.out.println("------------------------");

                            index += 4;
                        }
                    }
                    return;
                } else { //Don't care about other chunks
                    data = new byte[length + 4]; //Data length + 4 byte CRC
                    stream.readFully(data); //Skip Data and CRC.
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static long inflate(byte[] data, byte[] outputBuffer, int maxInflateBuffer) throws GZIPException {
        Inflater inflater = new Inflater(15);

        inflater.setInput(data, true);

        inflater.setOutput(outputBuffer);

        try {
            inflater.inflate(JZlib.Z_NO_COMPRESSION);
        } finally {
            inflater.inflateEnd();
        }

        return inflater.getTotalOut();
    }

    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();

    public static String byteToHex(byte b) {
        char[] hexChars = new char[2];
        int v = b & 0xFF;
        hexChars[0] = hexArray[v >>> 4];
        hexChars[1] = hexArray[v & 0x0F];
        return new String(hexChars);
    }
}

这是输出,顺序为 R : G : B : A...

FF : FF : FF : 00
------------------------
00 : 00 : 02 : 00
------------------------
00 : 00 : 00 : 00
------------------------
00 : 00 : 00 : 00
------------------------

我的假设是它看起来更像:

FF : FF : FF : FF
------------------------
FF : FF : FF : FF
------------------------
FF : FF : FF : FF
------------------------
FF : FF : FF : FF
------------------------

为什么不呢?怎么了?我花了一段时间才弄明白。

这是图片的链接: http://i.imgur.com/57lhTXP.png(右键->另存为)

编辑:

我尝试使用 cmets 中建议的 CoderNeji 内置的 Java Inflater 库:

private static long inflate(byte[] data, byte[] outputBuffer, int maxInflateBuffer) {
    Inflater inflater = new Inflater();

    inflater.setInput(data, 0, maxInflateBuffer);

    try {
        inflater.inflate(outputBuffer);
    } catch (DataFormatException e) {
        e.printStackTrace();
    }

    return inflater.getTotalOut();
}

得到了同样的结果。此外,使用任何一种方法,2x2 黑色图像都会给出以下输出:

00 : 00 : 00 : 00
------------------------
00 : 00 : 00 : 00
------------------------
00 : 00 : 00 : 00
------------------------
00 : 00 : 00 : 00
------------------------

感谢您的帮助!

【问题讨论】:

  • 我认为在 java FF FF FF FF 是黑色
  • 我也建议使用 java 内置库来膨胀压缩数据,即 java.util 包。
  • @CoderNeji 查看我的编辑

标签: java png zlib


【解决方案1】:

您忘记了row filters。您在打印时跳过了第一个字节,因此没有看到第一个字节(随后每行所有其他“第一个字节”,这不是一件坏事 - 除非您做得太早了)。
此外,您正在打印 alpha 通道输出,而图像没有。与您所说的相反,图像不是 RGBA,它是 RGB,没有 alpha。线

index += 4;

应该是这样的

index += 3;

通过将打印输出大小与 inflatedSize 得到的大小进行比较,您可能已经注意到了这些错误。实际上,inflatedSize 应该是 14,并且您打印了 16 个字节的数据(并且还跳过了 2 个字节)。

实际解压后的数据,一次一行,是

01 : FF FF FF : 00 00 00
02 : 00 00 00 : 00 00 00

每行的第一个数字是行过滤器,后跟 2(宽度)* 3(通道)像素数据条目。

这 2 行过滤器是 Sub 和 Up,并从左到右、从上到下应用它们,你会得到

FF FF FF : FF FF FF
FF FF FF : FF FF FF

正如人们所期望的那样,它转换为“2x2 全白像素”。

【讨论】:

    猜你喜欢
    • 2011-08-09
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    • 2015-09-02
    • 1970-01-01
    • 2017-03-22
    • 2011-06-24
    • 2018-10-21
    相关资源
    最近更新 更多