PNG CRC 是如何准确计算的?

问题描述 投票:0回答:1

过去4个小时我一直在研究CRC算法。我很确定我已经掌握了窍门。

我正在尝试编写一个 png 编码器,并且我不想使用外部库进行 CRC 计算,也不希望使用 png 编码本身。

我的程序已经能够获得与教程示例相同的 CRC。就像维基百科上的那样: enter image description here

使用与示例中相同的多项式和消息,我能够在两种情况下产生相同的结果。我也能够对其他几个示例执行此操作。

但是,我似乎无法正确计算 png 文件的 CRC。我通过在 Paint 中创建一个空白的、一像素大的 .png 文件并使用它的 CRC 作为比较来对此进行测试。我从 png 的 IDAT 块中复制了数据(和块名称)(从中计算 CRC),并使用 png 规范中提供的多项式计算它的 CRC。

png规范中提供的多项式如下:

x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1

应翻译为:

1 00000100 11000001 00011101 10110111

使用该多项式,我尝试获取以下数据的 CRC:

01001001 01000100 01000001 01010100 00011000 01010111 01100011 11101000 11101100 11101100 00000100 00000000 00000011 00111010 00000001 10011100

这就是我得到的:

01011111 11000101 01100001 01101000 (MSB First) 10111011 00010011 00101010 11001100 (LSB First)

这就是实际的 CRC:

11111010 00010110 10110110 11110111

我不太确定如何解决这个问题,但我的猜测是我正在做这部分

来自规范错误:

在 PNG 中,32 位 CRC 被初始化为全 1,然后每个字节的数据从最低有效位 (1) 到最高有效位 (128) 进行处理。处理完所有数据字节后,将 CRC 反转(取其补码)。该值首先传输(存储在数据流中)MSB。为了区分字节和排序,32 位 CRC 的最低有效位被定义为 x31 项的系数。

我不完全确定我能理解所有这些。

另外,这是我用来获取 CRC 的代码:

public BitArray GetCRC(BitArray data) { // Prepare the divident; Append the proper amount of zeros to the end BitArray divident = new BitArray(data.Length + polynom.Length - 1); for (int i = 0; i < divident.Length; i++) { if (i < data.Length) { divident[i] = data[i]; } else { divident[i] = false; } } // Calculate CRC for (int i = 0; i < divident.Length - polynom.Length + 1; i++) { if (divident[i] && polynom[0]) { for (int j = 0; j < polynom.Length; j++) { if ((divident[i + j] && polynom[j]) || (!divident[i + j] && !polynom[j])) { divident[i + j] = false; } else { divident[i + j] = true; } } } } // Strip the CRC off the divident BitArray crc = new BitArray(polynom.Length - 1); for (int i = data.Length, j = 0; i < divident.Length; i++, j++) { crc[j] = divident[i]; } return crc; }

那么,如何修复此问题以匹配 PNG 规范?

c# png crc crc32
1个回答
16
投票
您可以在这个

公共领域代码中找到CRC计算(以及一般的PNG编码)的完整实现:

static uint[] crcTable; // Stores a running CRC (initialized with the CRC of "IDAT" string). When // you write this to the PNG, write as a big-endian value static uint idatCrc = Crc32(new byte[] { (byte)'I', (byte)'D', (byte)'A', (byte)'T' }, 0, 4, 0); // Call this function with the compressed image bytes, // passing in idatCrc as the last parameter private static uint Crc32(byte[] stream, int offset, int length, uint crc) { uint c; if(crcTable==null){ crcTable=new uint[256]; for(uint n=0;n<=255;n++){ c = n; for(var k=0;k<=7;k++){ if((c & 1) == 1) c = 0xEDB88320^((c>>1)&0x7FFFFFFF); else c = ((c>>1)&0x7FFFFFFF); } crcTable[n] = c; } } c = crc^0xffffffff; var endOffset=offset+length; for(var i=offset;i<endOffset;i++){ c = crcTable[(c^stream[i]) & 255]^((c>>8)&0xFFFFFF); } return c^0xffffffff; }
    
© www.soinside.com 2019 - 2024. All rights reserved.