Исходный код не обрабатывает сразу несколько IDAT
фрагментов;он делает правильные вещи ™ и объединяет их в один большой объект, а затем распаковывает его целиком.IDAT
чанки не сжимаются отдельно, но ваш код предполагает, что они это делают, и поэтому происходит сбой, когда их несколько.
Может быть несколько чанков IDAT;если это так, они должны появляться последовательно без каких-либо других промежуточных фрагментов.Затем сжатый поток данных является конкатенацией содержимого полей данных всех блоков IDAT.
11.2.4 Данные изображения IDAT
Повторное подключение цикла кСначала соберите все исправления IDAT
.Только при обнаружении чанка IEND
эти данные распаковываются, меняются байты и создается новый чанк IDAT
.Последний шаг, добавление IEND
, закрывает файл.
from struct import *
from zlib import *
import stat
import sys
import os
import zlib
def getNormalizedPNG(filename):
pngheader = b"\x89PNG\r\n\x1a\n"
file = open(filename, "rb")
oldPNG = file.read()
file.close()
if oldPNG[:8] != pngheader:
return None
newPNG = oldPNG[:8]
chunkPos = len(newPNG)
chunkD = bytearray()
foundCGBi = False
# For each chunk in the PNG file
while chunkPos < len(oldPNG):
# Reading chunk
chunkLength = oldPNG[chunkPos:chunkPos+4]
chunkLength = unpack(">L", chunkLength)[0]
chunkType = oldPNG[chunkPos+4 : chunkPos+8]
chunkData = oldPNG[chunkPos+8:chunkPos+8+chunkLength]
chunkCRC = oldPNG[chunkPos+chunkLength+8:chunkPos+chunkLength+12]
chunkCRC = unpack(">L", chunkCRC)[0]
chunkPos += chunkLength + 12
# Parsing the header chunk
if chunkType == b"IHDR":
width = unpack(">L", chunkData[0:4])[0]
height = unpack(">L", chunkData[4:8])[0]
# Parsing the image chunk
if chunkType == b"IDAT":
# Concatename all image data chunks
chunkD += chunkData
continue
# Stopping the PNG file parsing
if chunkType == b"IEND":
if not foundCGBi:
print ('Already normalized')
return None
bufSize = width * height * 4 + height
chunkData = decompress(chunkD, -8, bufSize)
# Swapping red & blue bytes for each pixel
chunkData = bytearray(chunkData)
offset = 1
for y in range(height):
for x in range(width):
chunkData[offset+4*x],chunkData[offset+4*x+2] = chunkData[offset+4*x+2],chunkData[offset+4*x]
offset += 1+4*width
# Compressing the image chunk
#chunkData = newdata
chunkData = compress( chunkData )
chunkLength = len( chunkData )
chunkCRC = crc32(b'IDAT')
chunkCRC = crc32(chunkData, chunkCRC)
chunkCRC = (chunkCRC + 0x100000000) % 0x100000000
newPNG += pack(">L", chunkLength)
newPNG += b'IDAT'
newPNG += chunkData
newPNG += pack(">L", chunkCRC)
chunkCRC = crc32(chunkType)
newPNG += pack(">L", 0)
newPNG += b'IEND'
newPNG += pack(">L", chunkCRC)
break
# Removing CgBI chunk
if chunkType == b"CgBI":
foundCGBi = True
else:
newPNG += pack(">L", chunkLength)
newPNG += chunkType
if chunkLength > 0:
newPNG += chunkData
newPNG += pack(">L", chunkCRC)
return newPNG
def updatePNG(filename):
data = getNormalizedPNG(filename)
if data != None:
file = open(filename+'_fixed.png', "wb")
file.write(data)
file.close()
return True
return data
updatePNG("broken_image.png")
, что приводит к правильному исправленному файлу.
Этот код не восстанавливает поврежденный файлCgBI
альфа-канал!Если вам нужна правильная альфа-прозрачность, вам нужно применить фильтры строк, чтобы получить прямые значения RGB, инвертировать альфа, а затем применить инверсию фильтров строк перед повторным сжатием.
Вы можете использовать Оболочка Python для PNGDefry , которая является программой на C, которая действительно выполняет эти пропущенные шаги.
Отказ от ответственности: я пишу PNGdefry.