compression - 构建快速 PNG 编码器问题

标签 compression png zlib libpng encoder

我正在尝试构建一个快速的 8 位灰度 PNG 编码器。不幸的是我一定是误解了规范的一部分。较小的图像尺寸似乎可行,但较大的图像只能在某些图像查看器中打开。该图像(带有多个 DEFLATE block )给出了 我的图像查看器中出现“IDAT 解压缩错误”错误,但在浏览器中打开正常: sample grayscale image

这张图片只有一个 DEFLATE block ,但也给出了一个错误:

smaller sample grayscale image

下面我将概述我在 IDAT block 中放入的内容,以便您可以轻松发现任何错误(注意,图像和步骤已根据答案进行修改,但仍然存在问题):

  1. IDAT长度

  2. ascii 格式的“IDAT”(字面意思是字节 0x49 0x44 0x41 0x54)

  3. Zlib header 0x78 0x01

步骤 4-7 适用于每个 deflate block ,因为数据可能需要分解:

  • 字节 0x00 或 0x01,具体取决于它是中间 block 还是最后一个 block 。

  • 存储为小端 16 位整数的 block 中的字节数(最多 2^16-1)

  • 此整数表示形式的 1 的补码。

  • 图像数据(对于 PNG 中的无滤镜选项,每条扫描线均以零字节开头,后跟灰度像素数据的宽度字节)

  • 所有图像数据的 adler-32 校验和

  • 所有 IDAT 数据的 CRC

  • 我在 Linux 上尝试过 pngcheck,它没有发现任何错误。如果没有人能看出问题所在,你能给我指出调试工具的正确方向吗?

    我的最后手段是使用 libpng 库来制作我自己的解码器,并从那里进行调试。

    有人建议这可能是我的 adler-32 函数计算:

    static uint32_t adler32(uint32_t height, uint32_t width, char** pixel_array)
    {
      uint32_t a=1,b=0,w,h;
      for(h=0;h<height;h++)
        {
          b+=a;
          for(w=0;w<width;w++)
            {
              a+=pixel_array[h][w];
              b+=a;
            }
        }
      return (uint32_t)(((b%65521)*65536)|(a%65521));
    }
    

    请注意,由于传递给函数的 Pixel_array 在每个扫描线的开头不包含零字节(PNG 需要),因此在每次迭代的开头有一个额外的 b+=a (和隐式的 a+=0)外循环。

    最佳答案

    1. The byte 0x00 or 0x80, depending on if it is a middle or the last block.

    0x80更改为0x01,一切都会好起来的。

    0x80 显示为存储 block ,而不是最后一个 block 。我们所关注的只是低位,它为零,表示中间 block 。所有数据都位于该“中间” block 中,因此解码器将恢复完整图像。一些自由派 PNG 解码器可能会忽略在尝试解码下一个不存在的 block 时出现的错误,然后忽略丢失的校验值(Adler-32 和 CRC-32)等。这就是它出现的原因在浏览器中正常,即使它是无效的 PNG 文件。

    您的 Adler-32 代码有两处错误。首先,您从 char 数组访问数据。 char 已签名,因此您的 0xff 字节将被添加为 -127,而不是 255。您需要先创建数组 unsigned char 或将其转换为该数组,然后再从中提取字节值。

    其次,您进行模运算的时间太晚了。您必须在 uint32_t 溢出之前执行 % 65521。否则,您将无法获得算法所需的总和的模。一个简单的修复方法是在宽度循环之后、高度循环内对 ab 执行 % 65521。只要您能保证宽度小于 5551 字节,此方法就可以工作。 (为什么 5551 留给读者作为练习。)如果您不能保证这一点,那么您将需要嵌入另一个循环来消耗该行中的字节,直到达到 5551 个字节,进行取模,然后继续线。或者,稍微慢一点,只需运行一个计数器并在达到极限时进行取模。

    以下是适用于任何宽度的版本示例:

    static uint32_t adler32(uint32_t height, uint32_t width, unsigned char ** pixel_array)
    {
        uint32_t a = 1, b = 0, w, h, k;
        for (h = 0; h < height; h++)
        {
            b += a;
            w = k = 0;
            while (k < width) {
                k += 5551;
                if (k > width)
                    k = width;
                while (w < k) {
                    a += pixel_array[h][w++];
                    b += a;
                }
                a %= 65521;
                b %= 65521;
            }
        }
        return (b << 16) | a;
    }
    

    关于compression - 构建快速 PNG 编码器问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25753695/

    相关文章:

    asp.net - 在 IIS7 中启用动态压缩是否值得?

    algorithm - Weissman 压缩分数是否有效?

    python - PIL - 从 JPG 和 PNG 框架制作 1 个 PNG

    c++ - 来自 cudaMemcpy2D 的错误数据

    javascript - 将 Node 请求或 axios 与流一起使用来下载和解压缩文件未按预期处理背压?

    iphone - 稍后从UIImagePickerController压缩视频吗?

    python - 读取 PNG 像素的 Alpha。通过纯Python 的快速方法?

    javascript - 用 pako(javascript 中的 zlib)压缩,用 zlib(python)解压不起作用

    python - PIL zip jpeg 解码器在运行时不工作,但在安装/自测试时工作

    python - 是否有与适用于 Python 的 Google Closure Compiler 等效的工具?