Skip to content

Instantly share code, notes, and snippets.

@DavidBuchanan314
Created November 18, 2021 01:14
Show Gist options
  • Save DavidBuchanan314/f2a00896ccc0f8f6cdda017d787506f5 to your computer and use it in GitHub Desktop.
Save DavidBuchanan314/f2a00896ccc0f8f6cdda017d787506f5 to your computer and use it in GitHub Desktop.
import sys
import zlib
PNG_MAGIC = b"\x89PNG\r\n\x1a\n"
def parse_chunks(stream, out):
magic = stream.read(8)
assert(magic == PNG_MAGIC)
out.write(magic)
chunks = [] # (type, body)
while True:
chunk_len = int.from_bytes(stream.read(4), "big")
chunk_type = stream.read(4)
if not chunk_type:
raise Exception("Unexpected EOF")
chunk_body = stream.read(chunk_len)
chunk_crc = int.from_bytes(stream.read(4), "big")
assert(chunk_crc == zlib.crc32(chunk_type + chunk_body))
if chunk_type == b"IHDR":
width = int.from_bytes(chunk_body[0:4], "big")
height = int.from_bytes(chunk_body[4:8], "big")
if chunk_type == b"CgBI":
# drop CgBI chunks
pass
elif chunk_type == b"IDAT" and chunks[-1][0] == b"IDAT":
# consolidate consecutive IDATs
chunks[-1] = (b"IDAT", chunks[-1][1] + chunk_body)
else:
# passthru
chunks.append((chunk_type, chunk_body))
if chunk_type == b"IEND":
break
for chunk_type, chunk_body in chunks:
if chunk_type == b"IDAT":
body = zlib.decompress(chunk_body, -15)
fixed = []
for y in range(height):
fixed.append(body[(width*4+1)*y])
for x in range(width):
i = (width*4+1)*y+x*4
fixed += [body[i+3], body[i+2], body[i+1], body[i+0]]
chunk_body = zlib.compress(bytes(fixed))
out.write(len(chunk_body).to_bytes(4, "big"))
out.write(chunk_type)
out.write(chunk_body)
out.write(zlib.crc32(chunk_type+chunk_body).to_bytes(4, "big"))
data = open(sys.argv[1], "rb")
out = open(sys.argv[2], "wb")
parse_chunks(data, out)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment