| #!/usr/bin/env python |
| |
| import struct |
| |
| import png |
| |
| |
| def write_pnm(file, plain, rows, meta): |
| """ |
| Write a Netpbm PNM (or PAM) file. |
| *file* output file object; |
| *plain* (a bool) true if writing plain format (not possible for PAM); |
| *rows* an iterator for the rows; |
| *meta* the info dictionary. |
| """ |
| |
| meta = dict(meta) |
| meta["maxval"] = 2 ** meta["bitdepth"] - 1 |
| meta["width"], meta["height"] = meta["size"] |
| |
| # Number of planes determines both image formats: |
| # 1 : L to PGM |
| # 2 : LA to PAM |
| # 3 : RGB to PPM |
| # 4 : RGBA to PAM |
| planes = meta["planes"] |
| |
| # Assume inputs are from a PNG file. |
| assert planes in (1, 2, 3, 4) |
| if planes in (1, 3): |
| if 1 == planes: |
| # PGM |
| # Even if maxval is 1 we use PGM instead of PBM, |
| # to avoid converting data. |
| magic = "P5" |
| if plain: |
| magic = "P2" |
| else: |
| # PPM |
| magic = "P6" |
| if plain: |
| magic = "P3" |
| header = "{magic} {width:d} {height:d} {maxval:d}\n".format(magic=magic, **meta) |
| if planes in (2, 4): |
| # PAM |
| # See https://netpbm.sourceforge.net/doc/pam.html |
| if plain: |
| raise Exception("PAM (%d-plane) does not support plain format" % planes) |
| if 2 == planes: |
| tupltype = "GRAYSCALE_ALPHA" |
| else: |
| tupltype = "RGB_ALPHA" |
| header = ( |
| "P7\nWIDTH {width:d}\nHEIGHT {height:d}\n" |
| "DEPTH {planes:d}\nMAXVAL {maxval:d}\n" |
| "TUPLTYPE {tupltype}\nENDHDR\n".format(tupltype=tupltype, **meta) |
| ) |
| file.write(header.encode("ascii")) |
| |
| # Values per row |
| vpr = planes * meta["width"] |
| |
| if plain: |
| for row in rows: |
| row_b = b" ".join([b"%d" % v for v in row]) |
| file.write(row_b) |
| file.write(b"\n") |
| else: |
| # format for struct.pack |
| fmt = ">%d" % vpr |
| if meta["maxval"] > 0xFF: |
| fmt = fmt + "H" |
| else: |
| fmt = fmt + "B" |
| for row in rows: |
| file.write(struct.pack(fmt, *row)) |
| |
| file.flush() |
| |
| |
| def main(argv=None): |
| import argparse |
| |
| parser = argparse.ArgumentParser(description="Convert PNG to PAM") |
| parser.add_argument("--plain", action="store_true") |
| parser.add_argument( |
| "input", nargs="?", default="-", type=png.cli_open, metavar="PNG" |
| ) |
| |
| args = parser.parse_args() |
| |
| # Encode PNG to PNM (or PAM) |
| image = png.Reader(file=args.input) |
| _, _, rows, info = image.asDirect() |
| write_pnm(png.binary_stdout(), args.plain, rows, info) |
| |
| |
| if __name__ == "__main__": |
| import sys |
| |
| sys.exit(main()) |