Compare commits

..

No commits in common. "c79b3b1a9d5bf189ffa9a47a091651f3eec8dcff" and "eeeb0fa6b152853c0ee59551f5a151d2e7f63e9a" have entirely different histories.

4 changed files with 38 additions and 136 deletions

View File

@ -15,18 +15,17 @@ fxconv converts data files such as images and fonts into gint formats
optimized for fast execution, or into object files.
Operating modes:
-s, --script Expose the fxconv module and run this Python script
-b, --binary Turn data into an object file, no conversion
-i, --image Convert to gint's bopti image format
-f, --font Convert to gint's topti font format
--libimg-image Convert to the libimg image format
-s, --script Expose the fxconv module and run this Python script
-b, --binary Turn data into an object file, no conversion
-i, --image Convert to gint's image format
-f, --font Convert to gint's font format
When using -s, additional arguments are stored in the [fxconv.args] variable of
the module. This is intended to be a restricted list of file names specified by
a Makefile, used to convert only a subset of the files in the script.
The operating mode options are shortcuts to convert single files without a
script. They accept parameters with a "category.key:value" syntax, for example:
The -b, -i and -f modes are shortcuts to convert single files without a script.
They accept parameters with a "category.key:value" syntax, for example:
fxconv -f myfont.png -o myfont.o charset:ascii grid.padding:1 height:7
When converting images, use --fx (black-and-white calculators) or --cg (16-bit
@ -37,11 +36,11 @@ color calculators) to specify the target machine.
def err(msg):
print("\x1b[31;1merror:\x1b[0m", msg, file=sys.stderr)
def warn(msg):
print("\x1b[33;1mwarning:\x1b[0m", msg, file=sys.stderr)
print("warning:", msg, file=sys.stderr)
def main():
# Default execution mode is to run a Python script for conversion
modes = "script binary image font bopti-image libimg-image"
modes = "script binary image font"
mode = "s"
output = None
model = None
@ -80,7 +79,7 @@ def main():
target['section'] = value
# Other names are modes
else:
mode = name[1] if len(name)==2 else name[2:]
mode = name[1] if len(name)==2 else name[2]
# Remaining arguments
if args == []:
@ -119,17 +118,7 @@ def main():
for (name, value) in args:
insert(params, name.split("."), value)
if "type" in params:
pass
elif(len(mode) == 1):
params["type"] = { "b": "binary", "i": "image", "f": "font" }[mode]
else:
params["type"] = mode
# Will be deprecated in the future
if params["type"] == "image":
warn("type 'image' is deprecated, use 'bopti-image' instead")
params["type"] = "bopti-image"
params["type"] = { "b": "binary", "i": "image", "f": "font" }[mode]
try:
fxconv.convert(input, params, target, output, model)

View File

@ -503,42 +503,6 @@ def convert_topti(input, output, params, target):
elf(data, output, "_" + params["name"], **target)
#
# libimg conversion for fx-CG 50
#
def convert_libimg_cg(input, output, params, target):
img = Image.open(input)
if img.width >= 65536 or img.height >= 65536:
raise FxconvError(f"'{input}' is too large (max. 65535x65535)")
# Crop image to key "area"
area = Area(params.get("area", {}), img)
img = img.crop(area.tuple())
# Encode the image into 16-bit format and force the alpha to 0x0001
encoded, alpha = r5g6b5(img, alpha=(0x0001,0x0000))
w, h, s = img.width, img.height, img.width
FLAG_OWN = 1
FLAG_RO = 2
assembly = f"""
.section .rodata
.global _{params["name"]}
_{params["name"]}:
.word {img.width}
.word {img.height}
.word {img.width}
.byte {FLAG_RO}
.byte 0
.long _{params["name"]}_data
"""
dataname = "_{}_data".format(params["name"])
elf(encoded, output, dataname, assembly=assembly, **target)
#
# Exceptions
#
@ -611,7 +575,7 @@ def quantize(img, dither=False):
return img
def r5g6b5(img, color_count=0, alpha=None):
def r5g6b5(img, color_count=0):
"""
Convert a PIL.Image.Image into an R5G6B5 byte stream. If there are
transparent pixels, chooses a color to implement alpha and replaces them
@ -624,10 +588,6 @@ def r5g6b5(img, color_count=0, alpha=None):
encoded with a palette of this size. Returns the converted image as a
bytearray, the palette as a bytearray, and the alpha value (None if there
were no transparent pixels).
If alpha is provided, it should be a pair (alpha value, replacement).
Trandarpent pixels will be encoded with the specified alpha value and
pixels with the value will be encoded with the replacement.
"""
def rgb24to16(r, g, b):
@ -641,7 +601,6 @@ def r5g6b5(img, color_count=0, alpha=None):
alpha_channel = img.getchannel("A").convert("1", dither=Image.NONE)
alpha_levels = { t[1]: t[0] for t in alpha_channel.getcolors() }
has_alpha = 0 in alpha_levels
replacement = None
if has_alpha:
alpha_pixels = alpha_channel.load()
@ -663,10 +622,7 @@ def r5g6b5(img, color_count=0, alpha=None):
# Choose an alpha color
if alpha is not None:
alpha, replacement = alpha
elif color_count > 0:
if color_count > 0:
# Transparency is mapped to the last palette element, if there are no
# transparent pixels then select an index out of bounds.
alpha = color_count - 1 if has_alpha else 0xffff
@ -690,15 +646,6 @@ def r5g6b5(img, color_count=0, alpha=None):
else:
alpha = None
def alpha_encoding(color, a):
if a > 0:
if color == alpha:
return replacement
else:
return color
else:
return alpha
# Create a byte array with all encoded pixels
pixel_count = img.width * img.height
@ -722,13 +669,13 @@ def r5g6b5(img, color_count=0, alpha=None):
a = alpha_pixels[x, y] if has_alpha else 0xff
if not color_count:
c = alpha_encoding(rgb24to16(*pixels[x, y]), a)
c = rgb24to16(*pixels[x, y]) if a > 0 else alpha
encoded[offset] = c >> 8
encoded[offset+1] = c & 0xff
offset += 2
elif color_count == 16:
c = alpha_encoding(pixels[x, y], a)
c = pixels[x, y] if a > 0 else alpha
# Aligned pixels: left 4 bits = high 4 bits of current byte
if (entries % 2) == 0:
@ -739,7 +686,7 @@ def r5g6b5(img, color_count=0, alpha=None):
offset += 1
elif color_count == 256:
c = alpha_encoding(pixels[x, y], a)
c = pixels[x, y] if a > 0 else alpha
encoded[offset] = c
offset += 1
@ -792,21 +739,14 @@ def convert(input, params, target, output=None, model=None):
raise FxconvError(f"missing type in conversion '{input}'")
elif params["type"] == "binary":
convert_binary(input, output, params, target)
elif params["type"] == "bopti-image" and model in [ "fx", None ]:
elif params["type"] == "image" and model in [ "fx", None ]:
convert_bopti_fx(input, output, params, target)
elif params["type"] == "bopti-image" and model == "cg":
elif params["type"] == "image" and model == "cg":
convert_bopti_cg(input, output, params, target)
elif params["type"] == "font":
convert_topti(input, output, params, target)
elif params["type"] == "libimg-image" and model in [ "fx", None ]:
raise FxconvError(f"libimg not yet supported for fx-9860G o(x_x)o")
elif params["type"] == "libimg-image" and model == "cg":
convert_libimg_cg(input, output, params, target)
else:
raise FxconvError(f'unknown resource type \'{params["type"]}\'')
def elf(data, output, symbol, toolchain=None, arch=None, section=None,
assembly=None):
def elf(data, output, symbol, toolchain=None, arch=None, section=None):
"""
Call objcopy to create an object file from the specified data. The object
file will export three symbols:
@ -833,10 +773,6 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None,
would be section=".rodata,contents,alloc,load,readonly,data", which is the
default.
If assembly is set to a non-empty assembly program, this function also
generates a temporary ELF file by assembling this piece of code, and merges
it into the original one.
Arguments:
data -- A bytes-like object with data to embed into the object file
output -- Name of output file
@ -844,7 +780,6 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None,
toolchain -- Target triplet [default: "sh3eb-elf"]
arch -- Target architecture [default: try to guess]
section -- Target section [default: above variation of .rodata]
assembly -- Additional assembly code [default: None]
Produces an output file and returns nothing.
"""
@ -867,42 +802,21 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None,
raise FxconvError(f"non-trivial architecture for {toolchain} must be "+
"specified")
fp_obj = tempfile.NamedTemporaryFile()
fp_obj.write(data)
fp_obj.flush()
with tempfile.NamedTemporaryFile() as fp:
fp.write(data)
fp.flush()
if assembly is not None:
fp_asm = tempfile.NamedTemporaryFile()
fp_asm.write(assembly.encode('utf-8'))
fp_asm.flush()
sybl = "_binary_" + fp.name.replace("/", "_")
proc = subprocess.run([
f"{toolchain}-as", "-c", fp_asm.name, "-o", fp_asm.name + ".o" ])
objcopy_args = [
f"{toolchain}-objcopy", "-I", "binary", "-O", "elf32-sh",
"--binary-architecture", arch, "--file-alignment", "4",
"--rename-section", f".data={section}",
"--redefine-sym", f"{sybl}_start={symbol}",
"--redefine-sym", f"{sybl}_end={symbol}_end",
"--redefine-sym", f"{sybl}_size={symbol}_size",
fp.name, output ]
proc = subprocess.run(objcopy_args)
if proc.returncode != 0:
raise FxconvError(f"as returned {proc.returncode}")
sybl = "_binary_" + fp_obj.name.replace("/", "_")
objcopy_args = [
f"{toolchain}-objcopy", "-I", "binary", "-O", "elf32-sh",
"--binary-architecture", arch, "--file-alignment", "4",
"--rename-section", f".data={section}",
"--redefine-sym", f"{sybl}_start={symbol}",
"--redefine-sym", f"{sybl}_end={symbol}_end",
"--redefine-sym", f"{sybl}_size={symbol}_size",
fp_obj.name, output if assembly is None else fp_obj.name + "-tmp" ]
proc = subprocess.run(objcopy_args)
if proc.returncode != 0:
raise FxconvError(f"objcopy returned {proc.returncode}")
if assembly is not None:
proc = subprocess.run([
f"{toolchain}-ld", "-r", fp_obj.name + "-tmp", fp_asm.name + ".o",
"-o", output ])
if proc.returncode != 0:
raise FxconvError("ld returned {proc.returncode}")
fp_asm.close()
fp_obj.close()
raise FxconvError(f"objcopy returned {proc.returncode}")

View File

@ -136,10 +136,10 @@ build-cg/%.S.o: %.S
# Images
build-fx/assets/img/%.o: assets-fx/img/%
@ mkdir -p $(dir $@)
fxconv --bopti-image $< -o $@ $(FXCONVFX) name:img_$(basename $*) $(IMG.$*)
fxconv -i $< -o $@ $(FXCONVFX) name:img_$(basename $*) $(IMG.$*)
build-cg/assets/img/%.o: assets-cg/img/%
@ mkdir -p $(dir $@)
fxconv --bopti-image $< -o $@ $(FXCONVCG) name:img_$(basename $*) $(IMG.$*)
fxconv -i $< -o $@ $(FXCONVCG) name:img_$(basename $*) $(IMG.$*)
# Fonts
build-fx/assets/fonts/%.o: assets-fx/fonts/%

View File

@ -191,12 +191,11 @@ INCLUDE := -I include
# Libraries. Add one -l option for each library you are using, and also
# suitable -L options if you have library files in custom folders. To use
# fxlib, add libfx.a to the project directory and use "-L . -lfx".
LIBS_FX :=
LIBS_CG :=
LIBS :=
# Base linker flags for the fxSDK, you usually want to keep these.
LDFLAGS_FX := -T fx9860g.ld -lgint-fx \$(LIBS_FX) -lgint-fx -lgcc
LDFLAGS_CG := -T fxcg50.ld -lgint-cg \$(LIBS_CG) -lgint-cg -lgcc
LDFLAGS_FX := -T fx9860g.ld -lgint-fx \$(LIBS) -lgint-fx -lgcc
LDFLAGS_CG := -T fxcg50.ld -lgint-cg \$(LIBS) -lgint-cg -lgcc
# Additional linker flags, if you need any.
LDFLAGS :=