#!/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()
