mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-01 11:53:46 +08:00
cache it
This commit is contained in:
@@ -38,6 +38,37 @@ UNIFIED_ACCENT = rl.Color(160, 96, 230, 230)
|
||||
OFFSET_COLOR = rl.Color(UNIFIED_ACCENT.r, UNIFIED_ACCENT.g, UNIFIED_ACCENT.b, 255)
|
||||
|
||||
|
||||
def _draw_header_icon(icon_key: str, x: float, y: float) -> None:
|
||||
scale = max(1.0, gui_app._scale * max(gui_app._pixel_scale_x, gui_app._pixel_scale_y))
|
||||
# Supersample for smooth edges.
|
||||
texture_size = math.ceil(2 * HEADER_ICON_SIZE * scale)
|
||||
|
||||
def render() -> None:
|
||||
rl.rl_push_matrix()
|
||||
try:
|
||||
texture_scale = texture_size / HEADER_ICON_SIZE
|
||||
rl.rl_scalef(texture_scale, texture_scale, 1.0)
|
||||
_draw_source_icon(icon_key, 0, 0, HEADER_ICON_SIZE, rl.WHITE)
|
||||
finally:
|
||||
rl.rl_pop_matrix()
|
||||
|
||||
texture = gui_app.cached_render_texture(
|
||||
f"unified-speed-header:{icon_key}:{texture_size}", texture_size, texture_size, render,
|
||||
)
|
||||
if texture is None:
|
||||
_draw_source_icon(icon_key, x, y, HEADER_ICON_SIZE, rl.WHITE)
|
||||
return
|
||||
|
||||
rl.begin_blend_mode(rl.BlendMode.BLEND_ALPHA_PREMULTIPLY)
|
||||
try:
|
||||
rl.draw_texture_pro(
|
||||
texture, rl.Rectangle(0, 0, texture_size, -texture_size),
|
||||
rl.Rectangle(x, y, HEADER_ICON_SIZE, HEADER_ICON_SIZE), rl.Vector2(0, 0), 0.0, rl.WHITE,
|
||||
)
|
||||
finally:
|
||||
rl.end_blend_mode()
|
||||
|
||||
|
||||
class UnifiedSpeedWidget(LayoutWidget):
|
||||
TOUCH_SLOP = 20
|
||||
|
||||
@@ -112,7 +143,7 @@ class UnifiedSpeedWidget(LayoutWidget):
|
||||
group_x = bounds.x + (bounds.width - group_width) / 2
|
||||
icon_y = bounds.y + 20
|
||||
if icon_key:
|
||||
_draw_source_icon(icon_key, group_x, icon_y, HEADER_ICON_SIZE, rl.WHITE)
|
||||
_draw_header_icon(icon_key, group_x, icon_y)
|
||||
rl.draw_text_ex(
|
||||
self._font_semi_bold, text,
|
||||
rl.Vector2(group_x + icon_width, icon_y + (HEADER_ICON_SIZE - text_size.y) / 2),
|
||||
|
||||
@@ -21,6 +21,74 @@ def make_widget(mode="split", pending=False):
|
||||
return widget
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def header_icon_cache(monkeypatch):
|
||||
app = object.__new__(type(unified_speed.gui_app))
|
||||
app._scale = app._pixel_scale_x = app._pixel_scale_y = 1.0
|
||||
app._cached_render_textures = {}
|
||||
app._pending_render_textures = {}
|
||||
geometry, draws, allocations, scales = [], [], [], []
|
||||
monkeypatch.setattr(unified_speed, "gui_app", app)
|
||||
monkeypatch.setattr(unified_speed, "_draw_source_icon", lambda *args: geometry.append(args))
|
||||
monkeypatch.setattr(unified_speed, "measure_text_cached", lambda *args: rl.Vector2(100, 28))
|
||||
monkeypatch.setattr(rl, "draw_text_ex", lambda *args: None)
|
||||
monkeypatch.setattr(rl, "draw_texture_pro", lambda *args: draws.append(args))
|
||||
monkeypatch.setattr(rl, "rl_scalef", lambda *args: scales.append(args))
|
||||
for name in ("rl_push_matrix", "rl_pop_matrix", "begin_texture_mode", "end_texture_mode", "clear_background",
|
||||
"rl_set_blend_factors_separate", "begin_blend_mode", "end_blend_mode", "set_texture_filter", "set_texture_wrap"):
|
||||
monkeypatch.setattr(rl, name, lambda *args: None)
|
||||
|
||||
def allocate(width, height):
|
||||
allocations.append((width, height))
|
||||
return SimpleNamespace(texture=SimpleNamespace(width=width, height=height))
|
||||
|
||||
monkeypatch.setattr(rl, "load_render_texture", allocate)
|
||||
return app, geometry, draws, allocations, scales
|
||||
|
||||
|
||||
def test_header_glyph_cache_is_shared_and_skips_geometry_after_first_frame(header_icon_cache):
|
||||
app, geometry, draws, allocations, _scales = header_icon_cache
|
||||
widgets = [make_widget(), make_widget()]
|
||||
for widget in widgets:
|
||||
widget._font_semi_bold = None
|
||||
widget = widgets[0]
|
||||
for label, icon in (("MAX SET", "speedometer"), ("SPEED LIMIT", "map")):
|
||||
widget._draw_header(widget.rect, label, icon, rl.WHITE)
|
||||
assert len(geometry) == 2
|
||||
assert allocations == []
|
||||
app._populate_render_texture_cache()
|
||||
assert len(geometry) == 4
|
||||
|
||||
for frame in range(60):
|
||||
widget = widgets[frame % 2]
|
||||
bounds = rl.Rectangle(frame, frame, 260, 250)
|
||||
widget._draw_header(bounds, "MAX SET", "speedometer", rl.WHITE)
|
||||
widget._draw_header(bounds, f"LIMIT {frame}", "map", rl.GRAY)
|
||||
assert len(geometry) == 4
|
||||
assert len(draws) == 120
|
||||
assert len(allocations) == len(app._cached_render_textures) == 2
|
||||
assert app._pending_render_textures == {}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("scale,dpi,texture_size", [(0.5, 1.0, 68), (1.0, 2.0, 136), (1.25, 1.5, 128)])
|
||||
def test_header_cache_resolution_preserves_logical_geometry(header_icon_cache, scale, dpi, texture_size):
|
||||
app, geometry, draws, allocations, scales = header_icon_cache
|
||||
app._scale, app._pixel_scale_x = scale, dpi
|
||||
for icon in ("speedometer", "map", "camera", "dashboard", "next"):
|
||||
unified_speed._draw_header_icon(icon, 10, 20)
|
||||
app._populate_render_texture_cache()
|
||||
assert len(app._cached_render_textures) == 5
|
||||
assert allocations == [(texture_size, texture_size)] * 5
|
||||
assert all(args[1:4] == (0, 0, 34) for args in geometry[5:])
|
||||
assert scales == [(texture_size / 34, texture_size / 34, 1.0)] * 5
|
||||
|
||||
unified_speed._draw_header_icon("map", 200, 300)
|
||||
assert len(geometry) == 10
|
||||
source, destination = draws[-1][1:3]
|
||||
assert (source.width, source.height) == (texture_size, -texture_size)
|
||||
assert (destination.x, destination.y, destination.width, destination.height) == (200, 300, 34, 34)
|
||||
|
||||
|
||||
def test_speed_limit_hit_target_is_right_half_in_both_layouts():
|
||||
for mode in ("split", "merged"):
|
||||
right = make_widget(mode)._speed_limit_bounds(rl.Rectangle(30, 75, 520, 250))
|
||||
|
||||
Reference in New Issue
Block a user