nuttx/tools/splashscreen_converter.py
Tim Hardisty ed0c18c66c drivers/video/fb.c: Add startup splashscreen option
Adds Kconfig-selected splashscreen options used when the driver is first registered

* Includes a new Python script in ./tools to create RLE bitmap files
* Includes default NS logo btimaps in 320x320, 160x160 and 80x80 resolutions along with their PNG files

Signed-off-by: Tim Hardisty  timh@jti.uk.com>
2025-04-30 06:38:05 -03:00

257 lines
8.8 KiB
Python
Executable File

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