85 lines
2.7 KiB
Python
85 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Convert an XWD dump (from xwd) into a PNG, without external image libraries.
|
|
|
|
Usage: xwd -root -silent | python3 xwd2png.py out.png
|
|
"""
|
|
import struct
|
|
import sys
|
|
import zlib
|
|
|
|
|
|
def read_header(data):
|
|
fields = struct.unpack(">25I", data[:100])
|
|
names = [
|
|
"header_size", "file_version", "pixmap_format", "pixmap_depth",
|
|
"pixmap_width", "pixmap_height", "xoffset", "byte_order",
|
|
"bitmap_unit", "bitmap_bit_order", "bitmap_pad", "bits_per_pixel",
|
|
"bytes_per_line", "visual_class", "red_mask", "green_mask",
|
|
"blue_mask", "bits_per_rgb", "colormap_entries", "ncolors",
|
|
"window_width", "window_height", "window_x", "window_y",
|
|
"window_bdrwidth",
|
|
]
|
|
return dict(zip(names, fields))
|
|
|
|
|
|
def write_png(path, width, height, rgb_rows):
|
|
raw = b"".join(b"\x00" + row for row in rgb_rows)
|
|
|
|
def chunk(tag, payload):
|
|
return (struct.pack(">I", len(payload)) + tag + payload +
|
|
struct.pack(">I", zlib.crc32(tag + payload) & 0xFFFFFFFF))
|
|
|
|
header = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)
|
|
png = (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", header) +
|
|
chunk(b"IDAT", zlib.compress(raw, 6)) + chunk(b"IEND", b""))
|
|
with open(path, "wb") as handle:
|
|
handle.write(png)
|
|
|
|
|
|
def main():
|
|
out_path = sys.argv[1] if len(sys.argv) > 1 else "out.png"
|
|
data = sys.stdin.buffer.read()
|
|
head = read_header(data)
|
|
width = head["pixmap_width"]
|
|
height = head["pixmap_height"]
|
|
bpp = head["bits_per_pixel"]
|
|
stride = head["bytes_per_line"]
|
|
offset = head["header_size"] + head["ncolors"] * 12
|
|
pixels = data[offset:]
|
|
|
|
if bpp == 32:
|
|
rows = []
|
|
for y in range(height):
|
|
line = pixels[y * stride:y * stride + width * 4]
|
|
out = bytearray(width * 3)
|
|
for x in range(width):
|
|
src = x * 4
|
|
dst = x * 3
|
|
out[dst] = line[src + 2]
|
|
out[dst + 1] = line[src + 1]
|
|
out[dst + 2] = line[src]
|
|
rows.append(bytes(out))
|
|
elif bpp == 24:
|
|
rows = []
|
|
for y in range(height):
|
|
line = pixels[y * stride:y * stride + width * 3]
|
|
out = bytearray(width * 3)
|
|
for x in range(width):
|
|
src = x * 3
|
|
dst = x * 3
|
|
out[dst] = line[src + 2]
|
|
out[dst + 1] = line[src + 1]
|
|
out[dst + 2] = line[src]
|
|
rows.append(bytes(out))
|
|
else:
|
|
sys.stderr.write("unsupported bits_per_pixel=%s\n" % bpp)
|
|
return 2
|
|
|
|
write_png(out_path, width, height, rows)
|
|
print("%s %dx%d" % (out_path, width, height))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|