У меня были проблемы с распаковкой файла png. Это код, который обрабатывает блок IDAT:
case PNG_CHUNK_IDAT: {
z_stream Stream = {};
int Error = inflateInit(&Stream);
if(Error == Z_OK) {
Stream.avail_in = ChunkLength;
Stream.next_in = At;
Stream.avail_out = PngImage.Width * PngImage.Height * PngImage.Depth;
Stream.next_out = PngImage.Pixels;
do {
Error = inflate(&Stream, Z_NO_FLUSH);
if(Error != Z_OK) {
break;
}
} while(Stream.avail_out != 0);
inflateEnd(&Stream);
}
At += ChunkLength;
break;
}
куда At
текущая позиция в буфере файла png, и PngImage
это просто структура, которая содержит ширину, высоту и глубину изображения и имеет массив без знака размера width*height*depth
,
Изображение, которое я пытаюсь распаковать, выглядит так: arial.png
Он не имеет фильтрации (0) и является полноцветным png с альфа-каналом. Он также содержит только один блок IDAT.
Но вместо этого я получаю что-то вроде этого: неверный png
Я знаю, что изображение переворачивается вверх ногами, это проблема моего рендерера снизу вверх и png сверху вниз; это не то, что меня сейчас волнует.
Я должен также отметить, что inflate
только на самом деле запускается один раз и возвращает Z_OK, поэтому я не думаю, что проблема в том, что не обрабатываются фильтры линии сканирования. Я попытался добавить один байт на строку, а затем не копировать первый байт каждой строки в Pixels
массив, но это не имело большого значения.
Есть идеи, что я могу делать не так?
Во-первых, возвращаясь Z_OK
на последнем звонке не ок. Если не вернется Z_STREAM_END
, то вы не распаковали и не проверили полные сжатые данные.
Во-вторых, ваш avail_out
не учитывает байт фильтра в начале каждой строки сканирования. Добавьте высоту к avail_out
и предоставить место для этого.
В-третьих, я понятия не имею, как вы получили изображение RGBA 643×514 из исходного изображения RGBA 512×512. Вам нужно пропустить байт фильтра в каждой строке, а затем каждый пиксель четыре байт.
Похоже, вы можете игнорировать байт метода фильтра в начале каждой строки развертки. Это там, даже если это 0.
Я думаю, ты должен сделать это while(Stream.avail_out == 0);
Пока, z.avail_out
равен нулю, это означает, что сжатые данные заполнены байтами, доступными для распакованных данных и inflate()
нужно позвонить снова.
Всегда думайте о его значении, мы должны остановиться, когда есть доступные (не заполненные) байты для вывода, чтобы avail_out > 0
,