mirror of
https://github.com/PodeCaradox/GalaxyQuest-SteamFrame.git
synced 2026-10-20 05:01:16 +02:00
A native port built on the Petari decompilation (previous commit): an HLE platform layer (OS, DVD, GX, audio, input, NAND) under the game's own code, an OpenXR presentation (a diorama around the player, a giant screen, the HUD and settings as compositor layers, SpaceWarp, dynamic resolution), Touch Plus controls, the tools that convert your own game files (tools/cook, which also takes the few pieces of data the game's executable holds from your sys/main.dol), and the port's changes to the decompilation (`git diff petari-base -- decomp`). README.md explains installing and building, docs/TECHNICAL.md how it works. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
4.4 KiB
Python
112 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
||
"""Bakes the glyph atlas the VR layer's own panels draw text with
|
||
(platform/src/xr/ui_font.h).
|
||
|
||
make_ui_font.py <font.ttf> <output.h>
|
||
|
||
Needs Pillow. The port uses Inter Bold (SIL Open Font License 1.1), e.g.
|
||
from tools/ref/dusklight/res/Inter-Bold.ttf. Printable ASCII and a few
|
||
symbols are rendered at each size in SIZES as 4-bit coverage, packed into one
|
||
atlas per size and stored base64-encoded.
|
||
"""
|
||
import base64
|
||
import sys
|
||
|
||
from PIL import Image, ImageDraw, ImageFont
|
||
|
||
SIZES = (40, 28)
|
||
CHARS = [chr(c) for c in range(32, 127)] + ['−', '·'] # minus sign, middle dot
|
||
ATLAS_W = 512
|
||
|
||
|
||
def bake(path, size):
|
||
font = ImageFont.truetype(path, size)
|
||
ascent, descent = font.getmetrics()
|
||
glyphs = []
|
||
for ch in CHARS:
|
||
left, top, right, bottom = font.getbbox(ch, anchor='ls')
|
||
w, h = max(0, right - left), max(0, bottom - top)
|
||
img = Image.new('L', (max(w, 1), max(h, 1)), 0)
|
||
if w and h:
|
||
ImageDraw.Draw(img).text((-left, -top), ch, font=font, fill=255, anchor='ls')
|
||
advance = int(round(font.getlength(ch) * 4))
|
||
glyphs.append({'code': ord(ch), 'img': img, 'w': w, 'h': h, 'left': left, 'top': -top, 'advance': advance})
|
||
# Shelf packing, tallest first, a pixel of space around each glyph.
|
||
x = y = shelf = 1
|
||
for g in sorted(glyphs, key=lambda g: -g['h']):
|
||
if x + g['w'] + 1 > ATLAS_W:
|
||
x, y, shelf = 1, y + shelf + 1, 1
|
||
g['x'], g['y'] = x, y
|
||
x += g['w'] + 1
|
||
shelf = max(shelf, g['h'])
|
||
height = y + shelf + 1
|
||
atlas = Image.new('L', (ATLAS_W, height), 0)
|
||
for g in glyphs:
|
||
if g['w'] and g['h']:
|
||
atlas.paste(g['img'], (g['x'], g['y']))
|
||
px = atlas.tobytes()
|
||
packed = bytearray()
|
||
for i in range(0, len(px), 2):
|
||
hi = (px[i] * 15 + 127) // 255
|
||
lo = (px[i + 1] * 15 + 127) // 255 if i + 1 < len(px) else 0
|
||
packed.append(hi << 4 | lo)
|
||
return {'size': size, 'ascent': ascent, 'descent': descent, 'height': height, 'glyphs': glyphs, 'data': base64.b64encode(bytes(packed)).decode()}
|
||
|
||
|
||
def main():
|
||
if len(sys.argv) != 3:
|
||
sys.exit(__doc__)
|
||
faces = [bake(sys.argv[1], s) for s in SIZES]
|
||
out = []
|
||
out.append('// Generated by tools/make_ui_font.py from Inter Bold: do not edit.')
|
||
out.append('// Inter: Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter),')
|
||
out.append('// licensed under the SIL Open Font License, Version 1.1 (https://openfontlicense.org).')
|
||
out.append('#pragma once')
|
||
out.append('#include <stdint.h>')
|
||
out.append('')
|
||
out.append('namespace ui_font {')
|
||
out.append('')
|
||
out.append('// A glyph in its atlas; `left` and `top` place its box relative to the pen on')
|
||
out.append('// the baseline (top: pixels above it), advance is in quarter pixels.')
|
||
out.append('struct Glyph {')
|
||
out.append(' uint16_t code, x, y;')
|
||
out.append(' uint8_t w, h;')
|
||
out.append(' int8_t left, top;')
|
||
out.append(' uint16_t advance;')
|
||
out.append('};')
|
||
out.append('')
|
||
out.append('// Atlas: `width` x `height` 4-bit coverage values, two per byte (high')
|
||
out.append('// nibble first), base64-encoded.')
|
||
out.append('struct Face {')
|
||
out.append(' int size, ascent, descent, width, height;')
|
||
out.append(' const Glyph* glyphs;')
|
||
out.append(' int count;')
|
||
out.append(' const char* atlas;')
|
||
out.append('};')
|
||
for f in faces:
|
||
n = f['size']
|
||
out.append('')
|
||
out.append('static const Glyph kGlyphs%d[] = {' % n)
|
||
for g in f['glyphs']:
|
||
out.append(' {%d, %d, %d, %d, %d, %d, %d, %d},' % (g['code'], g['x'], g['y'], g['w'], g['h'], g['left'], g['top'], g['advance']))
|
||
out.append('};')
|
||
out.append('static const char kAtlas%d[] =' % n)
|
||
data = f['data']
|
||
for i in range(0, len(data), 120):
|
||
out.append(' "%s"' % data[i:i + 120])
|
||
out.append(' ;')
|
||
out.append('')
|
||
out.append('static const Face kFaces[] = {')
|
||
for f in faces:
|
||
n = f['size']
|
||
out.append(' {%d, %d, %d, %d, %d, kGlyphs%d, %d, kAtlas%d},' % (n, f['ascent'], f['descent'], ATLAS_W, f['height'], n, len(f['glyphs']), n))
|
||
out.append('};')
|
||
out.append('')
|
||
out.append('} // namespace ui_font')
|
||
with open(sys.argv[2], 'w', newline='\n') as fp:
|
||
fp.write('\n'.join(out) + '\n')
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|