mini-arcade-native-backend 0.4.3__tar.gz → 0.4.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/CHANGELOG.md +16 -0
  2. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/PKG-INFO +1 -1
  3. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/cpp/bindings.cpp +7 -1
  4. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/cpp/engine.cpp +24 -0
  5. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/cpp/engine.h +5 -0
  6. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/pyproject.toml +1 -1
  7. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/src/mini_arcade_native_backend/__init__.py +44 -5
  8. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/.github/workflows/ci.yml +0 -0
  9. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/.github/workflows/create-release-branch.yml +0 -0
  10. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/.github/workflows/release-finalize.yml +0 -0
  11. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/.github/workflows/release-publish.yml +0 -0
  12. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/.gitignore +0 -0
  13. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/.vscode/settings.json +0 -0
  14. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/CMakeLists.txt +0 -0
  15. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/LICENSE +0 -0
  16. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/README.md +0 -0
  17. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/examples/native_backend_demo.py +0 -0
  18. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/poetry.lock +0 -0
  19. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/poetry.toml +0 -0
  20. {mini_arcade_native_backend-0.4.3 → mini_arcade_native_backend-0.4.5}/tests/test_init.py +0 -0
@@ -6,6 +6,22 @@ This project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.5] - 2025-12-16
10
+
11
+ ### Added
12
+ - implement font caching and update draw_text and measure_text methods to support dynamic font sizes
13
+
14
+ ## [0.4.4] - 2025-12-16
15
+
16
+ ### Added
17
+ - add measure_text method to Engine and expose it in NativeBackend
18
+ - add alpha handling methods and refactor color extraction in NativeBackend
19
+ - update draw_rect and draw_text methods to support alpha channel in color
20
+
21
+ ### Other
22
+ - Merge branch 'release/0.4' of https://github.com/alexsc6955/mini-arcade-native-backend into release/0.4
23
+ - Merge branch 'release/0.4' of https://github.com/alexsc6955/mini-arcade-native-backend into release/0.4
24
+
9
25
  ## [0.4.3] - 2025-12-16
10
26
 
11
27
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mini-arcade-native-backend
3
- Version: 0.4.3
3
+ Version: 0.4.5
4
4
  Summary: Native SDL2 backend for mini-arcade-core using SDL2 + pybind11.
5
5
  Author-Email: Santiago Rincon <rincores@gmail.com>
6
6
  License: Copyright (c) 2025 Santiago Rincón
@@ -79,5 +79,11 @@ PYBIND11_MODULE(_native, m) {
79
79
  .def("poll_events", &mini::Engine::poll_events)
80
80
 
81
81
  .def("capture_frame", &mini::Engine::capture_frame,
82
- py::arg("path"));
82
+ py::arg("path"))
83
+ .def(
84
+ "measure_text",
85
+ &mini::Engine::measure_text,
86
+ py::arg("text"),
87
+ py::arg("font_id") = -1
88
+ );
83
89
  }
@@ -2,6 +2,8 @@
2
2
 
3
3
  #include <stdexcept>
4
4
  #include <iostream>
5
+ #include <utility>
6
+ #include <cstring>
5
7
 
6
8
  namespace mini {
7
9
 
@@ -381,4 +383,26 @@ namespace mini {
381
383
  return events;
382
384
  }
383
385
 
386
+ std::pair<int, int> Engine::measure_text(const char* text, int font_id)
387
+ {
388
+ if (!initialized_) return {0, 0};
389
+
390
+ int idx = (font_id >= 0) ? font_id : default_font_id_;
391
+ if (idx < 0 || idx >= (int)fonts_.size() || fonts_[idx] == nullptr) return {0, 0};
392
+
393
+ if (text == nullptr || text[0] == '\0') return {0, 0};
394
+
395
+ int w = 0;
396
+ int h = 0;
397
+
398
+ // TTF_SizeUTF8 returns 0 on success, -1 on error
399
+ if (TTF_SizeUTF8(fonts_[idx], text, &w, &h) != 0) {
400
+ // Optional: log error
401
+ // std::cerr << "TTF_SizeUTF8 Error: " << TTF_GetError() << std::endl;
402
+ return {0, 0};
403
+ }
404
+
405
+ return {w, h};
406
+ }
407
+
384
408
  } // namespace mini
@@ -4,6 +4,7 @@
4
4
  #include <SDL_ttf.h>
5
5
  #include <vector>
6
6
  #include <string>
7
+ #include <utility>
7
8
 
8
9
  // A minimal 2D graphics engine binding for Python using SDL.
9
10
  namespace mini {
@@ -89,6 +90,10 @@ namespace mini {
89
90
  // Returns true on success, false on failure.
90
91
  bool capture_frame(const char* path);
91
92
 
93
+ // Measure text (UTF-8) using a loaded font.
94
+ // Returns (width, height) in pixels. Returns (0,0) if no valid font or error.
95
+ std::pair<int, int> measure_text(const char* text, int font_id = -1);
96
+
92
97
  private:
93
98
  SDL_Window* window_; // The main application window.
94
99
  SDL_Renderer* renderer_; // The renderer for drawing.
@@ -8,7 +8,7 @@ build-backend = "scikit_build_core.build"
8
8
 
9
9
  [project]
10
10
  name = "mini-arcade-native-backend"
11
- version = "0.4.3"
11
+ version = "0.4.5"
12
12
  description = "Native SDL2 backend for mini-arcade-core using SDL2 + pybind11."
13
13
  authors = [
14
14
  { name = "Santiago Rincon", email = "rincores@gmail.com" },
@@ -80,6 +80,33 @@ class NativeBackend(Backend):
80
80
  self._font_path = font_path
81
81
  self._font_size = font_size
82
82
  self._default_font_id: int | None = None
83
+ self._fonts_by_size: dict[int, int] = {}
84
+
85
+ def _get_font_id(self, font_size: int | None) -> int:
86
+ # No font loaded -> keep current “no-op” behavior
87
+ if self._font_path is None:
88
+ return -1
89
+
90
+ # Default font
91
+ if font_size is None:
92
+ return (
93
+ self._default_font_id
94
+ if self._default_font_id is not None
95
+ else -1
96
+ )
97
+
98
+ if font_size <= 0:
99
+ raise ValueError(f"font_size must be > 0, got {font_size}")
100
+
101
+ # Cached
102
+ cached = self._fonts_by_size.get(font_size)
103
+ if cached is not None:
104
+ return cached
105
+
106
+ # Lazily load and cache
107
+ font_id = self._engine.load_font(self._font_path, int(font_size))
108
+ self._fonts_by_size[font_size] = font_id
109
+ return font_id
83
110
 
84
111
  def init(self, width: int, height: int, title: str):
85
112
  """
@@ -101,6 +128,7 @@ class NativeBackend(Backend):
101
128
  self._default_font_id = self._engine.load_font(
102
129
  self._font_path, self._font_size
103
130
  )
131
+ self._fonts_by_size[self._font_size] = self._default_font_id
104
132
 
105
133
  def set_clear_color(self, r: int, g: int, b: int):
106
134
  """
@@ -277,14 +305,13 @@ class NativeBackend(Backend):
277
305
  r, g, b, a = self._get_color_values(color)
278
306
  self._engine.draw_rect(x, y, w, h, r, g, b, a)
279
307
 
280
- # pylint: enable=too-many-arguments,too-many-positional-arguments
281
-
282
308
  def draw_text(
283
309
  self,
284
310
  x: int,
285
311
  y: int,
286
312
  text: str,
287
313
  color: tuple[int, int, int] = (255, 255, 255),
314
+ font_size: int | None = None,
288
315
  ):
289
316
  """
290
317
  Draw text at the given position using the loaded font.
@@ -303,13 +330,13 @@ class NativeBackend(Backend):
303
330
  :type color: tuple[int, int, int]
304
331
  """
305
332
  r, g, b, a = self._get_color_values(color)
306
- font_id = (
307
- self._default_font_id if self._default_font_id is not None else -1
308
- )
333
+ font_id = self._get_font_id(font_size)
309
334
  self._engine.draw_text(
310
335
  text, x, y, int(r), int(g), int(b), int(a), font_id
311
336
  )
312
337
 
338
+ # pylint: enable=too-many-arguments,too-many-positional-arguments
339
+
313
340
  def capture_frame(self, path: str | None = None) -> bool:
314
341
  """
315
342
  Capture the current frame.
@@ -324,3 +351,15 @@ class NativeBackend(Backend):
324
351
  if path is None:
325
352
  raise ValueError("Path must be provided to capture frame.")
326
353
  return self._engine.capture_frame(path)
354
+
355
+ def measure_text(
356
+ self, text: str, font_size: int | None = None
357
+ ) -> tuple[int, int]:
358
+ """
359
+ Measure text size (width, height) in pixels for the active font.
360
+
361
+ Returns (0,0) if no font is loaded (matches draw_text no-op behavior).
362
+ """
363
+ font_id = self._get_font_id(font_size)
364
+ w, h = self._engine.measure_text(text, font_id)
365
+ return int(w), int(h)