Как вычисляется PNG CRC точно?

в течение последних 4 часов я изучал алгоритм CRC. Я уверен, что уже освоился.

Я пытаюсь написать кодировщик png, и я не хочу использовать внешние библиотеки для вычисления CRC, ни для самой кодировки png.

моя программа смогла получить те же CRC, что и примеры на учебниках. Как на Википедия: enter image description here

используя тот же полином и сообщение, что и в примере, я смог в обоих случаях результат один и тот же. Я смог сделать это и для нескольких других примеров.

однако я не могу правильно рассчитать CRC файлов png. Я проверил это, создав пробел размером в один пиксель .png-файл в paint, и использование его CRC в качестве сравнения. Я скопировал данные( и имя фрагмента) из фрагмента IDAT png (который CRC рассчитывается из), и вычисляется CRC с использованием полинома, предоставленного в png спецификация.

многочлен, указанный в спецификации 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?

1 ответов


вы можете найти полную реализацию вычисления CRC (и кодировку PNG в целом) в этом коде общественного достояния:http://upokecenter.dreamhosters.com/articles/png-image-encoder-in-c/

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