mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-07 21:13:41 +08:00
a82935f326
Integrate onroad views, configurable widgets, local settings, steering-profile contracts, Bluetooth and Android Auto with their device and runtime services. Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Daniel Koepping <elkoled@gmail.com> Co-authored-by: Danny <dannyirwin31@yahoo.com> Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com> Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> Co-authored-by: Shane Smiskol <shane@smiskol.com> Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Co-authored-by: stef <19478336+stefpi@users.noreply.github.com> Co-authored-by: whoisdomi <dcantu83@gmail.com> Co-authored-by: Intelli <contact@intelli.software> Co-authored-by: pharmacomaniac <blittle65@gmail.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
114 lines
4.9 KiB
Python
114 lines
4.9 KiB
Python
"""Bounded native resource ownership for the Settings development presentation."""
|
|
|
|
import hashlib
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
|
|
import pyray as rl
|
|
|
|
from openpilot.starpilot.ui.settings_icons import draw_icon_geometry
|
|
|
|
|
|
class SettingsAssets:
|
|
def __init__(self, directory: Path):
|
|
self.directory = directory
|
|
manifest = json.loads(Path(__file__).with_name("settings-assets.json").read_text())
|
|
self._manifest = {entry["file"]: entry for entry in manifest["files"]}
|
|
self._images: dict[tuple, rl.Texture] = {}
|
|
self._icons: dict[tuple, rl.RenderTexture] = {}
|
|
self._pixel_scale = (max(1, rl.get_render_width() / max(1, rl.get_screen_width())),
|
|
max(1, rl.get_render_height() / max(1, rl.get_screen_height())))
|
|
|
|
def image(self, filename: str, width: int, height: int) -> rl.Texture:
|
|
key = filename, width, height
|
|
if key in self._images:
|
|
return self._images[key]
|
|
if filename not in self._manifest or not 0 < width <= 4096 or not 0 < height <= 4096:
|
|
raise ValueError("Unreviewed Settings image or dimensions")
|
|
record = self._manifest[filename]
|
|
path = self.directory / filename
|
|
with path.open("rb") as stream:
|
|
data = stream.read(record["bytes"] + 1)
|
|
if len(data) != record["bytes"] or hashlib.sha256(data).hexdigest() != record["sha256"]:
|
|
raise ValueError(f"Settings image differs from the reviewed manifest: {filename}")
|
|
image = rl.load_image(str(path))
|
|
texture = None
|
|
try:
|
|
if image.data == rl.ffi.NULL or not 0 < image.width <= 4096 or not 0 < image.height <= 4096:
|
|
raise ValueError(f"Invalid Settings image: {filename}")
|
|
logical_ratio = min(width / image.width, height / image.height)
|
|
logical_width, logical_height = round(image.width * logical_ratio), round(image.height * logical_ratio)
|
|
ratio = min(min(int(width * self._pixel_scale[0]), image.width) / image.width,
|
|
min(int(height * self._pixel_scale[1]), image.height) / image.height)
|
|
if ratio != 1:
|
|
rl.image_resize(image, int(image.width * ratio), int(image.height * ratio))
|
|
texture = rl.load_texture_from_image(image)
|
|
if texture.id == 0:
|
|
raise RuntimeError(f"Unable to load Settings texture: {filename}")
|
|
rl.set_texture_filter(texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR)
|
|
rl.set_texture_wrap(texture, rl.TextureWrap.TEXTURE_WRAP_CLAMP)
|
|
texture.width, texture.height = logical_width, logical_height
|
|
self._images[key] = texture
|
|
except BaseException:
|
|
if texture is not None and texture.id:
|
|
rl.unload_texture(texture)
|
|
raise
|
|
finally:
|
|
if image.data != rl.ffi.NULL:
|
|
rl.unload_image(image)
|
|
return texture
|
|
|
|
def prepare_icon(self, name: str, scale: float, color: rl.Color) -> None:
|
|
"""Create vector cache between frames; render-texture modes cannot nest."""
|
|
if name not in ("sound", "aicar", "steering", "system", "display", "vehicle") or not 0 < scale <= 3:
|
|
raise ValueError("Unreviewed Settings vector icon")
|
|
key = name, scale, color.r, color.g, color.b, color.a
|
|
if key in self._icons:
|
|
return
|
|
padding = max(1, math.ceil(3 * scale))
|
|
size = max(1, round(60 * scale)) + 2 * padding
|
|
target = rl.load_render_texture(size, size)
|
|
try:
|
|
if not target.id or not target.texture.id:
|
|
raise RuntimeError("Unable to create Settings vector cache")
|
|
rl.begin_texture_mode(target)
|
|
try:
|
|
rl.clear_background(rl.BLANK)
|
|
rl.rl_set_blend_factors_separate(rl.RL_SRC_ALPHA, rl.RL_ONE_MINUS_SRC_ALPHA,
|
|
rl.RL_ONE, rl.RL_ONE_MINUS_SRC_ALPHA, rl.RL_FUNC_ADD, rl.RL_FUNC_ADD)
|
|
rl.begin_blend_mode(rl.BlendMode.BLEND_CUSTOM_SEPARATE)
|
|
try:
|
|
draw_icon_geometry(name, padding, padding, scale, color)
|
|
finally:
|
|
rl.end_blend_mode()
|
|
finally:
|
|
rl.end_texture_mode()
|
|
rl.set_texture_filter(target.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR)
|
|
rl.set_texture_wrap(target.texture, rl.TextureWrap.TEXTURE_WRAP_CLAMP)
|
|
except BaseException:
|
|
if target.id:
|
|
rl.unload_render_texture(target)
|
|
raise
|
|
self._icons[key] = target
|
|
|
|
def icon(self, name: str, x: float, y: float, scale: float, color: rl.Color) -> None:
|
|
target = self._icons[(name, scale, color.r, color.g, color.b, color.a)]
|
|
padding = max(1, math.ceil(3 * scale))
|
|
width, height = target.texture.width, target.texture.height
|
|
rl.begin_blend_mode(rl.BlendMode.BLEND_ALPHA_PREMULTIPLY)
|
|
try:
|
|
rl.draw_texture_pro(target.texture, rl.Rectangle(0, 0, width, -height),
|
|
rl.Rectangle(x - padding, y - padding, width, height), rl.Vector2(0, 0), 0, rl.WHITE)
|
|
finally:
|
|
rl.end_blend_mode()
|
|
|
|
def close(self) -> None:
|
|
if rl.is_window_ready():
|
|
for image in self._images.values():
|
|
rl.unload_texture(image)
|
|
for icon in self._icons.values():
|
|
rl.unload_render_texture(icon)
|
|
self._images.clear()
|
|
self._icons.clear()
|