diff --git a/openpilot/selfdrive/ui/sunnypilot/onroad/speed_renderer.py b/openpilot/selfdrive/ui/sunnypilot/onroad/speed_renderer.py index 0a017876e..ce78a0cc1 100644 --- a/openpilot/selfdrive/ui/sunnypilot/onroad/speed_renderer.py +++ b/openpilot/selfdrive/ui/sunnypilot/onroad/speed_renderer.py @@ -8,7 +8,7 @@ import pyray as rl from openpilot.common.constants import CV from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.selfdrive.ui.onroad.hud_renderer import FONT_SIZES, COLORS @@ -19,8 +19,7 @@ class SpeedRenderer: self.speed: float = 0.0 self.v_ego_cluster_seen: bool = False - self._font_bold: rl.Font = gui_app.font(FontWeight.BOLD) - self._font_medium: rl.Font = gui_app.font(FontWeight.MEDIUM) + self._font_speed: rl.Font = gui_app.high_res_speed_font() def update(self) -> None: car_state = ui_state.sm['carState'] @@ -36,11 +35,11 @@ class SpeedRenderer: # Draw current speed and unit speed_text = str(round(self.speed)) - speed_text_size = measure_text_cached(self._font_bold, speed_text, FONT_SIZES.current_speed) + speed_text_size = measure_text_cached(self._font_speed, speed_text, FONT_SIZES.current_speed) speed_pos = rl.Vector2(rect.x + rect.width / 2 - speed_text_size.x / 2, 180 - speed_text_size.y / 2) - rl.draw_text_ex(self._font_bold, speed_text, speed_pos, FONT_SIZES.current_speed, 0, COLORS.WHITE) + rl.draw_text_ex(self._font_speed, speed_text, speed_pos, FONT_SIZES.current_speed, 0, COLORS.WHITE) unit_text = tr("km/h") if ui_state.is_metric else tr("mph") - unit_text_size = measure_text_cached(self._font_medium, unit_text, FONT_SIZES.speed_unit) + unit_text_size = measure_text_cached(self._font_speed, unit_text, FONT_SIZES.speed_unit) unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2) - rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.WHITE_TRANSLUCENT) + rl.draw_text_ex(self._font_speed, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.WHITE_TRANSLUCENT) diff --git a/openpilot/system/ui/lib/application.py b/openpilot/system/ui/lib/application.py index 487590c6e..32c052ce8 100644 --- a/openpilot/system/ui/lib/application.py +++ b/openpilot/system/ui/lib/application.py @@ -102,6 +102,8 @@ NOTO_FONTS = { "zh-CHS": "SourceHanSansSC-Bold.otf", "zh-CHT": "NotoSansCJKtc-Regular.otf", } +HIGH_RES_SPEED_CHARS = "0123456789.-/ kmhp" +HIGH_RES_SPEED_FONT_SIZE = 280 class FontWeight(StrEnum): @@ -132,7 +134,7 @@ class TextAlignmentVertical(IntEnum): def font_fallback(font: rl.Font) -> rl.Font: """Use a Noto fallback for languages not covered by Inter.""" - if multilang.requires_font_fallback(): + if multilang.requires_font_fallback() and not gui_app.is_high_res_speed_font(font): return gui_app.fallback_font() return font @@ -222,6 +224,8 @@ class GuiApplication(GuiApplicationExt): self._fonts: dict[FontWeight, rl.Font] = {} self._fallback_fonts: dict[str, rl.Font] = {} + self._high_res_speed_fonts: dict[str, rl.Font] = {} + self._high_res_speed_font_ids: set[int] = set() self._width = width if width is not None else GuiApplication._default_width() self._height = height if height is not None else GuiApplication._default_height() @@ -746,6 +750,28 @@ class GuiApplication(GuiApplicationExt): self._fallback_fonts[language] = font return self._fallback_fonts[language] + def high_res_speed_font(self) -> rl.Font: + """Font subset rasterized near the size used by the onroad speed display.""" + if not multilang.requires_font_fallback(): + return self._fonts[FontWeight.BOLD] + + language = multilang.language + if language not in self._high_res_speed_fonts: + codepoints = sorted(map(ord, HIGH_RES_SPEED_CHARS)) + codepoint_buffer = rl.ffi.new("int[]", codepoints) + with as_file(FONT_DIR) as fspath: + font = rl.load_font_ex((fspath / NOTO_FONTS[language]).as_posix(), HIGH_RES_SPEED_FONT_SIZE, + rl.ffi.cast("int *", codepoint_buffer), len(codepoints)) + rl.gen_texture_mipmaps(font.texture) + rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_TRILINEAR) + self._high_res_speed_fonts[language] = font + if font.texture.id != 0: + self._high_res_speed_font_ids.add(font.texture.id) + return self._high_res_speed_fonts[language] + + def is_high_res_speed_font(self, font: rl.Font) -> bool: + return font.texture.id in self._high_res_speed_font_ids + @property def width(self): return self._width