| #!/usr/bin/env python |
| |
| """ |
| Convert PNG (stdin) to the raw 565 format used on the Kobo devices. |
| |
| On the 2013-era Kobo devices, the screen is 800x600 so this size |
| input PNG works best. Note: image top-left is screen top-right. |
| """ |
| |
| # The raw format stores the pixels sequentially in top-left |
| # reading order; each pixel is a 16-bit word packed into bytes |
| # little-endian; the word stores (from high to low) 5 bits of |
| # Red, 6 bits of Green, 5 bits of Blue: |
| # 15 11 10 5 4 0 |
| # +-------+---------+-------+ |
| # | Red | Green | Blue | |
| # +-------+---------+-------+ |
| # 15 8 7 0 |
| # \ / \ / |
| # byte N+1 byte N |
| |
| import png |
| |
| class Kobo565: |
| |
| @staticmethod |
| def from_array(array): |
| return Kobo565(array) |
| |
| def __init__(self, array): |
| self.array = array |
| |
| def save(self, file): |
| import struct |
| |
| close = lambda: None |
| if not hasattr(file, 'write'): |
| file = open(file, 'wb') |
| close = file.close |
| |
| try: |
| for row in self.array: |
| for r, g, b in row: |
| w = (r << 11) + (g << 5) + (b) |
| file.write(struct.pack('<H', w)) |
| finally: |
| close() |
| |
| |
| def quads(i): |
| """ |
| [a, b, c, d, e, f, g, h, ...] -> [(a, b, c, d), (e, f, g, h)] |
| """ |
| return zip(*[iter(i)]*4) |
| |
| def conv565(r, g, b, a): |
| """ |
| Convert to a triple in the range: (0 to 31, 0 to 63, 0 to 31). |
| """ |
| return (r >> 3, g >> 2, b >> 3) |
| |
| def main(): |
| import sys |
| outname = None |
| arg = sys.argv[1:] |
| while arg: |
| if arg[0] == '-o': |
| outname = arg[1] |
| arg = arg[2:] |
| elif arg[0] == '--': |
| arg = arg[1:] |
| break |
| elif arg[0].startswith('-'): |
| raise Exception("Unknown option %r" % arg[0]) |
| else: |
| break |
| |
| w, h, pixels, info = png.Reader(file=sys.stdin).asRGBA() |
| |
| a565 = [[conv565(*t) for t in quads(row)] for row in pixels] |
| image = Kobo565.from_array(a565) |
| if outname is None: |
| image.save(sys.stdout) |
| else: |
| image.save(outname) |
| |
| if __name__ == '__main__': |
| main() |