#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, которое вызывает у вас проблемы.