From 3575e2da37974918bb82addd47bc92720f7bcb91 Mon Sep 17 00:00:00 2001 From: Nicolas Roggeman Date: Fri, 31 May 2024 17:22:02 +0200 Subject: [PATCH] Improve icon2glyph syntax and output to avoid some errors --- Makefile.app_params | 14 ++- Makefile.glyphs | 7 +- lib_nbgl/tools/icon2glyph.py | 172 ++++++++++++++++++----------------- 3 files changed, 104 insertions(+), 89 deletions(-) diff --git a/Makefile.app_params b/Makefile.app_params index 481e914b3..932c93fb6 100644 --- a/Makefile.app_params +++ b/Makefile.app_params @@ -18,11 +18,12 @@ # Command to print ICONNAME hexadecimal bitmap on stdout according to the # hardware target. ifneq ($(TARGET),nanos) +ICON_HEX_FILE=icon.hex #inverse B&W for non Stax ifeq ($(TARGET_NAME),$(filter $(TARGET_NAME),TARGET_NANOX TARGET_NANOS2)) -ICONHEX_CMD=python3 $(BOLOS_SDK)/lib_nbgl/tools/icon2glyph.py --reverse --hexbitmaponly $(ICONNAME) +ICONHEX_CMD=python3 $(BOLOS_SDK)/lib_nbgl/tools/icon2glyph.py --reverse --hexbitmap $(ICON_HEX_FILE) $(ICONNAME) else -ICONHEX_CMD=python3 $(BOLOS_SDK)/lib_nbgl/tools/icon2glyph.py --hexbitmaponly $(ICONNAME) +ICONHEX_CMD=python3 $(BOLOS_SDK)/lib_nbgl/tools/icon2glyph.py --hexbitmap $(ICON_HEX_FILE) $(ICONNAME) endif else ICONHEX_CMD=python3 $(BOLOS_SDK)/icon3.py --hexbitmaponly $(ICONNAME) @@ -76,9 +77,16 @@ endif # Consider only one path_slip21 can be added, whereas LedgerBlue seems to # support multiple, but has the path can hold a " " in it, it mess with the # foreach, so we choose to restrict to only one path_slip21. + APP_INSTALL_PARAMS = --appName $(APPNAME) APP_INSTALL_PARAMS += --appVersion $(APPVERSION) -APP_INSTALL_PARAMS += `ICONHEX=\`$(ICONHEX_CMD) 2>/dev/null\` ; [ ! -z "$$ICONHEX" ] && echo "--icon $$ICONHEX"` +ifneq ($(TARGET),nanos) +# icon2glyph outputs its result in a file that needs to be deleted after usage +ICONHEX := $(shell $(ICONHEX_CMD) && cat $(ICON_HEX_FILE) && rm -f $(ICON_HEX_FILE)) +else +ICONHEX := $(shell $(ICONHEX_CMD)) +endif +APP_INSTALL_PARAMS += --icon $(ICONHEX) APP_INSTALL_PARAMS += $(foreach curve, $(CURVE_APP_LOAD_PARAMS), --curve $(curve)) APP_INSTALL_PARAMS += $(foreach path, $(PATH_APP_LOAD_PARAMS), --path $(path)) ifneq ($(PATH_SLIP21_APP_LOAD_PARAMS),) diff --git a/Makefile.glyphs b/Makefile.glyphs index e10784524..a1309d4ae 100644 --- a/Makefile.glyphs +++ b/Makefile.glyphs @@ -59,7 +59,6 @@ ifneq (,$(wildcard $(ICONNAME))) GLYPH_FILES += $(ICONNAME) endif ICON_SCRIPT = $(BOLOS_SDK)/lib_nbgl/tools/icon2glyph.py -GENERATE_GLYPHS_CMD = python3 $(ICON_SCRIPT) $(GLYPH_OPT) --glyphcfile $(GLYPH_FILES) else # USE_NBGL @@ -70,15 +69,21 @@ ICON_SCRIPT = $(BOLOS_SDK)/icon3.py GENERATE_GLYPHS_CMD = python3 $(ICON_SCRIPT) --factorize --glyphcfile $(GLYPH_FILES) endif# USE_NBGL +.DELETE_ON_ERROR: + $(GLYPH_DESTH): $(GLYPH_FILES) $(ICON_SCRIPT) $(L)echo "[GLYPH] Compiling..." $(L)-mkdir -p $(GLYPH_SRC_DIR) +ifneq ($(USE_NBGL),0) + $(L)python3 $(ICON_SCRIPT) $(GLYPH_OPT) --glyphcheader $(GLYPH_DESTH) --glyphcfile $(GLYPH_DESTC) $(GLYPH_FILES) +else $(L)rm -f $(GLYPH_DESTC) $(GLYPH_DESTH) @# Use temporary files to prevent issues when build is interrupted $(L)python3 $(ICON_SCRIPT) $(GLYPH_OPT) --glyphcheader $(GLYPH_FILES) > $(GLYPH_DESTH)_tmp $(L)$(GENERATE_GLYPHS_CMD) > $(GLYPH_DESTC)_tmp $(L)mv $(GLYPH_DESTC)_tmp $(GLYPH_DESTC) $(L)mv $(GLYPH_DESTH)_tmp $(GLYPH_DESTH) +endif #add dependency for generation $(GLYPH_DESTC): $(GLYPH_DESTH) diff --git a/lib_nbgl/tools/icon2glyph.py b/lib_nbgl/tools/icon2glyph.py index 0136ea070..09ec9d290 100755 --- a/lib_nbgl/tools/icon2glyph.py +++ b/lib_nbgl/tools/icon2glyph.py @@ -252,99 +252,83 @@ def compute_regular_icon_data(no_comp: bool, im: Image, bpp, reverse) -> Tuple[b return is_file, image_data # glyphs.c/.h chunk files generators +def create_source_files(source_filename: str, header_filename: str): + source_file = open(source_filename, 'w') + source_file.write('/* Automatically generated by icon2glyph.py */\n\n') + source_file.write('#include "{0}"\n\n'.format(os.path.basename(header_filename))) + header_file = open(header_filename, 'w') + header_file.write('/* Automatically generated by icon2glyph.py */\n\n') + header_file.write("#pragma once\n\n") + header_file.write('#include "app_config.h"\n') + header_file.write('#include "nbgl_types.h"\n') + header_file.write('\n') -def print_glyphfile_top(add_include: bool, apps: bool): - print("#include \"app_config.h\"") - if add_include: - if apps: - print("#include \"apps_glyphs.h\"") - else: - print("#include \"glyphs.h\"") - print( - """\ -#ifdef HAVE_NBGL -#include \"nbgl_types.h\" -#else -#include -#endif -""") - - -def print_glyphcfile_data(image_name, bpp, image_data): - print("uint8_t const C_{0}_bitmap[] = {{".format(image_name)) - for i in range(0, len(image_data), 16): - print(" " + ", ".join("0x{0:02x}".format(c) - for c in image_data[i:i+16]) + ",") - print("};") - print("""#ifdef HAVE_NBGL -const nbgl_icon_details_t C_{0} = {{ GLYPH_{0}_WIDTH, GLYPH_{0}_HEIGHT, NBGL_BPP_{1}, GLYPH_{0}_ISFILE, C_{0}_bitmap }}; -#endif // HAVE_NBGL -""".format(image_name, bpp)) + return source_file, header_file - -def print_glyphcheader_data(image_name, bpp, width, height, is_file, image_data): +def write_image_source_data(source_file, image_name, bpp, width, height, is_file, image_data): if is_file: is_file = 'true' else: is_file = 'false' + source_file.write('uint8_t const C_{0}_bitmap[] = {{\n'.format(image_name)) + for i in range(0, len(image_data), 16): + source_file.write(" " + ", ".join("0x{0:02x}".format(c) + for c in image_data[i:i+16]) + ",\n") + source_file.write('};\n') + source_file.write(f'const nbgl_icon_details_t C_{image_name} = {{ {width}, {height}, NBGL_BPP_{bpp}, {is_file}, C_{image_name}_bitmap }};\n\n') - print("""#ifndef GLYPH_{0}_BPP - #define GLYPH_{0}_WIDTH {1} - #define GLYPH_{0}_HEIGHT {2} - #define GLYPH_{0}_ISFILE {3} - #define GLYPH_{0}_BPP {4}""".format(image_name, width, height, is_file, bpp)) - print(" extern uint8_t const C_{0}_bitmap[{1:d}];".format( + +def write_image_header_data(header_file, image_name, image_data): + header_file.write("extern uint8_t const C_{0}_bitmap[{1:d}];\n".format( image_name, len(image_data))) - print(""" #ifdef HAVE_NBGL - extern const nbgl_icon_details_t C_{0}; - #endif // HAVE_NBGL -#endif // GLYPH_{0}_BPP -""".format(image_name)) + header_file.write("extern const nbgl_icon_details_t C_{0};\n\n".format(image_name)) def main(): parser = argparse.ArgumentParser( - description='Generate source code for NBGL icons.') + description='Generate source code for icons, in NBGL format.') parser.add_argument('image_file', help=""" Icons to process. Images that will be transformed through rotation or symmetry must be suffixed by '_nocomp' (example: image_nocomp.png) """, nargs='+') - parser.add_argument('--hexbitmaponly', action='store_true') - parser.add_argument('--glyphcheader', action='store_true') - parser.add_argument('--glyphcfile', action='store_true') + parser.add_argument('--hexbitmap', help="If set, the script generates in given file the hex bitmap (for app icon)") + parser.add_argument('--glyphcheader', help="Name of the generated source (glyphs.c)") + parser.add_argument('--glyphcfile', help="Name of the generated header (glyphs.h)") + parser.add_argument('--init_id', help="Initial ID of the glyph list", type=int, default=0) parser.add_argument('--reverse', help="Reverse B&W for 1BPP icons", action='store_true') - parser.add_argument('--apps', help="to set to generate apps glyphs", action='store_true') args = parser.parse_args() - # Print C header - if args.glyphcfile or args.glyphcheader: - print_glyphfile_top(args.glyphcfile, args.apps) - - processed_image_names = [] - for file in args.image_file: - try: - # Get image name - image_name = os.path.splitext(os.path.basename(file))[0] - - # if image name has already been done, then don't do it twice - if image_name in processed_image_names: - continue - processed_image_names.append(image_name) - - # Open image in luminance format - opened = open_image(file) - if opened is not None: - im, bpp = opened - else: - continue - - if args.hexbitmaponly: - # Prepare and print app icon data - _, image_data = compute_app_icon_data(im, bpp, args.reverse) - print(binascii.hexlify(image_data).decode('utf-8')) - else: + # ensure that glyphcheader and glyphcfile are both set or unset + if (args.glyphcfile is not None and args.glyphcheader is None) or (args.glyphcfile is None and args.glyphcheader is not None): + print('glyphcheader and glyphcfile must be provided at the same time') + exit(-1) + + + if args.glyphcfile is not None: + # create source and header + source, header = create_source_files(args.glyphcfile, args.glyphcheader) + + processed_image_names = [] + id = args.init_id + for file in args.image_file: + try: + # Get image name + image_name = os.path.splitext(os.path.basename(file))[0] + + # if image name has already been done, then don't do it twice + if image_name in processed_image_names: + continue + processed_image_names.append(image_name) + + # Open image in luminance format + opened = open_image(file) + if opened is not None: + im, bpp = opened + else: + continue + # Prepare and print regular icon data width, height = im.size @@ -357,21 +341,39 @@ def main(): is_file, image_data = compute_regular_icon_data(no_comp, im, bpp, args.reverse) - if args.glyphcfile: - print_glyphcfile_data(image_name, bpp, image_data) + write_image_source_data(source, image_name, bpp, width, height, is_file, image_data) - if args.glyphcheader: - print_glyphcheader_data( - image_name, bpp, width, height, is_file, image_data) - - except Exception as e: - sys.stderr.write( - "Exception while processing {}: {}\n".format(file, e)) - try: - traceback.print_tb() - except: - pass + write_image_header_data(header, image_name, image_data) + id += 1 + except Exception as e: + sys.stderr.write( + "Exception while processing {}: {}\n".format(file, e)) + try: + traceback.print_tb() + except: + pass + else: + if args.hexbitmap is None: + print('something must be set') + exit(-1) + if len(args.image_file) != 1: + print('only one icon can be processed in hexbitmap mode') + exit(-1) + file = args.image_file[0] + # Get image name + image_name = os.path.splitext(os.path.basename(file))[0] + + # Open image in luminance format + opened = open_image(file) + if opened is not None: + im, bpp = opened + + # Prepare and print app icon data + _, image_data = compute_app_icon_data(im, bpp, args.reverse) + with open(args.hexbitmap, 'w') as out_file: + out_file.write(binascii.hexlify(image_data).decode('utf-8')) + #print(binascii.hexlify(image_data).decode('utf-8')) if __name__ == "__main__": main()