Compare commits
2 Commits
eeeb0fa6b1
...
c79b3b1a9d
Author | SHA1 | Date |
---|---|---|
Lephenixnoir | c79b3b1a9d | |
Lephenixnoir | b86b96aa4a |
|
@ -15,17 +15,18 @@ 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 image format
|
||||
-f, --font Convert to gint's font 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 bopti image format
|
||||
-f, --font Convert to gint's topti font format
|
||||
--libimg-image Convert to the libimg image 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 -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:
|
||||
The operating mode options 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
|
||||
|
@ -36,11 +37,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("warning:", msg, file=sys.stderr)
|
||||
print("\x1b[33;1mwarning:\x1b[0m", msg, file=sys.stderr)
|
||||
|
||||
def main():
|
||||
# Default execution mode is to run a Python script for conversion
|
||||
modes = "script binary image font"
|
||||
modes = "script binary image font bopti-image libimg-image"
|
||||
mode = "s"
|
||||
output = None
|
||||
model = None
|
||||
|
@ -79,7 +80,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 == []:
|
||||
|
@ -118,7 +119,17 @@ def main():
|
|||
for (name, value) in args:
|
||||
insert(params, name.split("."), value)
|
||||
|
||||
params["type"] = { "b": "binary", "i": "image", "f": "font" }[mode]
|
||||
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"
|
||||
|
||||
try:
|
||||
fxconv.convert(input, params, target, output, model)
|
||||
|
|
132
fxconv/fxconv.py
132
fxconv/fxconv.py
|
@ -503,6 +503,42 @@ 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
|
||||
#
|
||||
|
@ -575,7 +611,7 @@ def quantize(img, dither=False):
|
|||
|
||||
return img
|
||||
|
||||
def r5g6b5(img, color_count=0):
|
||||
def r5g6b5(img, color_count=0, alpha=None):
|
||||
"""
|
||||
Convert a PIL.Image.Image into an R5G6B5 byte stream. If there are
|
||||
transparent pixels, chooses a color to implement alpha and replaces them
|
||||
|
@ -588,6 +624,10 @@ def r5g6b5(img, color_count=0):
|
|||
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):
|
||||
|
@ -601,6 +641,7 @@ def r5g6b5(img, color_count=0):
|
|||
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()
|
||||
|
@ -622,7 +663,10 @@ def r5g6b5(img, color_count=0):
|
|||
|
||||
# Choose an alpha color
|
||||
|
||||
if color_count > 0:
|
||||
if alpha is not None:
|
||||
alpha, replacement = alpha
|
||||
|
||||
elif 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
|
||||
|
@ -646,6 +690,15 @@ def r5g6b5(img, color_count=0):
|
|||
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
|
||||
|
@ -669,13 +722,13 @@ def r5g6b5(img, color_count=0):
|
|||
a = alpha_pixels[x, y] if has_alpha else 0xff
|
||||
|
||||
if not color_count:
|
||||
c = rgb24to16(*pixels[x, y]) if a > 0 else alpha
|
||||
c = alpha_encoding(rgb24to16(*pixels[x, y]), a)
|
||||
encoded[offset] = c >> 8
|
||||
encoded[offset+1] = c & 0xff
|
||||
offset += 2
|
||||
|
||||
elif color_count == 16:
|
||||
c = pixels[x, y] if a > 0 else alpha
|
||||
c = alpha_encoding(pixels[x, y], a)
|
||||
|
||||
# Aligned pixels: left 4 bits = high 4 bits of current byte
|
||||
if (entries % 2) == 0:
|
||||
|
@ -686,7 +739,7 @@ def r5g6b5(img, color_count=0):
|
|||
offset += 1
|
||||
|
||||
elif color_count == 256:
|
||||
c = pixels[x, y] if a > 0 else alpha
|
||||
c = alpha_encoding(pixels[x, y], a)
|
||||
encoded[offset] = c
|
||||
offset += 1
|
||||
|
||||
|
@ -739,14 +792,21 @@ 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"] == "image" and model in [ "fx", None ]:
|
||||
elif params["type"] == "bopti-image" and model in [ "fx", None ]:
|
||||
convert_bopti_fx(input, output, params, target)
|
||||
elif params["type"] == "image" and model == "cg":
|
||||
elif params["type"] == "bopti-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):
|
||||
def elf(data, output, symbol, toolchain=None, arch=None, section=None,
|
||||
assembly=None):
|
||||
"""
|
||||
Call objcopy to create an object file from the specified data. The object
|
||||
file will export three symbols:
|
||||
|
@ -773,6 +833,10 @@ 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
|
||||
|
@ -780,6 +844,7 @@ 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.
|
||||
"""
|
||||
|
@ -802,21 +867,42 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None):
|
|||
raise FxconvError(f"non-trivial architecture for {toolchain} must be "+
|
||||
"specified")
|
||||
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
fp.write(data)
|
||||
fp.flush()
|
||||
fp_obj = tempfile.NamedTemporaryFile()
|
||||
fp_obj.write(data)
|
||||
fp_obj.flush()
|
||||
|
||||
sybl = "_binary_" + fp.name.replace("/", "_")
|
||||
if assembly is not None:
|
||||
fp_asm = tempfile.NamedTemporaryFile()
|
||||
fp_asm.write(assembly.encode('utf-8'))
|
||||
fp_asm.flush()
|
||||
|
||||
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)
|
||||
proc = subprocess.run([
|
||||
f"{toolchain}-as", "-c", fp_asm.name, "-o", fp_asm.name + ".o" ])
|
||||
if proc.returncode != 0:
|
||||
raise FxconvError(f"objcopy returned {proc.returncode}")
|
||||
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()
|
||||
|
|
|
@ -136,10 +136,10 @@ build-cg/%.S.o: %.S
|
|||
# Images
|
||||
build-fx/assets/img/%.o: assets-fx/img/%
|
||||
@ mkdir -p $(dir $@)
|
||||
fxconv -i $< -o $@ $(FXCONVFX) name:img_$(basename $*) $(IMG.$*)
|
||||
fxconv --bopti-image $< -o $@ $(FXCONVFX) name:img_$(basename $*) $(IMG.$*)
|
||||
build-cg/assets/img/%.o: assets-cg/img/%
|
||||
@ mkdir -p $(dir $@)
|
||||
fxconv -i $< -o $@ $(FXCONVCG) name:img_$(basename $*) $(IMG.$*)
|
||||
fxconv --bopti-image $< -o $@ $(FXCONVCG) name:img_$(basename $*) $(IMG.$*)
|
||||
|
||||
# Fonts
|
||||
build-fx/assets/fonts/%.o: assets-fx/fonts/%
|
||||
|
|
|
@ -191,11 +191,12 @@ 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 :=
|
||||
LIBS_FX :=
|
||||
LIBS_CG :=
|
||||
|
||||
# Base linker flags for the fxSDK, you usually want to keep these.
|
||||
LDFLAGS_FX := -T fx9860g.ld -lgint-fx \$(LIBS) -lgint-fx -lgcc
|
||||
LDFLAGS_CG := -T fxcg50.ld -lgint-cg \$(LIBS) -lgint-cg -lgcc
|
||||
LDFLAGS_FX := -T fx9860g.ld -lgint-fx \$(LIBS_FX) -lgint-fx -lgcc
|
||||
LDFLAGS_CG := -T fxcg50.ld -lgint-cg \$(LIBS_CG) -lgint-cg -lgcc
|
||||
|
||||
# Additional linker flags, if you need any.
|
||||
LDFLAGS :=
|
||||
|
|
Loading…
Reference in New Issue