vxSDK - 0.12.0-42 : remove assets converter and project module
*update* > [vxsdk] | [conv] remove converter module | [project] remove project module *fix* > [install] | allow force install (useful when python major version update break pip env)
This commit is contained in:
parent
1a14f4ca4e
commit
7766bd6bd7
|
@ -1,13 +0,0 @@
|
|||
# Build files
|
||||
/build-fx
|
||||
/build-cg
|
||||
/*.g1a
|
||||
/*.g3a
|
||||
|
||||
# Python bytecode
|
||||
__pycache__/
|
||||
|
||||
# Common IDE files
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.vscode
|
|
@ -1,92 +0,0 @@
|
|||
#! /usr/bin/make -f
|
||||
|
||||
|
||||
#
|
||||
# variable definition
|
||||
#
|
||||
|
||||
# color definition, for swagg :D
|
||||
red := \033[1;31m
|
||||
green := \033[1;32m
|
||||
blue := \033[1;34m
|
||||
white := \033[1;37m
|
||||
nocolor := \033[1;0m
|
||||
|
||||
src := $(foreach path,\
|
||||
$(shell find src -not -path "*/\.*" -type d), \
|
||||
$(wildcard $(path)/*.c) \
|
||||
$(wildcard $(path)/*.S) \
|
||||
$(wildcard $(path)/*.s))
|
||||
|
||||
obj := $(patsubst src_%,$(VXSDK_PREFIX_BUILD)/%.o,$(subst /,_,$(src)))
|
||||
obj += $(patsubst \
|
||||
$(VXSDK_ASSETS_SRC)/%,\
|
||||
$(VXSDK_ASSETS_BUILD)/%.o,\
|
||||
$(wildcard $(VXSDK_ASSETS_SRC)/*.c) \
|
||||
)
|
||||
|
||||
cflags := -ffreestanding -nostdlib -m4-nofpu -fPIE -O1
|
||||
cflags += -mb -fstrict-volatile-bitfields
|
||||
cflags += $(VXSDK_CFLAGS_INCLUDE) -I.. -Iinclude
|
||||
|
||||
# debug vars
|
||||
VERBOSE ?= false
|
||||
|
||||
#
|
||||
# build rules
|
||||
#
|
||||
|
||||
vxaddin: $(obj)
|
||||
@ printf "$(blue)Create $(red)$@$(nocolor)\n"
|
||||
sh-elf-vhex-gcc \
|
||||
-T $(VXSDK_PREFIX_LIB)/fxcg50-dynamic.ld -Wl,-q -Wl,-M \
|
||||
$(VXSDK_CFLAGS_LINK) \
|
||||
-o $@ $^ \
|
||||
-lvhex-fxcg50 -lc -lgcc \
|
||||
> $(VXSDK_PREFIX_BUILD)/map.txt
|
||||
vxsdk conv addin -b $@ -n vxaddin -o /tmp/vxaddin
|
||||
|
||||
version:
|
||||
@echo "$(VXSDK_PKG_VERSION)"
|
||||
|
||||
help:
|
||||
@ echo 'Rules listing:'
|
||||
@ echo '... all the default, if no target is provided'
|
||||
@ echo '... clean remove build object'
|
||||
@ echo '... fclean remove all generated object'
|
||||
@ echo '... re same as `make fclean all`'
|
||||
@ echo '... version display version'
|
||||
@ echo '... install install the library'
|
||||
@ echo '... uninstall uninstall the library'
|
||||
|
||||
.PHONY: help version
|
||||
|
||||
#
|
||||
# Object rules
|
||||
#
|
||||
|
||||
$(VXSDK_PREFIX_BUILD)%.o:
|
||||
ifeq ($(VERBOSE),true)
|
||||
@ mkdir -p $(dir $@)
|
||||
sh-elf-vhex-gcc \
|
||||
$(cflags) -D FXCG50 \
|
||||
-o $@ \
|
||||
-c $(addprefix src/,$(subst _,/,$(notdir $(basename $@))))
|
||||
else
|
||||
@ mkdir -p $(dir $@)
|
||||
@ printf "$(green)>$(nocolor) $(white)$@$(nocolor)\n"
|
||||
@ sh-elf-vhex-gcc \
|
||||
$(cflags) -D FXCG50 \
|
||||
-o $@ \
|
||||
-c $(addprefix src/,$(subst _,/,$(notdir $(basename $@))))
|
||||
endif
|
||||
|
||||
$(VXSDK_ASSETS_BUILD)%.o: $(VXSDK_ASSETS_SRC)/%
|
||||
ifeq ($(VERBOSE),true)
|
||||
@ mkdir -p $(dir $@)
|
||||
sh-elf-vhex-gcc $(cflags) -D FXCG50 -o $@ -c $<
|
||||
else
|
||||
@ mkdir -p $(dir $@)
|
||||
@ printf "$(green)>$(nocolor) $(white)$@$(nocolor)\n"
|
||||
@ sh-elf-vhex-gcc $(cflags) -D FXCG50 -o $@ -c $<
|
||||
endif
|
|
@ -1,14 +0,0 @@
|
|||
#include <vhex/display.h>
|
||||
#include <vhex/keyboard.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
dclear(C_WHITE);
|
||||
dtext(1, 1, C_BLACK, "Sample fxSDK add-in.");
|
||||
dupdate();
|
||||
|
||||
while (1) { __asm__("sleep"); }
|
||||
|
||||
//getkey();
|
||||
return 1;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
[project]
|
||||
name = 'vxaddin'
|
||||
|
||||
[dependencies]
|
||||
vxKernel = 'dev'
|
||||
|
||||
[build]
|
||||
build = 'make'
|
39
install.sh
39
install.sh
|
@ -20,6 +20,7 @@ Actions:
|
|||
uninstall Uninstall the VxSDK
|
||||
|
||||
Options:
|
||||
--force Force reinstallation
|
||||
--prefix=<PREFIX> Installation prefix (default is ~/.local)
|
||||
-h, --help Display this help
|
||||
OEF
|
||||
|
@ -33,6 +34,7 @@ OEF
|
|||
#
|
||||
|
||||
target='install'
|
||||
force_reinstall='false'
|
||||
|
||||
for arg; do case "$arg" in
|
||||
--help | -h)
|
||||
|
@ -40,6 +42,9 @@ for arg; do case "$arg" in
|
|||
-v | --version)
|
||||
echo "$VERSION"
|
||||
exit 0;;
|
||||
--force)
|
||||
force_reinstall='true'
|
||||
;;
|
||||
|
||||
install)
|
||||
target='install';;
|
||||
|
@ -65,7 +70,7 @@ then
|
|||
if [[ -d "$prefix/lib/vxsdk/vxsdk" ]]
|
||||
then
|
||||
echo 'warning : vxsdk is already installed !' >&2
|
||||
read -n 1 -p 'Do you whant to re-install the vxSDK [y/N] ? ' reinstall
|
||||
read -n 1 -p 'Do you whant to re-install the vxSDK [y/N] ? ' -r reinstall
|
||||
[[ "$reinstall" != 'y' ]] && exit 1
|
||||
echo ''
|
||||
"$projdir/install.sh" uninstall-cached
|
||||
|
@ -79,11 +84,11 @@ then
|
|||
"$prefix/lib/vxsdk/"
|
||||
|
||||
install -d "$prefix/bin"
|
||||
echo '#! /usr/bin/env bash' > $prefix/bin/vxsdk
|
||||
echo '' >> $prefix/bin/vxsdk
|
||||
echo "source $prefix/lib/vxsdk/venv/bin/activate" >> $prefix/bin/vxsdk
|
||||
echo "python3 $prefix/lib/vxsdk/vxsdk \$@" >> $prefix/bin/vxsdk
|
||||
echo 'deactivate' >> $prefix/bin/vxsdk
|
||||
echo '#! /usr/bin/env bash' > "$prefix/bin/vxsdk"
|
||||
echo '' >> "$prefix/bin/vxsdk"
|
||||
echo "source $prefix/lib/vxsdk/venv/bin/activate" >> "$prefix/bin/vxsdk"
|
||||
echo "python3 $prefix/lib/vxsdk/vxsdk \$@" >> "$prefix/bin/vxsdk"
|
||||
echo 'deactivate' >> "$prefix/bin/vxsdk"
|
||||
chmod +x "$prefix/bin/vxsdk"
|
||||
|
||||
build_date=$(date '+%Y-%m-%d')
|
||||
|
@ -94,17 +99,17 @@ then
|
|||
-e "s*%BUILD_HASH%*$build_hash*" \
|
||||
-e "s*%BUILD_DATE%*$build_date*" \
|
||||
"$projdir/vxsdk/__main__.py" \
|
||||
> $f
|
||||
> "$f"
|
||||
|
||||
mkdir -p $prefix/share/vxsdk
|
||||
mkdir -p "$prefix/share/vxsdk"
|
||||
|
||||
cd $prefix/lib/vxsdk
|
||||
if [[ ! -d venv ]];
|
||||
cd "$prefix/lib/vxsdk" || exit 1
|
||||
if [[ ! -d venv ]] || [[ "$force_reinstall" == 'true' ]]
|
||||
then
|
||||
set -x; python3 -m venv venv; set +x
|
||||
source venv/bin/activate
|
||||
set -x
|
||||
pip install --upgrade pip 2>&1 > /dev/null
|
||||
{ pip install --upgrade pip > /dev/null ; } 2>&1
|
||||
pip install -r requirements.txt
|
||||
set +x
|
||||
deactivate
|
||||
|
@ -121,9 +126,9 @@ fi
|
|||
if [[ "$target" = 'uninstall-cached' ]]
|
||||
then
|
||||
set -x
|
||||
rm $prefix/bin/vxsdk
|
||||
rm -rf $prefix/lib/vxsdk/vxsdk
|
||||
rmdir $prefix/share/vxsdk 2>/dev/null || exit 0
|
||||
rm "$prefix/bin/vxsdk"
|
||||
rm -rf "$prefix/lib/vxsdk/vxsdk"
|
||||
rmdir "$prefix/share/vxsdk" 2>/dev/null || exit 0
|
||||
echo 'vxSDK has been partially removed'
|
||||
exit 0
|
||||
fi
|
||||
|
@ -134,9 +139,9 @@ then
|
|||
|
||||
# TODO : rm -rf dependencies too
|
||||
# TODO : add confirmation input
|
||||
rm $prefix/bin/vxsdk
|
||||
rm -rf $prefix/lib/vxsdk
|
||||
rmdir $prefix/share/vxsdk 2>/dev/null || exit 0
|
||||
rm "$prefix/bin/vxsdk"
|
||||
rm -rf "$prefix/lib/vxsdk"
|
||||
rmdir "$prefix/share/vxsdk" 2>/dev/null || exit 0
|
||||
echo 'vxSDK has been removed'
|
||||
exit 0
|
||||
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
"""
|
||||
cli.conv - assset conversion abstraction
|
||||
|
||||
This package provides conversion abstraction (image -> source code, ELF ->
|
||||
addin, ...) for the Vhex project.
|
||||
"""
|
||||
|
||||
from core.logger import log
|
||||
|
||||
from cli.conv.doctor import doctor_conv_cli
|
||||
from cli.conv.assets import assets_conv_cli
|
||||
from cli.conv.addin import addin_conv_cli
|
||||
|
||||
__all__ = [
|
||||
'__VXSDK_MODULE_META__',
|
||||
'cli_validate',
|
||||
'cli_parse',
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
__VXSDK_MODULE_META__ = (
|
||||
['conv'],
|
||||
'assets converter',
|
||||
"""vxsdk-conv
|
||||
Project assets converter
|
||||
|
||||
USAGE:
|
||||
vxsdk conv(-<ACTION>) [OPTIONS] ...
|
||||
|
||||
DESCRIPTION:
|
||||
Convert vhex project assets (or binary) into various form. By default, if
|
||||
no action is specified, the "asset" conversion is selected.
|
||||
|
||||
ACTIONS:
|
||||
asset convert asset into source file or binary file
|
||||
addin convert binary into addin file for vxOS
|
||||
doctor try to display assets and addin information (debug)
|
||||
|
||||
See `vxsdk conv-<action> --help` for more information on a specific action
|
||||
"""
|
||||
)
|
||||
|
||||
def cli_validate(name):
|
||||
""" validate the module name """
|
||||
return name.find('conv') == 0
|
||||
|
||||
def cli_parse(argv):
|
||||
""" Build subcommand entry """
|
||||
if '--help' in argv or '-h' in argv:
|
||||
log.user(__VXSDK_MODULE_META__[2])
|
||||
return 0
|
||||
if argv[0].find('conv-') != 0:
|
||||
argv[0] = 'conv-asset'
|
||||
action = argv[0][5:]
|
||||
if action == 'doctor':
|
||||
return doctor_conv_cli(argv[1:])
|
||||
if action == 'asset':
|
||||
return assets_conv_cli(argv[1:])
|
||||
if action == 'addin':
|
||||
return addin_conv_cli(argv[1:])
|
||||
log.error(f"unable to find action '{action}'")
|
||||
return 84
|
|
@ -1,69 +0,0 @@
|
|||
"""
|
||||
cli.conv.addin - vxSDK addin conversion
|
||||
"""
|
||||
import sys
|
||||
|
||||
#from core.conv.addin import generate_addin
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'addin_conv_cli'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
__HELP__ = """vxsdk-converter-addin
|
||||
Converte binary file into Vhex OS addin.
|
||||
|
||||
USAGE:
|
||||
vxsdk conv addin -b BINARY ...
|
||||
|
||||
DESCRIPTION:
|
||||
Convert a binary file into an application for the Vhex operating system.
|
||||
|
||||
OPTIONS:
|
||||
-b <binary path> ELF file (no check is performed in this file)
|
||||
-i <icon path> 92x62 pixel image path
|
||||
-o <output path> output path for the generated addin
|
||||
-n <internal name> internal addin name
|
||||
-v <internal version> internal addin version
|
||||
"""
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def addin_conv_cli(argv):
|
||||
"""Process CLI arguments"""
|
||||
if '-h' in argv or '--help' in argv:
|
||||
log.user(__HELP__)
|
||||
sys.exit(0)
|
||||
|
||||
action = None
|
||||
info = [None, None, None, None, None]
|
||||
for arg in argv:
|
||||
if action == '-b':
|
||||
info[0] = arg
|
||||
if action == '-i':
|
||||
info[1] = arg
|
||||
if action == '-n':
|
||||
info[2] = arg
|
||||
if action == '-o':
|
||||
info[3] = arg
|
||||
if action == '-v':
|
||||
info[4] = arg
|
||||
if action:
|
||||
action = None
|
||||
continue
|
||||
if arg in ['-b', '-i', '-n', '-o', '-v']:
|
||||
action = arg
|
||||
continue
|
||||
|
||||
if not info[0]:
|
||||
log.error('converter: need binary path !')
|
||||
sys.exit(84)
|
||||
|
||||
log.error('not supported addin convertion')
|
||||
#return generate_addin(info[0], info[1], info[2], info[3], info[4])
|
|
@ -1,75 +0,0 @@
|
|||
"""
|
||||
cli.conv.assets - Vhex asset converter user interface
|
||||
"""
|
||||
import os
|
||||
|
||||
from core.logger import log
|
||||
from core.conv import assets_generate
|
||||
|
||||
__all__ = [
|
||||
'assets_conv_cli'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
__HELP__ = """vxsdk-converter-asset
|
||||
Convert all assets file in the project directory.
|
||||
|
||||
USAGE:
|
||||
vxsdk conv-asset [project path] [OPTIONS]
|
||||
|
||||
DESCRIPTION:
|
||||
Convert all assets file in the asset directory. This part of the converter
|
||||
module will scan the provided folder (or the current working directory) and
|
||||
will try to find all `vxconv.txt` file, which describe all assets that
|
||||
should be converted.
|
||||
|
||||
If no argument is provided, then the current working directory is used as
|
||||
asset prefix and a storag for all generated source file. You can modify
|
||||
this behaviour using OPTIONS.
|
||||
|
||||
For more information about the `vxconv.txt` in the wiki.
|
||||
|
||||
OPTIONS:
|
||||
-o <output prefix> The prefix for source file that will be generated
|
||||
-h, --help Display this help
|
||||
"""
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def assets_conv_cli(argv):
|
||||
"""Process CLI arguments"""
|
||||
# check obvious flags
|
||||
if '-h' in argv or '--help' in argv:
|
||||
log.user(__HELP__)
|
||||
return 0
|
||||
|
||||
# fetch user indication
|
||||
manual_output = False
|
||||
prefix_output = None
|
||||
prefix_asset = None
|
||||
for arg in argv:
|
||||
if arg == '-o':
|
||||
manual_output = True
|
||||
continue
|
||||
if manual_output:
|
||||
prefix_output = arg
|
||||
continue
|
||||
if prefix_asset:
|
||||
log.warn(f"warning: previous path ({prefix_asset}) dropped")
|
||||
prefix_asset = arg
|
||||
|
||||
# check indication
|
||||
if not prefix_asset:
|
||||
prefix_asset = os.getcwd()
|
||||
if not prefix_output:
|
||||
prefix_output = os.getcwd()
|
||||
prefix_asset = os.path.abspath(prefix_asset)
|
||||
prefix_output = os.path.abspath(prefix_output)
|
||||
|
||||
# generate asset information
|
||||
return assets_generate(prefix_asset, prefix_output, True)
|
|
@ -1,24 +0,0 @@
|
|||
"""
|
||||
cli.conv.doctor - vxSDK converter doctor.
|
||||
"""
|
||||
import sys
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'doctor_conv_cli'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def doctor_conv_cli(_):
|
||||
"""Process CLI handling
|
||||
|
||||
TODO:
|
||||
> give asset file description to check error
|
||||
> try to display asset and addin information based on the project type
|
||||
"""
|
||||
log.warn('conv: doctor action not implemented yet')
|
||||
sys.exit(84)
|
|
@ -1,54 +0,0 @@
|
|||
"""
|
||||
cli.project - handles project creation and more
|
||||
"""
|
||||
import sys
|
||||
|
||||
from core.project import new_project
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'__VXSDK_MODULE_META__',
|
||||
'cli_validate',
|
||||
'cli_parse'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
__VXSDK_MODULE_META__ = (
|
||||
['p', 'project'],
|
||||
"project abstraction",
|
||||
r"""vxsdk-project
|
||||
Abstract project manipulation
|
||||
|
||||
USAGE:
|
||||
vxsdk project <COMMAND> [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print helps information
|
||||
|
||||
Common used commands:
|
||||
n, new Create a new project
|
||||
|
||||
See `vxsdk project <action> --help` for more information on a specific command
|
||||
"""
|
||||
)
|
||||
|
||||
def cli_validate(name):
|
||||
""" validate the module name """
|
||||
return name in __VXSDK_MODULE_META__[0]
|
||||
|
||||
def cli_parse(argv):
|
||||
""" Project subcommand entry """
|
||||
if len(argv) > 1:
|
||||
if '-h' in argv or '--help' in argv:
|
||||
log.user(__VXSDK_MODULE_META__[2])
|
||||
sys.exit(0)
|
||||
if len(argv) > 3:
|
||||
if argv[0] in ['n', 'new']:
|
||||
for path in argv[1:]:
|
||||
new_project(path)
|
||||
sys.exit(0)
|
||||
log.error(__VXSDK_MODULE_META__[2])
|
||||
sys.exit(84)
|
|
@ -2,8 +2,6 @@
|
|||
core.build.compile - Compilation hadling using the dependency DAG graph
|
||||
"""
|
||||
|
||||
import core.conv
|
||||
|
||||
from core.logger import log
|
||||
from core.build.rules import rules_project_exec
|
||||
|
||||
|
@ -15,20 +13,6 @@ __all__ = [
|
|||
# Internals
|
||||
#---
|
||||
|
||||
def _dep_generate_assets(dep_info, _):
|
||||
dep_meta = dep_info['meta']
|
||||
counter = 0
|
||||
for prefix in dep_meta.get_assets_prefix_list():
|
||||
if counter == 0:
|
||||
log.user(f"[{dep_meta.name}] generate assets...")
|
||||
core.conv.assets_generate(
|
||||
prefix,
|
||||
f"{dep_meta.build_prefix}/converter/{dep_meta.name}",
|
||||
force_generate=False
|
||||
)
|
||||
counter += 1
|
||||
return 0
|
||||
|
||||
def _dep_build_sources(dep_info, verbose):
|
||||
log.user(f"[{dep_info['meta'].name}] build sources...")
|
||||
return rules_project_exec(
|
||||
|
@ -58,15 +42,12 @@ def _compile_dependency(dep, verbose):
|
|||
> 0 on success, negative value otherwise
|
||||
"""
|
||||
dep_meta = dep['info']['meta']
|
||||
if _dep_generate_assets(dep['info'], verbose) != 0:
|
||||
log.error(f"[{dep_meta.name}] error during asset generation")
|
||||
return -1
|
||||
if _dep_build_sources(dep['info'], verbose) != 0:
|
||||
log.error(f"[{dep_meta.name}] error during source build")
|
||||
return -2
|
||||
return -1
|
||||
if _dep_install(dep['info'], verbose) != 0:
|
||||
log.error(f"[{dep_meta.name}] error during installation")
|
||||
return -3
|
||||
return -2
|
||||
return 0
|
||||
|
||||
#---
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
"""
|
||||
core.conv - Vhex converter module
|
||||
"""
|
||||
|
||||
from core.conv.assets import assets_generate
|
||||
|
||||
__all__ = [
|
||||
'generate_assets'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def generate_assets(prefix_assets, prefix_src, force_generate=True):
|
||||
r"""Generate Vhex assets.
|
||||
|
||||
This function abstract the asset convertion for the Vhex Operating System.
|
||||
It will walk througt the `<source_prefix>` folder and will try to find some
|
||||
files named 'vxconv.txt' wich discribe assets information of a potential
|
||||
project. Then it will use them to convert assets into an appropriate source
|
||||
file in C without using any Vhex-specific function (so, you can use this
|
||||
converter for other project).
|
||||
|
||||
The vxconv.txt file is structured like basic key/value file:
|
||||
|
||||
```
|
||||
<exposed_symbols_name>:
|
||||
type: <image type> (font, bitmap) - required
|
||||
path: <image path> - required
|
||||
...
|
||||
|
||||
<next_exposed_symbols_name>:
|
||||
...
|
||||
```
|
||||
|
||||
Each asset file description should have at least type and name information,
|
||||
and each type have potentially its own requierements.
|
||||
|
||||
type = bitmap:
|
||||
================================== ========================================
|
||||
Keys name and value type Description
|
||||
================================== ========================================
|
||||
profile: <name> Select the bitmap pixel profile
|
||||
| rgb4 | RGB 4 (indexed)
|
||||
| rgb4a | RGBA 4 (indexed)
|
||||
| rgb8 | RGB 8 (indexed)
|
||||
| rgb8a | RGBA 8 (indexed)
|
||||
| rgb16 | RGB 16 (5:R, 6:G, 5:B)
|
||||
| rgb16a | RGBA 16 (5:R, 5:G, 5:B, 1:A)
|
||||
================================== ========================================
|
||||
|
||||
type = font:
|
||||
================================== ========================================
|
||||
Keys name and value type Description
|
||||
================================== ========================================
|
||||
grid.size: 8x9 (widthxheight) caracter size in pixel
|
||||
grid.padding: <pixel> space between caracter
|
||||
grig.border: <pixel> space around grid
|
||||
proportional: <true,false> caracter are cropped
|
||||
line_height: <pixel> caracter line alignement
|
||||
charset: <print,unicode> charset specification
|
||||
================================== ========================================
|
||||
|
||||
@args:
|
||||
> path (str) - the path to find assets
|
||||
> source_prefix (str) - the path to generate image source file
|
||||
|
||||
@return:
|
||||
> a list of string which represents all assets sources files path
|
||||
"""
|
||||
return assets_generate(prefix_assets, prefix_src, force_generate)
|
|
@ -1,67 +0,0 @@
|
|||
#from core.conv.pixel import rgba8conv
|
||||
#
|
||||
#from PIL import Image
|
||||
#import os
|
||||
#
|
||||
#__all__ = [
|
||||
# 'generate_addin'
|
||||
#]
|
||||
#
|
||||
#
|
||||
#def generate_addin(binary, icon=None, name=None, output=None, version=None):
|
||||
# r"""Generate an addin for the Vhex Operating System.
|
||||
#
|
||||
# The addin name (passed through the `name` argument) is optional here. In
|
||||
# this case, the name will use the internal name...which can be guessed using
|
||||
# the binary name (e.i '/path/to/the/super_addin.elf' -> internal name =
|
||||
# 'super_addin' -> output name = '/path/to/the/super_addin').
|
||||
#
|
||||
# The output path for the generated file is, by defautl, the same path that
|
||||
# the binary but the suffix '.vxos' will be added.
|
||||
#
|
||||
# if the icon is not specified, a default blank icon will be used.
|
||||
#
|
||||
# Args:
|
||||
# > binary (str) - binary path
|
||||
# > icon (str) - addin icon path (optional)
|
||||
# > name (str) - addin name (displayed in the menu) (optional)
|
||||
# > output (str) - output path for the generated addin (optional)
|
||||
#
|
||||
# _fixme:
|
||||
# > generate default internal name
|
||||
# > change 8-bits icon into rgb565
|
||||
# > add internal addin version in the header
|
||||
# """
|
||||
# if not os.path.exists(binary):
|
||||
# logger(LOG_ERR, 'binary path is invalid')
|
||||
# sys.exit(84)
|
||||
# if icon and not os.path.exists(icon):
|
||||
# logger(LOG_WARN, f'{icon}: icon does not exists, ignored')
|
||||
# icon = None
|
||||
# if not name:
|
||||
# name = ''
|
||||
# if not output:
|
||||
# output = binary + '.vxos'
|
||||
#
|
||||
# if icon:
|
||||
# bitmap = Image.open(icon)
|
||||
# if bitmap.size != (92, 64):
|
||||
# logger(
|
||||
# LOG_ERR,
|
||||
# f'{icon}:icon size does not match {bitmap.size} != (92, 64)',
|
||||
# exit=84
|
||||
# )
|
||||
#
|
||||
# with open(binary, 'rb') as b:
|
||||
# with open(output, 'wb') as a:
|
||||
# a.write(b'VHEX')
|
||||
# a.write(name.encode('utf8'))
|
||||
# a.write(b'\x00')
|
||||
# if icon:
|
||||
# for pixel in bitmap.getdata():
|
||||
# a.write(rgba8conv(pixel).to_bytes(1, 'big'))
|
||||
# else:
|
||||
# a.write(bytes(92*64))
|
||||
# a.write(b.read())
|
||||
#
|
||||
# return 0
|
|
@ -1,132 +0,0 @@
|
|||
"""
|
||||
core.conv.assets - Vhex assets converter
|
||||
"""
|
||||
import os
|
||||
import toml
|
||||
|
||||
from core.logger import log
|
||||
from core.conv.type.font import font_generate
|
||||
from core.conv.type.image import image_generate
|
||||
|
||||
__all__ = [
|
||||
'assets_generate'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
class _VxAssetException(Exception):
|
||||
""" simple exception wrapper """
|
||||
|
||||
class _VxAsset():
|
||||
"""Represent a asset object
|
||||
|
||||
This is an internal class which represents assets information with some
|
||||
methods to abstract conversion and file type manipulation (for asset type
|
||||
font and asset type bitmap).
|
||||
|
||||
Also note that this class is private because we use a tricky optimization
|
||||
to parse the `vxconv.txt` file, this is why we have no "private" property
|
||||
with setter and getter, and why this class is "hidden".
|
||||
|
||||
Some important methods to note:
|
||||
|
||||
================================== =======================================
|
||||
Name Description
|
||||
================================== =======================================
|
||||
generate() Generate the source file (C)
|
||||
================================== =======================================
|
||||
|
||||
"""
|
||||
def __init__(self, prefix, name, meta):
|
||||
if 'path' not in meta:
|
||||
raise _VxAssetException(f"[{name}] missing path information")
|
||||
if 'type' not in meta:
|
||||
raise _VxAssetException(f"[{name}] missing type information")
|
||||
if meta['type'] not in ['font', 'image']:
|
||||
raise _VxAssetException(f"asset type '{meta[type]}' is not known")
|
||||
|
||||
self._name = name
|
||||
self._meta = meta
|
||||
self._type = meta['type']
|
||||
self._path = prefix + '/' + meta['path']
|
||||
if not os.path.exists(self.path):
|
||||
raise _VxAssetException(
|
||||
"asset path '{self._path}' cannot be openned"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<_VxAssetObj, {self.name}>'
|
||||
|
||||
def __str__(self):
|
||||
content = f"[{self.name}]\n"
|
||||
content += f" - type: {self.type}\n"
|
||||
content += f" - path: {self.path}\n"
|
||||
return content
|
||||
|
||||
#---
|
||||
# Getter
|
||||
#---
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""<property> path"""
|
||||
return self._path
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""<property> name"""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""<property> type"""
|
||||
return self._type
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
"""<property> meta"""
|
||||
return self._meta
|
||||
|
||||
#---
|
||||
# Public method
|
||||
#---
|
||||
|
||||
def generate_source_file(self, prefix_output, force_generate):
|
||||
"""generate source file """
|
||||
if self.type == 'font':
|
||||
return font_generate(self, prefix_output, force_generate)
|
||||
return image_generate(self, prefix_output, force_generate)
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def assets_generate(prefix_assets, prefix_output, force_generate):
|
||||
""" Walk through the assets prefix and generate all source file
|
||||
|
||||
@args
|
||||
> prefix_asset (str) - prefix used for recursivly search for asset info
|
||||
> prefix_output (str) - prefix used for the output of generated file
|
||||
> force_generate (bool) - force generate the source file
|
||||
|
||||
@return
|
||||
> a list of all generated sources pathname
|
||||
"""
|
||||
if not os.path.exists(prefix_output):
|
||||
os.makedirs(prefix_output)
|
||||
generated = []
|
||||
for root, _, files in os.walk(prefix_assets):
|
||||
if 'vxconv.toml' not in files:
|
||||
continue
|
||||
with open(f"{root}/vxconv.toml", 'r', encoding='utf-8') as inf:
|
||||
content = toml.loads(inf.read())
|
||||
for asset_name in content:
|
||||
log.user(f"converting {asset_name}...")
|
||||
asset = _VxAsset(root, asset_name, content[asset_name])
|
||||
generated += asset.generate_source_file(
|
||||
prefix_output,
|
||||
force_generate
|
||||
)
|
||||
return generated
|
|
@ -1,45 +0,0 @@
|
|||
"""
|
||||
core.conv.pixel - Pixel converter utilities
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
'pixel_rgb24to16'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def pixel_rgb24to16(rgb):
|
||||
""" convert RGB24 -> RGB16-565"""
|
||||
_r = (rgb[0] & 0xff) >> 3
|
||||
_g = (rgb[1] & 0xff) >> 2
|
||||
_b = (rgb[2] & 0xff) >> 3
|
||||
return (_r << 11) | (_g << 5) | _b
|
||||
|
||||
#def rgb1conv(pixel):
|
||||
# """ Convert RGB<BLACK> -> 1-bit color """
|
||||
# return pixel == (0, 0, 0)
|
||||
|
||||
#def rgb8conv(pixel):
|
||||
# """ Convert RGB8 -> """
|
||||
# return int((pixel[0] * 7) / 255) << 5 \
|
||||
# | int((pixel[1] * 4) / 255) << 3 \
|
||||
# | int((pixel[2] * 7) / 255) << 0
|
||||
|
||||
#def rgba8conv(pixel):
|
||||
# return int((pixel[0] * 4) / 256) << 6 \
|
||||
# | int((pixel[1] * 8) / 256) << 3 \
|
||||
# | int((pixel[2] * 4) / 256) << 1 \
|
||||
# | (len(pixel) >= 4 and pixel[3] == 0)
|
||||
|
||||
#def rgb16conv(pixel):
|
||||
# return int((pixel[0] * 31) / 255) << 11 \
|
||||
# | int((pixel[1] * 63) / 255) << 5 \
|
||||
# | int((pixel[2] * 31) / 255) << 0
|
||||
|
||||
#def rgba16conv(pixel):
|
||||
# return int((pixel[0] * 31) / 255) << 11 \
|
||||
# | int((pixel[1] * 63) / 255) << 6 \
|
||||
# | int((pixel[2] * 31) / 255) << 1 \
|
||||
# | (pixel[3] != 0)
|
|
@ -1,468 +0,0 @@
|
|||
"""
|
||||
core.conv.type.font - Vhex font converter
|
||||
"""
|
||||
import os
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'font_generate'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
## Font meta info
|
||||
|
||||
def _font_fetch_info(asset):
|
||||
""" Check and fetch font information
|
||||
|
||||
@arg
|
||||
> asset (VxAsset) - asset information
|
||||
|
||||
@return
|
||||
> dictionary with font information
|
||||
"""
|
||||
# generate font default information
|
||||
font_info = {
|
||||
# user can customise
|
||||
'charset' : 'normal',
|
||||
'grid_size_x' : 0,
|
||||
'grid_size_y' : 0,
|
||||
'grid_padding' : 1,
|
||||
'grid_border' : 1,
|
||||
'is_proportional' : False,
|
||||
'line_height' : 0,
|
||||
'char_spacing' : 1,
|
||||
|
||||
# generated "on-the-fly" by the conversion step
|
||||
# @notes
|
||||
# This is mainly to provide cache for the Vhex operating system to
|
||||
# speed-up render calculation by avoiding recurent caculation.
|
||||
'glyph_size' : 0,
|
||||
'glyph_height' : 0,
|
||||
'font_size' : 0,
|
||||
'data' : []
|
||||
}
|
||||
|
||||
# handle user meta-indication
|
||||
if 'charset' in asset.meta:
|
||||
if asset.meta['charset'] not in ['default', 'unicode']:
|
||||
log.error(f"Unknown charset '{asset.meta['charset']}', abord")
|
||||
return None
|
||||
font_info['charset'] = asset.meta['charset']
|
||||
if 'grid_size' not in asset.meta:
|
||||
log.error("Missing critical grid size information, abord")
|
||||
return None
|
||||
grid_size = asset.meta['grid_size'].split('x')
|
||||
font_info['grid_size_x'] = int(grid_size[0])
|
||||
font_info['grid_size_y'] = int(grid_size[1])
|
||||
if 'grid_padding' in asset.meta:
|
||||
font_info['grid_padding'] = int(asset.meta['grid_padding'])
|
||||
if 'grid_border' in asset.meta:
|
||||
font_info['grid_border'] = int(asset.meta['grid_border'])
|
||||
if 'proportional' in asset.meta:
|
||||
font_info['is_proportional'] = asset.meta['proportional']
|
||||
font_info['line_height'] = font_info['grid_size_y']
|
||||
if 'line_height' in asset.meta:
|
||||
font_info['line_height'] = asset.meta['line_height']
|
||||
if 'char_spacing' in asset.meta:
|
||||
font_info['char_spacing'] = asset.meta['char_spacing']
|
||||
font_info['glyph_height'] = font_info['grid_size_y']
|
||||
|
||||
# return font information
|
||||
return font_info
|
||||
|
||||
## Glyph handling
|
||||
|
||||
def _glyph_get_wgeometry(geometry_info, img_raw, img_size, pos, grid_size):
|
||||
""" Generate glyph width geometry information
|
||||
|
||||
@args
|
||||
> geometry_info (dict) - geometry information
|
||||
> img_raw (list) - list of all pixel of the image
|
||||
> img_size (tuple) - image width and image height
|
||||
> pos (tuple) - glyph position information (X and Y in pixel)
|
||||
> grid_size (tuple) - glyph grid size information (width and height)
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
|
||||
geometry_info['wstart'] = -1
|
||||
geometry_info['wend'] = -1
|
||||
|
||||
_px = pos[0]
|
||||
_py = pos[1]
|
||||
log.debug(f'[geometry] X:{pos[0]} Y:{int(pos[1]/img_size[0])}')
|
||||
log.debug(f' - grid_size = {grid_size}')
|
||||
for _ in range(0, grid_size[1]):
|
||||
for offx in range(0, grid_size[0]):
|
||||
if img_raw[_py + (_px + offx)][:3] == (255, 255, 255):
|
||||
continue
|
||||
if geometry_info['wstart'] < 0 or offx < geometry_info['wstart']:
|
||||
geometry_info['wstart'] = offx
|
||||
if geometry_info['wstart'] < 0 or offx > geometry_info['wend']:
|
||||
geometry_info['wend'] = offx
|
||||
_py += img_size[0]
|
||||
geometry_info['wend'] += 1
|
||||
log.debug(f' - geometry = {geometry_info}')
|
||||
|
||||
def _glyph_encode(data_info, img_info, geometry, posx, posy):
|
||||
""" Encode glyph bitmap
|
||||
|
||||
@args
|
||||
> data_info (dict) - internal data information (list, index and shift)
|
||||
> img_info (dict) - image-related information (object and raw content)
|
||||
> geometry (dict) - geometry information
|
||||
> posx (int) - X-axis position in pixel
|
||||
> posy (int) - Y-axis position in pixel
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
# fetch information
|
||||
img = img_info['obj']
|
||||
img_raw = img_info['raw']
|
||||
data = data_info['table']
|
||||
data_idx = data_info['idx']
|
||||
data_shift = data_info['shift']
|
||||
wstart = geometry['wstart']
|
||||
wend = geometry['wend']
|
||||
|
||||
# encode the glyph
|
||||
yoff = 0
|
||||
log.debug(f'[encode] X:{posx} Y:{int(posy/img.size[0])}')
|
||||
for _h in range(geometry['hstart'], geometry['hend']):
|
||||
for _w in range(wstart, wend):
|
||||
if img_raw[(posy + yoff) + (posx + _w)][:3] == (0, 0, 0):
|
||||
log.debug('#', end='')
|
||||
data[data_idx] |= 0x80000000 >> data_shift
|
||||
else:
|
||||
log.debug('.', end='')
|
||||
data[data_idx] &= ~(0x80000000 >> data_shift)
|
||||
if (data_shift := data_shift + 1) >= 32:
|
||||
data_shift = 0
|
||||
data_idx += 1
|
||||
log.debug('')
|
||||
yoff += img.size[0]
|
||||
|
||||
# commit modification
|
||||
data_info['idx'] = data_idx
|
||||
data_info['shift'] = data_shift
|
||||
|
||||
## Font convert handling
|
||||
|
||||
def _font_convert_proportional(packed_info):
|
||||
""" Generate proportional font
|
||||
|
||||
Proportional font means that each character have its own width size (but
|
||||
have a common height). We need to performs more complexe handling than the
|
||||
monospaced one.
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> font_information (dict) - font indication
|
||||
|
||||
@return
|
||||
> 0 if success, negative value otherwise
|
||||
"""
|
||||
# unpack information
|
||||
font_info = packed_info[0]
|
||||
img_info = packed_info[1]
|
||||
glyph_info = packed_info[2]
|
||||
data_info = packed_info[4]
|
||||
geometry_info = packed_info[5]
|
||||
|
||||
# isolate needed information
|
||||
img = img_info['obj']
|
||||
img_raw = img_info['raw']
|
||||
nb_col = packed_info[3][0]
|
||||
nb_row = packed_info[3][1]
|
||||
gwidth = glyph_info[0]
|
||||
gheight = glyph_info[1]
|
||||
|
||||
# main loop, walk glyph per glyph
|
||||
_py = (font_info['grid_border'] + font_info['grid_padding']) * img.size[0]
|
||||
for _ in range(0, nb_row):
|
||||
_px = font_info['grid_border'] + font_info['grid_padding']
|
||||
for _ in range(0, nb_col):
|
||||
# generate width geometry information
|
||||
_glyph_get_wgeometry(
|
||||
geometry_info,
|
||||
img_raw,
|
||||
img.size,
|
||||
(_px, _py),
|
||||
(font_info['grid_size_x'], font_info['grid_size_y'])
|
||||
)
|
||||
|
||||
# save critical glyph geometry information that will be encoded in
|
||||
# the final C source file
|
||||
font_info['glyph_props'].append((
|
||||
geometry_info['wend'] - geometry_info['wstart'],
|
||||
data_info['idx'],
|
||||
data_info['shift']
|
||||
))
|
||||
|
||||
# encode glyph information
|
||||
_glyph_encode(data_info, img_info, geometry_info, _px, _py)
|
||||
|
||||
# update loop information
|
||||
font_info['glyph_count'] += 1
|
||||
_px += gwidth
|
||||
_py += gheight * img.size[0]
|
||||
return 0
|
||||
|
||||
def _font_convert_monospaced(packed_info):
|
||||
""" Generate proportional font
|
||||
|
||||
Proportional font means that each character have its own width size (but
|
||||
have a common height). We need to performs more complexe handling than the
|
||||
monospaced one.
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> font_information (dict) - font indication
|
||||
|
||||
@return
|
||||
> 0 if success, negative value otherwise
|
||||
"""
|
||||
# unpack information
|
||||
font_info = packed_info[0]
|
||||
img_info = packed_info[1]
|
||||
glyph_info = packed_info[2]
|
||||
grid_info = packed_info[3]
|
||||
data_info = packed_info[4]
|
||||
geometry_info = packed_info[5]
|
||||
|
||||
# isolate needed information
|
||||
img = img_info['obj']
|
||||
nb_row = grid_info[1]
|
||||
nb_col = grid_info[0]
|
||||
gwidth = glyph_info[0]
|
||||
gheight = glyph_info[1]
|
||||
|
||||
# main loop, walk glyph per glyph
|
||||
_py = (font_info['grid_border'] + font_info['grid_padding']) * img.size[0]
|
||||
for _ in range(0, nb_row):
|
||||
_px = font_info['grid_border'] + font_info['grid_padding']
|
||||
for _ in range(0, nb_col):
|
||||
_glyph_encode(data_info, img_info, geometry_info, _px, _py)
|
||||
font_info['glyph_count'] += 1
|
||||
_px += gwidth
|
||||
_py += gheight * img.size[0]
|
||||
return 0
|
||||
|
||||
def _font_convert(asset, font_info):
|
||||
""" Generate font information
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> font_info (dict) - font information
|
||||
|
||||
@return
|
||||
> 0 if success, negative value otherwise
|
||||
"""
|
||||
# generate image information
|
||||
img = Image.open(asset.path)
|
||||
img_raw = img.getdata()
|
||||
img_info = {
|
||||
'obj' : img,
|
||||
'raw' : img_raw
|
||||
}
|
||||
|
||||
# pre-calculate the "real" glyph width and height using padding information
|
||||
glyph_info = [0, 0]
|
||||
glyph_info[0] = font_info['grid_size_x'] + font_info['grid_padding']
|
||||
glyph_info[1] = font_info['grid_size_y'] + font_info['grid_padding']
|
||||
gheight = glyph_info[1]
|
||||
gwidth = glyph_info[0]
|
||||
log.debug(f"gwidth = {gwidth} && gheight = {gheight}")
|
||||
|
||||
# pre-calculate the number of row and column of the font
|
||||
grid_info = [0, 0]
|
||||
grid_info[0] = int((img.size[0] - (font_info['grid_border'] * 2)) / gwidth)
|
||||
grid_info[1] = int((img.size[1] - (font_info['grid_border'] * 2)) /gheight)
|
||||
nb_col = grid_info[0]
|
||||
nb_row = grid_info[1]
|
||||
log.debug(f"nb_row = {nb_row} && nb_col = {nb_col}")
|
||||
|
||||
# pre-calculate and prepare per-glyph information
|
||||
# @note
|
||||
# The generated data is designed for 4-alignement padding. This to have
|
||||
# speed-up on drawing function.
|
||||
font_info['glyph_size'] = font_info['grid_size_x']
|
||||
font_info['glyph_size'] *= font_info['grid_size_y']
|
||||
font_info['font_size'] = font_info['glyph_size'] * nb_row * nb_col
|
||||
font_info['glyph_count'] = 0
|
||||
font_info['glyph_props'] = []
|
||||
font_info['data'] = [0] * int((font_info['font_size'] + 31) / 32)
|
||||
log.debug(f"data original = {id(font_info['data'])}")
|
||||
|
||||
# generate data information
|
||||
data_info = {
|
||||
'table' : font_info['data'],
|
||||
'idx' : 0,
|
||||
'shift' : 0
|
||||
}
|
||||
log.debug(f"data packed = {id(data_info['table'])}")
|
||||
|
||||
# generate geometry information
|
||||
geometry_info = {
|
||||
'hstart' : 0,
|
||||
'hend' : font_info['grid_size_y'],
|
||||
'wstart' : 0,
|
||||
'wend' : font_info['grid_size_x'],
|
||||
}
|
||||
|
||||
# select the converter
|
||||
converter = _font_convert_monospaced
|
||||
if font_info['is_proportional']:
|
||||
converter = _font_convert_proportional
|
||||
|
||||
# convert font
|
||||
converter((
|
||||
font_info,
|
||||
img_info,
|
||||
glyph_info,
|
||||
grid_info,
|
||||
data_info,
|
||||
geometry_info
|
||||
))
|
||||
|
||||
log.debug(f"data packed end = {id(data_info['table'])}")
|
||||
return 0
|
||||
|
||||
## Font generator
|
||||
|
||||
def _font_generate_unicode_source(_):
|
||||
""" Unicode special chaset directory """
|
||||
log.error("unicode conversion not implemented yet o(x_x)o")
|
||||
return ''
|
||||
|
||||
def _font_generate_normal_source(font_info):
|
||||
""" Print chaset is a image file
|
||||
"""
|
||||
content = "\t.glyph = {\n"
|
||||
content += f"\t\t.height = {font_info['glyph_height']},\n"
|
||||
content += f"\t\t.line_height = {font_info['line_height']},\n"
|
||||
|
||||
# encode font bitmap
|
||||
line = 0
|
||||
log.debug(f"data = {font_info['data']}")
|
||||
content += "\t\t.data = (uint32_t[]){\n"
|
||||
for pixel in font_info['data']:
|
||||
if line == 0:
|
||||
content += '\t\t\t'
|
||||
if line >= 1:
|
||||
content += ' '
|
||||
content += f"{pixel:#010x},"
|
||||
if (line := line + 1) == 4:
|
||||
content += '\n'
|
||||
line = 0
|
||||
if line != 0:
|
||||
content += '\n'
|
||||
content += '\t\t},\n'
|
||||
|
||||
# indicate the number of glyph in the bitmap
|
||||
content += f"\t\t.count = {font_info['glyph_count']},\n"
|
||||
|
||||
# encode proportional information if needed
|
||||
if font_info['is_proportional']:
|
||||
content += '\t\t.prop = (struct __workaround[]){\n'
|
||||
for prop in font_info['glyph_props']:
|
||||
content += "\t\t\t{\n"
|
||||
content += f"\t\t\t\t.width = {prop[0]},\n"
|
||||
content += f"\t\t\t\t.index = {prop[1]},\n"
|
||||
content += f"\t\t\t\t.shift = {prop[2]},\n"
|
||||
content += "\t\t\t},\n"
|
||||
else:
|
||||
content += "\t\t.mono = {,\n"
|
||||
content += f"\t\t\t.width = {font_info['glyph_width']},\n"
|
||||
content += f"\t\t\t.size = {font_info['glyph_size']},\n"
|
||||
content += "\t\t},\n"
|
||||
content += "\t},\n"
|
||||
|
||||
# skip unicode struct
|
||||
content += "\t.unicode = {\n"
|
||||
content += "\t\t.blocks = NULL,\n"
|
||||
content += "\t\t.block_count = 0,\n"
|
||||
content += "\t}\n"
|
||||
return content
|
||||
|
||||
def _font_generate_source_file(asset, font_info):
|
||||
""" Generate font source file content
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> info (dict) - hold font information
|
||||
|
||||
@return
|
||||
> file C content string
|
||||
"""
|
||||
# generate basic header
|
||||
content = "#include <vhex/display/font.h>\n"
|
||||
content += "\n"
|
||||
content += f"/* {asset.name} - Vhex asset\n"
|
||||
content += " This object has been converted by using the vxSDK "
|
||||
content += "converter */\n"
|
||||
content += f"struct font const {asset.name} = " + "{\n"
|
||||
content += f"\t.name = \"{asset.name}\",\n"
|
||||
|
||||
# shape information
|
||||
content += "\t.shape = {\n"
|
||||
content += "\t\t.bold = 0,\n"
|
||||
content += "\t\t.italic = 0,\n"
|
||||
content += "\t\t.serif = 0,\n"
|
||||
content += "\t\t.mono = 0,\n"
|
||||
content += f"\t\t.prop = {int(font_info['is_proportional'])},\n"
|
||||
content += "\t},\n"
|
||||
|
||||
# manage display indication
|
||||
content += f"\t.char_spacing = {font_info['char_spacing']},\n"
|
||||
|
||||
# handle special charset behaviour
|
||||
if font_info['charset'] == 'unicode':
|
||||
content += _font_generate_unicode_source(font_info)
|
||||
else:
|
||||
content += _font_generate_normal_source(font_info)
|
||||
|
||||
# closure and return
|
||||
content += '};\n'
|
||||
return content
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def font_generate(asset, prefix_output, force_generate):
|
||||
""" Convert an image asset to a C source file
|
||||
|
||||
@args
|
||||
> asset (_VxAsset) - minimal asset information
|
||||
> prefix_output (str) - prefix for source file generation
|
||||
> force_generate (bool) - force generate the source file
|
||||
|
||||
@return
|
||||
> pathname of the generated file
|
||||
"""
|
||||
# check if the asset already exists
|
||||
asset_src = f'{prefix_output}/{asset.name}_vxfont.c'
|
||||
if not force_generate and os.path.exists(asset_src):
|
||||
return asset_src
|
||||
|
||||
# generate font information
|
||||
if not (font_info := _font_fetch_info(asset)):
|
||||
return ''
|
||||
if _font_convert(asset, font_info) != 0:
|
||||
return ''
|
||||
content = _font_generate_source_file(asset, font_info)
|
||||
|
||||
# create the source file
|
||||
with open(asset_src, "w", encoding='utf8') as file:
|
||||
file.write(content)
|
||||
log.debug(f"source file generated at {asset_src}")
|
||||
return asset_src
|
|
@ -1,403 +0,0 @@
|
|||
"""
|
||||
core.conv.type.image - Vhex image converter
|
||||
"""
|
||||
import os
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from core.logger import log
|
||||
from core.conv.pixel import pixel_rgb24to16
|
||||
|
||||
__all__ = [
|
||||
'image_generate'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
## Profile handling
|
||||
|
||||
def _profile_gen(profile, name, palette=None, alpha=None):
|
||||
r""" Internal image profile class
|
||||
|
||||
================================== ========================================
|
||||
Property Description
|
||||
================================== ========================================
|
||||
id (int) profile ID
|
||||
names (array of str) list all profile names
|
||||
format (str) profile format name (vhex API)
|
||||
has_alpha (bool) indicate if the profil has alpha
|
||||
alpha (int) alpha index in the palette (or mask)
|
||||
is_indexed (bool) indicate if it should be indexed
|
||||
palette_base (int) indicate base index for color inserting
|
||||
palette_color_count (int) indicate the number of color (palette)
|
||||
palette_trim (bool) indicate if the palette should be trimed
|
||||
================================== ========================================
|
||||
"""
|
||||
profile = {
|
||||
'profile' : profile,
|
||||
'name' : name,
|
||||
'has_alpha' : (alpha is not None),
|
||||
'alpha' : alpha,
|
||||
'is_indexed': (palette is not None),
|
||||
'palette' : None
|
||||
}
|
||||
if palette is not None:
|
||||
profile['palette_base'] = palette[0]
|
||||
profile['palette_color_count'] = palette[1]
|
||||
profile['palette_trim'] = palette[2]
|
||||
return profile
|
||||
|
||||
# all supported profile information
|
||||
VX_PROFILES = [
|
||||
_profile_gen('IMAGE_RGB565', "p16"),
|
||||
_profile_gen('IMAGE_RGB565A', "p16a", alpha=0x0001),
|
||||
_profile_gen('IMAGE_P8_RGB565', "p8", palette=(0,256,True)),
|
||||
_profile_gen('IMAGE_P8_RGB565A', "p8a", palette=(1,256,True), alpha=0),
|
||||
_profile_gen('IMAGE_P4_RGB565', "p4", palette=(0,16,False)),
|
||||
_profile_gen('IMAGE_P4_RGB565A', "p4a", palette=(1,16,False), alpha=0),
|
||||
]
|
||||
|
||||
def _profile_find(name):
|
||||
"""Find a profile by name."""
|
||||
for profile in VX_PROFILES:
|
||||
if name == profile['name']:
|
||||
return profile
|
||||
return None
|
||||
|
||||
## Image manipulation
|
||||
|
||||
def _image_isolate_alpha(info):
|
||||
""" Isolate alpha corlor of the image
|
||||
|
||||
Vhex use a particular handling for alpha color and this information should
|
||||
use a strict encoding way. Things that Pillow don't do properly. So, lets
|
||||
manually setup our alpha isolation and patch Pillow alpha palette handling.
|
||||
|
||||
@args
|
||||
> info (dict) - contains all needed information (image, data, ...)
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
# fetch needed information
|
||||
img = info['img']
|
||||
profile = info['profile']
|
||||
|
||||
# Save the alpha channel and make it 1-bit. We need to do this because
|
||||
# the alpha value is handled specialy in Vhex and the image conversion
|
||||
# to palette-oriented image is weird : the alpha colors is also converted
|
||||
# in the palette
|
||||
if profile['has_alpha']:
|
||||
alpha_channel = img.getchannel("A").convert("1", dither=Image.NONE)
|
||||
else:
|
||||
alpha_channel = Image.new("1", img.size, 1)
|
||||
|
||||
alpha_pixels = alpha_channel.load()
|
||||
img = img.convert("RGB")
|
||||
|
||||
# Transparent pixels have random values on the RGB channels, causing
|
||||
# them to use up palette entries during quantization. To avoid that, set
|
||||
# their RGB data to a color used somewhere else in the image.
|
||||
pixels = img.load()
|
||||
bg_color = next(
|
||||
(
|
||||
pixels[x,y]
|
||||
for x in range(img.width)
|
||||
for y in range(img.height)
|
||||
if alpha_pixels[x,y] > 0
|
||||
),
|
||||
(0,0,0)
|
||||
)
|
||||
for _y in range(img.height):
|
||||
for _x in range(img.width):
|
||||
if alpha_pixels[_x, _y] == 0:
|
||||
pixels[_x, _y] = bg_color
|
||||
|
||||
# update external information
|
||||
info['img'] = img
|
||||
info['img_pixel_list_alpha'] = alpha_pixels
|
||||
info['img_pixel_list_clean'] = pixels
|
||||
|
||||
def _image_encode_palette(info):
|
||||
""" Generate palette information
|
||||
|
||||
This routine is involved only if the targeted profile is indexed. We need
|
||||
to generate (and isolate) color palette.
|
||||
|
||||
@args
|
||||
> info (dict) - contains all needed information (image, data, ...)
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
# fetch needed information
|
||||
img = info['img']
|
||||
profile = info['profile']
|
||||
|
||||
# convert image into palette format
|
||||
# note: we remove one color slot in the palette for the alpha one
|
||||
color_count = profile['palette_color_count'] - int(profile['has_alpha'])
|
||||
img = img.convert(
|
||||
'P',
|
||||
dither=Image.NONE,
|
||||
palette=Image.ADAPTIVE,
|
||||
colors=color_count
|
||||
)
|
||||
|
||||
# The palette format is a list of N triplets ([r, g, b, ...]). But,
|
||||
# sometimes, colors after img.convert() are not numbered 0 to
|
||||
# `color_count`, because the palette don't need to be that big. So,
|
||||
# we calculate the "palette size" by walking throuth the bitmap and
|
||||
# by saving the biggest index used.
|
||||
pixels = img.load()
|
||||
nb_triplet = 1 + max(
|
||||
pixels[x,y]
|
||||
for y in range(img.height)
|
||||
for x in range(img.width)
|
||||
)
|
||||
palette = img.getpalette()[:3 * nb_triplet]
|
||||
palette = list(zip(palette[::3], palette[1::3], palette[2::3]))
|
||||
|
||||
# For formats with transparency, add an "unused" palette slot which
|
||||
# will used has pink/purple in case of a bad application try to use
|
||||
# this value anyway
|
||||
if profile['has_alpha']:
|
||||
palette = [(255, 0, 255)] + palette
|
||||
nb_triplet += 1
|
||||
|
||||
# Also keep track of how to remap indices from the values generated
|
||||
# by img.convert() into the palette, which is shifted by 1 due to
|
||||
# alpha and also starts at profile.palette_base.
|
||||
#
|
||||
# Note: profile.palette_base already starts 1 value later for
|
||||
# formats with alpha.
|
||||
palette_map = [
|
||||
(profile['palette_base'] + i) % profile['palette_color_count']
|
||||
for i in range(nb_triplet)
|
||||
]
|
||||
|
||||
# Encode the palette
|
||||
palette_color_count = nb_triplet
|
||||
if not profile['palette_trim']:
|
||||
palette_color_count = profile['palette_color_count']
|
||||
|
||||
palette_data = [0] * palette_color_count
|
||||
for i, rgb24 in enumerate(palette):
|
||||
palette_data[i] = pixel_rgb24to16(rgb24)
|
||||
|
||||
# update internal information
|
||||
info['palette_map'] = palette_map
|
||||
info['palette_data'] = palette_data
|
||||
info['palette_color_count'] = palette_color_count
|
||||
info['nb_triplet'] = nb_triplet
|
||||
info['img_pixel_list_clean'] = pixels
|
||||
|
||||
def _image_encode_bitmap(info):
|
||||
""" Encode the bitmap
|
||||
|
||||
This routine will generate the main data list which will contains the
|
||||
bitmap using Vhex-specific encoding.
|
||||
|
||||
@args
|
||||
> info (dict) - contains all needed information (image, data, ...)
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
# fetch needed information
|
||||
img = info['img']
|
||||
profile = info['profile']
|
||||
alpha_pixels = info['img_pixel_list_alpha']
|
||||
pixels = info['img_pixel_list_clean']
|
||||
palette_map = info['palette_map']
|
||||
|
||||
# generate profile-specific geometry information
|
||||
if profile['name'] in ['p16', 'p16a']:
|
||||
# Preserve alignment between rows by padding to 4 bytes
|
||||
nb_stride = ((img.width + 1) // 2) * 4
|
||||
data_size = (nb_stride * img.height) * 2
|
||||
elif profile['name'] in ['p8', 'p8a']:
|
||||
nb_stride = img.width
|
||||
data_size = img.width * img.height
|
||||
else:
|
||||
# Pad whole bytes
|
||||
nb_stride = (img.width + 1) // 2
|
||||
data_size = nb_stride * img.height
|
||||
|
||||
# Generate the real data map
|
||||
data = [0] * data_size
|
||||
|
||||
# encode the bitmap
|
||||
for _y in range(img.height):
|
||||
for _x in range(img.width):
|
||||
# get alpha information about this pixel
|
||||
_a = alpha_pixels[_x, _y]
|
||||
|
||||
if profile['name'] in ['p16', 'p16a']:
|
||||
# If c lands on the alpha value, flip its lowest bit to avoid
|
||||
# ambiguity with alpha
|
||||
_c = profile['alpha']
|
||||
if not _a:
|
||||
_c = pixel_rgb24to16(pixels[_x, _y]) & ~1
|
||||
data[(img.width * _y) + _x] = _c
|
||||
|
||||
elif profile['name'] in ['p8', 'p8a']:
|
||||
_c = palette_map[pixels[_x,_y]] if _a > 0 else profile['alpha']
|
||||
data[(img.width * _y) + _x] = _c
|
||||
|
||||
else:
|
||||
_c = palette_map[pixels[_x,_y]] if _a > 0 else profile['alpha']
|
||||
offset = (nb_stride * _y) + (_x // 2)
|
||||
if _x % 2 == 0:
|
||||
data[offset] |= (_c << 4)
|
||||
else:
|
||||
data[offset] |= _c
|
||||
|
||||
# update external information
|
||||
info['data'] = data
|
||||
info['data_size'] = data_size
|
||||
info['nb_stride'] = nb_stride
|
||||
info['data_size'] = data_size
|
||||
|
||||
def _image_convert(asset, profile_name):
|
||||
""" Image asset convertion
|
||||
|
||||
@args
|
||||
> asset (_VxAsset) - asset information
|
||||
> profile_name (str) - profile name information
|
||||
|
||||
@return
|
||||
> a dictionary with all image information
|
||||
"""
|
||||
# generate critical information and check posible error
|
||||
img_info = {
|
||||
'img' : Image.open(asset.path),
|
||||
'profile' : _profile_find(profile_name)
|
||||
}
|
||||
if not img_info['img']:
|
||||
log.error(f"unable to open the asset '{asset.path}', abord")
|
||||
return None
|
||||
if not img_info['profile']:
|
||||
log.error(f"unable to find the color profile '{profile_name}', abord")
|
||||
return None
|
||||
|
||||
# convert the bitmap and generate critical information
|
||||
_image_isolate_alpha(img_info)
|
||||
if img_info['profile']['is_indexed']:
|
||||
_image_encode_palette(img_info)
|
||||
_image_encode_bitmap(img_info)
|
||||
|
||||
# return generated information
|
||||
return img_info
|
||||
|
||||
## source file content generation
|
||||
|
||||
def _display_array(array, prefix='\t\t'):
|
||||
""" Display array information (only for p16* profile) """
|
||||
line = 0
|
||||
content = ''
|
||||
for pixels in array:
|
||||
if line == 0:
|
||||
content += prefix
|
||||
if line >= 1:
|
||||
content += ' '
|
||||
content += f'{pixels:#06x},'
|
||||
if (line := line + 1) >= 8:
|
||||
content += '\n'
|
||||
line = 0
|
||||
if line != 0:
|
||||
content += '\n'
|
||||
return content
|
||||
|
||||
def _image_generate_source_file(asset, info):
|
||||
"""Generate image source file
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> info (dict) - hold image information
|
||||
|
||||
@return
|
||||
> file C content string
|
||||
"""
|
||||
img = info['img']
|
||||
profile = info['profile']
|
||||
|
||||
# generate basic header
|
||||
content = "#include <vhex/display/image/types.h>\n"
|
||||
content += "\n"
|
||||
content += f"/* {asset.name} - Vhex asset\n"
|
||||
content += " This object has been converted by using the vxSDK "
|
||||
content += "converter */\n"
|
||||
content += "const image_t " + f"{asset.name} = " + "{\n"
|
||||
content += f"\t.format = {profile['profile']},\n"
|
||||
content += "\t.flags = IMAGE_FLAGS_RO | IMAGE_FLAGS_OWN,\n"
|
||||
content += f"\t.color_count = {profile['palette_color_count']},\n"
|
||||
content += f"\t.width = {img.width},\n"
|
||||
content += f"\t.height = {img.height},\n"
|
||||
content += f"\t.stride = {info['nb_stride']},\n"
|
||||
|
||||
# encode bitmap table
|
||||
encode = 16 if profile['profile'] in ['p16', 'p16a'] else 8
|
||||
content += f"\t.data = (void*)(const uint{encode}_t [])" + "{\n"
|
||||
for _y in range(img.height):
|
||||
content += '\t\t'
|
||||
for _x in range(info['nb_stride']):
|
||||
pixel = info['data'][(_y * info['nb_stride']) + _x]
|
||||
if profile['profile'] in ['p16', 'p16a']:
|
||||
content += f'{pixel:#06x},'
|
||||
elif profile['profile'] in ['p8', 'p8a']:
|
||||
content += f'{pixel:#04x},'
|
||||
else:
|
||||
content += f'{pixel:3},'
|
||||
content += '\n'
|
||||
|
||||
content += '\t},\n'
|
||||
|
||||
# add palette information
|
||||
if 'palette_data' in info:
|
||||
content += "\t.palette = (void*)(const uint16_t []){\n"
|
||||
content += _display_array(info['palette_data'])
|
||||
content += "\t},\n"
|
||||
else:
|
||||
content += "\t.palette = NULL,\n"
|
||||
|
||||
# closure and return
|
||||
content += '};'
|
||||
return content
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def image_generate(asset, prefix_output, force_generate):
|
||||
""" Convert an image asset to a C source file
|
||||
|
||||
@args
|
||||
> asset (_VxAsset) - minimal asset information
|
||||
> prefix_output (str) - prefix for source file generation
|
||||
> force_generate (bool) - force generate the source file
|
||||
|
||||
@return
|
||||
> pathname of the generated file
|
||||
"""
|
||||
# check critical requirement
|
||||
if 'profile' not in asset.meta:
|
||||
log.error(f"[{asset.name}] missing profile information!")
|
||||
return ''
|
||||
|
||||
# check if the file already exists
|
||||
asset_src = f'{prefix_output}/{asset.name}_vximage.c'
|
||||
if not force_generate and os.path.exists(asset_src):
|
||||
return asset_src
|
||||
|
||||
#generate the source file content
|
||||
if not (img_info := _image_convert(asset, asset.meta['profile'])):
|
||||
return ''
|
||||
content = _image_generate_source_file(asset, img_info)
|
||||
|
||||
# generate the source file
|
||||
with open(asset_src, "w", encoding='utf8') as file:
|
||||
file.write(content)
|
||||
return asset_src
|
|
@ -1,17 +0,0 @@
|
|||
"""
|
||||
core.project - project abstraction
|
||||
"""
|
||||
|
||||
from core.project.new import new_project
|
||||
|
||||
__all__ = [
|
||||
'new'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def new(projpath):
|
||||
""" create a new project from default template """
|
||||
return new_project(projpath)
|
|
@ -1,29 +0,0 @@
|
|||
"""
|
||||
core.project.new - create a new project from default template
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'new_project'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
# (todo/CDE6) : change internal project name
|
||||
def new_project(project_path):
|
||||
""" create a new project """
|
||||
if os.path.exists(project_path):
|
||||
log.warn(f"The path {project_path} already exists !")
|
||||
return True
|
||||
origin_path = os.path.dirname(__file__)
|
||||
shutil.copytree(
|
||||
origin_path + '/../../assets/project/',
|
||||
project_path
|
||||
)
|
||||
log.user(f"project '{project_path}' successfully created !")
|
||||
return False
|
Loading…
Reference in New Issue