Files
StarPilot/openpilot/starpilot/ui/preview_typography.py
T
firestar5683 aaac7a6733 Shared settings and onroad views
Present saved configuration and live driving state through native Small and Big UI layouts. Support per-field tune resets, movable widgets, camera availability, and a unified speed card while preserving decision-bound actions.
2026-10-03 22:34:37 +00:00

80 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Explicit desktop typography preview; no manager, Params or device service access."""
import argparse
import hashlib
import json
from pathlib import Path
import pyray as rl
from openpilot.starpilot.ui.presentation import BitmapFonts, FontRole, Profile
def samples(profile: Profile) -> list[dict]:
if profile == Profile.LARGE:
return [
{"text": "StarPilot", "role": "brand", "size": 96, "x": 40, "y": 25},
{"text": "StarPilot - Baseline model", "role": "normal", "size": 48, "x": 40, "y": 180},
{"text": "CONDITIONAL EXPERIMENTAL", "role": "normal", "size": 45, "x": 40, "y": 290},
{"text": "ALL TIME PAST WEEK PERSONAL RECORDS", "role": "semi_bold", "size": 40, "x": 40, "y": 390},
{"text": "TAKE CONTROL IMMEDIATELY", "role": "bold", "size": 76, "x": 40, "y": 500},
{"text": "45 mph 120 kilometers 0.0 miles", "role": "medium", "size": 58, "x": 40, "y": 650},
{"text": "ABC xyz 0123456789 – ° ✓", "role": "roman", "size": 44, "x": 40, "y": 790},
{"text": "ABC xyz 0123456789", "role": "fallback", "size": 36, "x": 40, "y": 900},
]
return [
{"text": "StarPilot", "role": "brand", "size": 96, "x": 6, "y": -16},
{"text": "6.7.7 Sep 15 Baseline model", "role": "roman", "size": 36, "x": 6, "y": 96},
{"text": "678af78", "role": "normal", "size": 36, "x": 6, "y": 138},
{"text": "45 mph 50 MAX", "role": "bold", "size": 32, "x": 6, "y": 185},
]
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--profile", choices=list(Profile), required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--font-directory", type=Path, required=True)
args = parser.parse_args()
profile = Profile(args.profile)
args.output.mkdir(parents=True, exist_ok=True)
width, height = profile.size
rl.set_config_flags(rl.ConfigFlags.FLAG_WINDOW_HIDDEN | rl.ConfigFlags.FLAG_MSAA_4X_HINT)
# The framebuffer is independent of the desktop window; a small hidden context
# also works when the logical device resolution exceeds the desktop monitor.
context_size = min(width, 960), min(height, 540)
rl.init_window(*context_size, "Typography preview")
if not rl.is_window_ready():
raise RuntimeError("Unable to create native graphics context")
texture = rl.load_render_texture(width, height)
try:
with BitmapFonts(profile, args.font_directory) as fonts:
measured = []
rl.begin_texture_mode(texture)
rl.clear_background(rl.BLACK)
for sample in samples(profile):
size = fonts.measure(sample["text"], FontRole(sample["role"]), sample["size"])
measured.append({**sample, "width": size.width, "height": size.height})
fonts.draw(sample["text"], FontRole(sample["role"]), sample["size"], sample["x"], sample["y"])
rl.end_texture_mode()
pixels = rl.load_image_from_texture(texture.texture)
try:
rl.image_format(pixels, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
rl.image_flip_vertical(pixels)
image_path = args.output / f"{profile}.png"
if not rl.export_image(pixels, str(image_path)):
raise RuntimeError("Unable to export native framebuffer")
pixel_hash = hashlib.sha256(bytes(rl.ffi.buffer(pixels.data, width * height * 4))).hexdigest()
finally:
rl.unload_image(pixels)
report = {"profile": profile, "dimensions": [width, height], "context_dimensions": list(context_size), "rgba_sha256": pixel_hash,
"measurements": measured, "scope": "native desktop typography only; device rendering and full UI not qualified"}
(args.output / f"{profile}.json").write_text(json.dumps(report, indent=2) + "\n")
finally:
rl.unload_render_texture(texture)
rl.close_window()
if __name__ == "__main__":
main()