Байты png повреждаются в C

#c #png #zlib

#c #png #zlib

Вопрос:

Я хочу написать свой собственный png Reader, используя небольшую помощь из других библиотек. Пока я могу отлично считывать изображения только с одним фрагментом IDAT. Однако, когда мне нужно объединить несколько фрагментов, файл оказывается поврежденным. Вот пример одного из них: Файл с несколькими идентификаторами был поврежден

У меня много кода, поэтому я попытаюсь сделать его «минимально воспроизводимым примером», насколько смогу, но это, наряду с тем фактом, что я не знаю конкретно, какая часть вызывает это, сделает его немного длиннее, чем я уверен, некоторые из васможет понравиться, и я сожалею.

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zlib.h"

unsigned char IEND_CHUNK[4] = {'I', 'E', 'N', 'D'}; //Indicates End Of File
unsigned char IDAT_CHUNK[4] = {'I', 'D', 'A', 'T'}; //Holds Pixel Information

typedef struct Chunk Chunk;
typedef struct IHDR IHDR;

struct Chunk {
    int length;
    unsigned char type[4];
    unsigned char *data;
    unsigned char crc[4];
};
struct IHDR {
    int width;
    int height;
    int bitd;
    int colort;
    int compm;
    int filterm;
    int interlacem;
    int channels;
    int bytesPerPixel;
};

int bytesToInt(unsigned char a, unsigned char b, unsigned char c, unsigned char d)
{
    return (int) a << 24 | b << 16 | c << 8 | d;
}

int compType(unsigned char *a, unsigned char b[], int len)
{
    int equal = 1;
    for(int i = 0; i < len; i  ) equal = a[i] == b[i] ? equal : 0;
    return equal;
}

Chunk* getChunksFromBytes(unsigned char* bytes)
{
    int next_seg = 7;
    int chunks = 0;
    long int size = 1;
    Chunk* temp = (Chunk*) malloc(sizeof(Chunk));

    while(1){
        size  = sizeof(Chunk);
        temp = realloc(temp, size);

        temp[chunks].length = bytesToInt(bytes[next_seg 1], bytes[next_seg 2],
                                      bytes[next_seg 3], bytes[next_seg 4]);

        temp[chunks].type[0] = bytes[next_seg 5];
        temp[chunks].type[1] = bytes[next_seg 6];
        temp[chunks].type[2] = bytes[next_seg 7];
        temp[chunks].type[3] = bytes[next_seg 8];

        temp[chunks].data = malloc(temp[chunks].length);
        if(temp[chunks].length > 0){
            memcpy(temp[chunks].data, bytes next_seg 9, temp[chunks].length);
        }
        else temp[chunks].data = NULL;

        temp[chunks].crc[0] = bytes[next_seg temp[chunks].length 9];
        temp[chunks].crc[1] = bytes[next_seg temp[chunks].length 10];
        temp[chunks].crc[2] = bytes[next_seg temp[chunks].length 11];
        temp[chunks].crc[3] = bytes[next_seg temp[chunks].length 12];

        if(compType(temp[chunks].type, IEND_CHUNK, 4)) break;
        next_seg =temp[chunks].length 12;
        chunks  ;
    }

    return temp;
}

IHDR getIHDRFromChunks(Chunk* chunks)
{
    IHDR img_info;
    int channels[7] = {1, 0, 3, 1, 2, 0, 4};

    img_info.width = bytesToInt(chunks[0].data[0], chunks[0].data[1],
                                chunks[0].data[2], chunks[0].data[3]);
    img_info.height = bytesToInt(chunks[0].data[4], chunks[0].data[5],
                                 chunks[0].data[6], chunks[0].data[7]);

    img_info.bitd = (int) chunks[0].data[8];
    img_info.colort = (int) chunks[0].data[9];
    img_info.compm = (int) chunks[0].data[10];

    img_info.filterm = (int) chunks[0].data[11];
    img_info.interlacem = (int) chunks[0].data[12];
    img_info.channels = channels[img_info.colort];

    img_info.bytesPerPixel = (int) img_info.channels * (img_info.bitd / 8);
    return img_info;
}

unsigned char* getImgFromChunks(Chunk* chunks)
{
    int count = 0;
    IHDR ihdr_data = getIHDRFromChunks(chunks);
    uLongf compressed_size = 0;
    uLongf uncompressed_size = (ihdr_data.width*ihdr_data.height*ihdr_data.bytesPerPixel)   ihdr_data.height   1;
    unsigned char* compressed_idat = (unsigned char*) malloc(sizeof(unsigned char)*4);
    unsigned char* uncompressed_idat = (unsigned char*) malloc(sizeof(unsigned char)*uncompressed_size);

    while(1){
        if(compType(chunks[count].type, IEND_CHUNK, 4)) break;
        else if (compType(chunks[count].type, IDAT_CHUNK, 4)){
            compressed_size  = sizeof(unsigned char)*chunks[count].length;
            compressed_idat = realloc(compressed_idat, compressed_size);

            memcpy(compressed_idat   compressed_size - chunks[count].length, chunks[count].data, chunks[count].length);
        }
        count  ;
    }

    int ret = uncompress(uncompressed_idat, amp;uncompressed_size, compressed_idat, compressed_size);
    free(compressed_idat);

    return ret == 0 ? uncompressed_idat : '';
}

int PaethPredictor(int a, int b, int c)
{
    int pa = abs(b - c);
    int pb = abs(a - c);
    int pc = abs(a   b - (2*c));

    if(pa <= pb amp;amp; pa <= pc) return a;
    else if(pb <= pc) return b;
    return c;

}

int* getPixelsFromImg(unsigned char* img, IHDR ihdr_info)
{
    int* pixels = (int*) malloc(sizeof(int)*ihdr_info.width*ihdr_info.height*ihdr_info.bytesPerPixel);
    int filter_type;

    int i = 0;
    int current_pixel = 0;
    int stride = ihdr_info.width * ihdr_info.bytesPerPixel;

    int filt_a;
    int filt_b;
    int filt_c;


    for(int r = 0; r < ihdr_info.height; r  ){
        filter_type = img[i];
        i  ;
        for(int c = 0; c < stride; c  ){

            filt_a = c >= ihdr_info.bytesPerPixel ? pixels[r * stride   c - ihdr_info.bytesPerPixel] : 0;
            filt_b = r > 0 ? pixels[(r - 1) * stride   c] : 0;
            filt_c = r > 0 amp;amp; c >= ihdr_info.bytesPerPixel ? pixels[(r - 1) * stride   c - ihdr_info.bytesPerPixel] : 0;

            switch(filter_type){
                case 0:
                    pixels[current_pixel] = img[i] amp; 0xff;
                    break;
                case 1:
                    pixels[current_pixel] = (img[i]   filt_a) amp; 0xff;
                    break;
                case 2:
                    pixels[current_pixel] = (img[i]   filt_b) amp; 0xff;
                    break;
                case 3:
                    pixels[current_pixel] = (img[i]   filt_a   filt_c) amp; 0xff;
                    break;
                case 4:
                    pixels[current_pixel] = (img[i]   PaethPredictor(filt_a, filt_b, filt_c)) amp; 0xff;
                    break;
            }
            current_pixel  ;
            i  ;
        }
    }

    return pixels;
}

  

Это комбинации функций, которые я использую для достижения этой цели. Сначала я использую отдельную функцию, которая, как я знаю, не является проблемой, и поэтому пропустил получение байтов файла png. Затем я вызываю getChunksFromBytes() , чтобы собрать фрагменты файла png, и я отделяю IHDR от этих фрагментов в отдельную структуру. Наконец, я вызываю getImgFromChunks() , чтобы распаковать данные IDAT и getPixelsFromImg() отфильтровать несжатые значения.

Комментарии:

1. Ваше вычисление байтов на пиксель дает ноль, когда разрядность меньше восьми. В этом случае не работает даже концепция байтов на пиксель.

2. @MarkAdler ты прав! Я только что заметил это. Как мне вычислить байты на пиксель?

3. Вы этого не делаете. Вы вычисляете байты на строку. Вам нужно округлить возможные неиспользуемые биты в строке, поскольку строка всегда представляет собой целое число байтов.

4. pixel_bits = bitd * channels;

5. row_size = 1 ((pixel_bits * width 7) >> 3);

Ответ №1:

Во-первых, байты на пиксель — это не вещь, поскольку пиксели могут быть битами, упакованными в байты. Таким образом, ваш расчет uncompressed_size in getImgFromChunks() неверен. Это должно быть:

 uLongf uncompressed_size = ihdr_data.height *
    (1   ((ihdr_data.bitd * ihdr_data.channels * (uLongf)ihdr_data.width   7) >> 3));
  

Учитывая, что биты на пиксель — это не вещь, вам нужно переосмыслить, как вы это делаете getPixelsFromImg() . Так что нет смысла копать дальше, пока вы этого не сделаете.

Нет ничего плохого в обработке нескольких фрагментов IDAT. Возможно, вы неправильно определили существенное отличие файла png, которое вызывает у вас проблемы.