#!/usr/bin/env python """This script converts from any image type supported by Python imaging library to the RLE-encoded format used by NxWidgets. """ from PIL import Image def get_palette(img, maxcolors=255): """Returns a list of colors. If there are too many colors in the image, the least used are removed. """ img = img.convert("RGB") colors = img.getcolors(65536) colors.sort(key=lambda c: -c[0]) return [c[1] for c in colors[:maxcolors]] def write_palette(outfile, palette): """Write the palette (normal and highlight) to the output file.""" outfile.write( "static const NXWidgets::nxwidget_pixel_t palette[BITMAP_PALETTESIZE] =\n" ) outfile.write("{\n") for i in range(0, len(palette), 4): outfile.write(" ") for r, g, b in palette[i : i + 4]: outfile.write("MKRGB(%3d,%3d,%3d), " % (r, g, b)) outfile.write("\n") outfile.write("};\n\n") outfile.write( "static const NXWidgets::nxwidget_pixel_t hilight_palette[BITMAP_PALETTESIZE] =\n" ) outfile.write("{\n") for i in range(0, len(palette), 4): outfile.write(" ") for r, g, b in palette[i : i + 4]: r = min(255, r + 50) g = min(255, g + 50) b = min(255, b + 50) outfile.write("MKRGB(%3d,%3d,%3d), " % (r, g, b)) outfile.write("\n") outfile.write("};\n\n") def quantize(color, palette): """Return the color index to closest match in the palette.""" try: return palette.index(color) except ValueError: # No exact match, search for the closest def distance(color2): return sum([(a - b) ** 2 for a, b in zip(color, color2)]) return palette.index(min(palette, key=distance)) def encode_row(img, palette, y): """RLE-encode one row of image data.""" entries = [] color = None repeats = 0 for x in range(0, img.size[0]): c = quantize(img.getpixel((x, y)), palette) if c == color and repeats < 255: repeats += 1 else: if color is not None: entries.append((repeats, color)) repeats = 1 color = c if color is not None: entries.append((repeats, color)) return entries def write_image(outfile, img, palette): """Write the image contents to the output file.""" outfile.write("static const NXWidgets::SRlePaletteBitmapEntry bitmap[] =\n") outfile.write("{\n") for y in range(0, img.size[1]): entries = encode_row(img, palette, y) row = "" for r, c in entries: if len(row) > 60: outfile.write(" " + row + "\n") row = "" row += "{%3d, %3d}, " % (r, c) row += " " * (73 - len(row)) outfile.write(" " + row + "/* Row %d */\n" % y) outfile.write("};\n\n") def write_descriptor(outfile, name): """Write the public descriptor structure for the image.""" outfile.write("extern const struct NXWidgets::SRlePaletteBitmap g_%s =\n" % name) outfile.write("{\n") outfile.write(" CONFIG_NXWIDGETS_BPP,\n") outfile.write(" CONFIG_NXWIDGETS_FMT,\n") outfile.write(" BITMAP_PALETTESIZE,\n") outfile.write(" BITMAP_WIDTH,\n") outfile.write(" BITMAP_HEIGHT,\n") outfile.write(" {palette, hilight_palette},\n") outfile.write(" bitmap\n") outfile.write("};\n") if __name__ == "__main__": import os.path import sys if len(sys.argv) != 3: print("Usage: bitmap_converter.py source.png output.cxx") sys.exit(1) img = Image.open(sys.argv[1]).convert("RGB") outfile = open(sys.argv[2], "w") palette = get_palette(img) outfile.write( """ /* Automatically NuttX bitmap file. */ /* Generated from %(src)s by bitmap_converter.py. */ #include #include #define BITMAP_WIDTH %(width)s #define BITMAP_HEIGHT %(height)s #define BITMAP_PALETTESIZE %(palettesize)s """ % { "src": sys.argv[1], "width": img.size[0], "height": img.size[1], "palettesize": len(palette), } ) name = os.path.splitext(os.path.basename(sys.argv[1]))[0] write_palette(outfile, palette) write_image(outfile, img, palette) write_descriptor(outfile, name)