tools/splashscreen_converter.py (141 lines of code) (raw):

#!/usr/bin/env python3 # tools/splashscreen_converter.py # # SPDX-License-Identifier: Apache-2.0 # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. The # ASF licenses this file to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance with the # License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """This script converts from any image type supported by Python imaging library to the RLE-encoded format used by the framebuffer splashscreen feature. """ from PIL import Image def get_palette(img, maxcolors=256): """Returns a list of colours. If there are too many colours in the image, the least used are removed. """ colors = img.getcolors(65536) colors.sort(key=lambda c: -c[0]) return [c[1] for c in colors[:maxcolors]] def write_palette(outfile, palette, type): """Write the palette to the output file.""" if type == "ARGB": outfile.write("#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32\n") outfile.write("static const fb_pixel_t palette[] =\n") outfile.write("{\n") for i in range(0, len(palette), 4): for r, g, b, a in palette[i : i + 4]: outfile.write(" MKRGB(%d, %d, %d, %d),\n" % (a, r, g, b)) outfile.write("};\n") outfile.write("#endif\n\n") elif type == "RGB": outfile.write("#if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24) || \\\n") outfile.write(" defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16) || \\\n") outfile.write(" defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP8)\n") outfile.write("static const fb_pixel_t palette[] =\n") outfile.write("{\n") for i in range(0, len(palette), 4): for r, g, b in palette[i : i + 4]: outfile.write(" MKRGB(%d, %d, %d),\n" % (r, g, b)) outfile.write("};\n") outfile.write("#endif\n\n") else: outfile.write("#if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_GREY) || \\\n") outfile.write(" defined(CONFIG_VIDEO_FB_SPLASHSCREEN_MONO)\n") outfile.write("static const fb_pixel_t palette[] =\n{\n};\n") outfile.write("#endif\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, type): """RLE-encode one row of image data.""" color = None entries = [] repeats = 0 for x in range(0, img.width): if type == "BPP32" or type == "BPP24" or type == "BPP16": c = quantize(img.getpixel((x, y)), palette) else: c = img.getpixel((x, y)) 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, suffix): """Write the image contents to the output file.""" if suffix == "BPP24" or suffix == "BPP16": outfile.write("#if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24) || \\") outfile.write("\n defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16)\n") else: outfile.write("#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN_%s\n" % suffix) outfile.write("static const struct splscr_bitmap_s bitmap[] =\n") outfile.write("{") for y in range(0, img.height): entries = encode_row(img, palette, y, suffix) for r, c in entries: outfile.write("\n") row = " {%d, %d}," % (r, c) outfile.write(row) outfile.write(("/* End of row %3d */" % (y + 1)).rjust(78 - len(row), " ")) outfile.write("\n};\n") outfile.write("#endif /* CONFIG_VIDEO_FB_SPLASHSCREEN_%s */\n\n" % suffix) def write_descriptor(outfile, name): outfile.write("const struct palette_bitmap_s g_%s =\n" % name) outfile.write("{\n") lw = len(str(img.width)) lh = len(str(img.height)) outfile.write(" %d," % img.width) outfile.write( ("/* width in pixels */\n").rjust( 76 - lw, " " ) ) outfile.write(" %d," % img.height) outfile.write( ("/* height in pixels */\n").rjust( 76 - lh, " " ) ) outfile.write( ( " palette, /* Colour palette */\n" ).rjust(76, " ") ) outfile.write( ( " bitmap, /* Pointer to the start of the RLE data */\n" ).rjust(76, " ") ) outfile.write("};\n") if __name__ == "__main__": import os.path import sys if len(sys.argv) < 3: print("Usage: splashscreen_converter.py image.xxx output_directory out.c") print( "\t- image.xxx\t\tis the image file (e.g. logo.png) in a format supported by PIL" ) print("\t- output_directory\tis where the output file will be saved.") print("\t- out.c\t\t\tis the name of the output file.") print("\t\t\t\t- If out.c is not specified it will default to fb_splash.c,") print("\t\t\t\t which is the name required for custom splashscreens") sys.exit(1) elif len(sys.argv) == 3: path = sys.argv[2] filename = "fb_splash.c" else: path = os.path.relpath(sys.argv[2]) filename = sys.argv[3] img = Image.open(sys.argv[1]).convert("RGBA") file = os.path.realpath(path + "/" + filename) outfile = open(file, "w") palette_argb = get_palette(img) palette_rgb = get_palette(img.convert("RGB")) outfile.write( """/**************************************************************************** * %(file)s * * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /* Script-generated framebuffer splashscreen bitmap file. * Generated from %(src)s * by splashscreen_converter.py */ /**************************************************************************** * Included Files ****************************************************************************/ #include <nuttx/video/fb.h> /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Private Data ****************************************************************************/ """ % {"file": path + "/" + filename, "src": os.path.relpath(sys.argv[1])} ) name = "splscr" write_palette(outfile, palette_argb, "ARGB") write_palette(outfile, palette_rgb, "RGB") write_palette(outfile, None, None) write_image(outfile, img, palette_argb, "BPP32") write_image(outfile, img.convert("RGB"), palette_rgb, "BPP24") write_image(outfile, img.convert("L"), None, "GREY") write_image(outfile, img.convert("1"), None, "MONO") write_descriptor(outfile, name) outfile.write( """ /**************************************************************************** * Private Functions ****************************************************************************/ """ ) print("Created %s from %s" % (file, sys.argv[1]))