xbintsc 0.5.32 → 0.5.36

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 (173) hide show
  1. package/README.md +10 -7
  2. package/README.zh-CN.md +10 -8
  3. package/dist/scripts/check-version.d.ts +27 -0
  4. package/dist/scripts/check-version.js +71 -0
  5. package/dist/scripts/check-version.js.map +1 -0
  6. package/dist/scripts/package-platform.d.ts +58 -0
  7. package/dist/scripts/package-platform.js +142 -0
  8. package/dist/scripts/package-platform.js.map +1 -0
  9. package/dist/scripts/verify-platform-package.d.ts +34 -0
  10. package/dist/scripts/verify-platform-package.js +161 -0
  11. package/dist/scripts/verify-platform-package.js.map +1 -0
  12. package/dist/src/cli/help.d.ts +1 -1
  13. package/dist/src/cli/main.js +3 -1
  14. package/dist/src/cli/main.js.map +1 -1
  15. package/dist/src/driver/runtime-lib.d.ts +29 -4
  16. package/dist/src/driver/runtime-lib.js +68 -10
  17. package/dist/src/driver/runtime-lib.js.map +1 -1
  18. package/dist/src/driver/version.d.ts +1 -1
  19. package/dist/src/driver/version.js +1 -1
  20. package/dist/src/extensions/gui/index.js +6 -5
  21. package/dist/src/extensions/gui/index.js.map +1 -1
  22. package/dist/src/extensions/media/index.js +4 -3
  23. package/dist/src/extensions/media/index.js.map +1 -1
  24. package/dist/src/parser/context.d.ts +41 -0
  25. package/dist/src/parser/context.js +89 -4
  26. package/dist/src/parser/context.js.map +1 -1
  27. package/dist/src/parser/literals.js +6 -12
  28. package/dist/src/parser/literals.js.map +1 -1
  29. package/dist/tests/driver/platform-package.test.d.ts +1 -0
  30. package/dist/tests/driver/platform-package.test.js +49 -0
  31. package/dist/tests/driver/platform-package.test.js.map +1 -0
  32. package/dist/tests/driver/runtime-lib.test.js +51 -4
  33. package/dist/tests/driver/runtime-lib.test.js.map +1 -1
  34. package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
  35. package/dist/tests/e2e/gui-audio.test.js +287 -0
  36. package/dist/tests/e2e/gui-audio.test.js.map +1 -0
  37. package/dist/tests/e2e/gui-helpers.d.ts +7 -0
  38. package/dist/tests/e2e/gui-helpers.js +1 -0
  39. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  40. package/dist/tests/e2e/gui-media.test.js +78 -0
  41. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  42. package/dist/tests/e2e/gui-style.test.js +55 -0
  43. package/dist/tests/e2e/gui-style.test.js.map +1 -1
  44. package/dist/tests/e2e/gui-webgl.test.js +258 -0
  45. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  46. package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
  47. package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
  48. package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
  49. package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
  50. package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
  51. package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
  52. package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
  53. package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
  54. package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
  55. package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
  56. package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
  57. package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
  58. package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
  59. package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
  60. package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
  61. package/dist/tests/e2e/gui-webgl2.test.js +45 -1
  62. package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
  63. package/dist/tests/extensions/gui-index.test.js +1 -0
  64. package/dist/tests/extensions/gui-index.test.js.map +1 -1
  65. package/dist/tests/extensions/media.test.js +1 -0
  66. package/dist/tests/extensions/media.test.js.map +1 -1
  67. package/dist/tests/parser/expressions.test.js +30 -0
  68. package/dist/tests/parser/expressions.test.js.map +1 -1
  69. package/dist/tests/platform-tarball.test.d.ts +1 -0
  70. package/dist/tests/platform-tarball.test.js +111 -0
  71. package/dist/tests/platform-tarball.test.js.map +1 -0
  72. package/dist/tests/version.test.js +6 -0
  73. package/dist/tests/version.test.js.map +1 -1
  74. package/doc/ai/contributing.md +11 -0
  75. package/doc/ai/extensions.md +7 -4
  76. package/doc/ai/troubleshooting.md +7 -5
  77. package/doc/ai/zh-CN/contributing.md +9 -0
  78. package/doc/ai/zh-CN/extensions.md +4 -2
  79. package/doc/ai/zh-CN/troubleshooting.md +5 -3
  80. package/doc/gui/api.md +61 -13
  81. package/doc/gui/build.md +11 -4
  82. package/doc/gui/dom.md +6 -3
  83. package/doc/gui/media.md +4 -0
  84. package/doc/gui/webgl.md +105 -46
  85. package/doc/gui.md +7 -3
  86. package/doc/npm-package.md +55 -27
  87. package/doc/requirements.md +6 -2
  88. package/doc/zh-CN/gui/api.md +51 -10
  89. package/doc/zh-CN/gui/build.md +7 -2
  90. package/doc/zh-CN/gui/dom.md +3 -1
  91. package/doc/zh-CN/gui/media.md +3 -0
  92. package/doc/zh-CN/gui/webgl.md +22 -16
  93. package/doc/zh-CN/gui.md +3 -2
  94. package/doc/zh-CN/npm-package.md +44 -23
  95. package/doc/zh-CN/requirements.md +4 -1
  96. package/package.json +17 -1
  97. package/runtime/ext_gui/audio.cpp +365 -153
  98. package/runtime/ext_gui/audio_buffer.cpp +166 -0
  99. package/runtime/ext_gui/audio_buffer.h +37 -0
  100. package/runtime/ext_gui/audio_decode.cpp +219 -0
  101. package/runtime/ext_gui/audio_decode.h +39 -0
  102. package/runtime/ext_gui/audio_device.cpp +87 -0
  103. package/runtime/ext_gui/audio_effects.cpp +163 -0
  104. package/runtime/ext_gui/audio_effects.h +25 -0
  105. package/runtime/ext_gui/audio_internal.h +30 -0
  106. package/runtime/ext_gui/audio_mixer.cpp +494 -0
  107. package/runtime/ext_gui/audio_mixer.h +238 -0
  108. package/runtime/ext_gui/audio_param.cpp +104 -0
  109. package/runtime/ext_gui/audio_param_object.cpp +155 -0
  110. package/runtime/ext_gui/browser.cpp +11 -1
  111. package/runtime/ext_gui/css_selector.cpp +21 -3
  112. package/runtime/ext_gui/dom_api_internal.h +16 -0
  113. package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
  114. package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
  115. package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
  116. package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
  117. package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
  118. package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
  119. package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
  120. package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
  121. package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
  122. package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
  123. package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
  124. package/runtime/ext_gui/gui.cpp +44 -9
  125. package/runtime/ext_gui/gui_engine.h +9 -4
  126. package/runtime/ext_gui/image_bitmap.cpp +28 -0
  127. package/runtime/ext_gui/image_bitmap.h +26 -0
  128. package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
  129. package/runtime/ext_gui/media.cpp +32 -0
  130. package/runtime/ext_gui/media.h +13 -0
  131. package/runtime/ext_gui/media_api.h +23 -0
  132. package/runtime/ext_gui/webgl.h +152 -18
  133. package/runtime/ext_gui/webgl_blit.cpp +95 -0
  134. package/runtime/ext_gui/webgl_buffer.cpp +178 -263
  135. package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
  136. package/runtime/ext_gui/webgl_clear.cpp +142 -0
  137. package/runtime/ext_gui/webgl_compressed.cpp +180 -0
  138. package/runtime/ext_gui/webgl_context.cpp +241 -156
  139. package/runtime/ext_gui/webgl_draw.cpp +68 -3
  140. package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
  141. package/runtime/ext_gui/webgl_glsl.h +40 -0
  142. package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
  143. package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
  144. package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
  145. package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
  146. package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
  147. package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
  148. package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
  149. package/runtime/ext_gui/webgl_program_internal.h +6 -0
  150. package/runtime/ext_gui/webgl_shader.cpp +13 -1
  151. package/runtime/ext_gui/webgl_state.cpp +228 -0
  152. package/runtime/ext_gui/webgl_texture.cpp +144 -17
  153. package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
  154. package/runtime/ext_gui/webgl_uniform_block.h +31 -0
  155. package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
  156. package/runtime/ext_media/decode_audio.cpp +308 -0
  157. package/runtime/ext_media/media_bridge.c +2 -0
  158. package/runtime/rt.h +17 -0
  159. package/runtime/xt_array_buffer.c +30 -0
  160. package/runtime/xt_typed_array/elements.inc +13 -0
  161. package/scripts/build-media.ts +14 -6
  162. package/scripts/build-runtime.ts +21 -16
  163. package/scripts/check-version.ts +39 -9
  164. package/scripts/package-platform.ts +183 -0
  165. package/scripts/set-platform-version.ts +62 -0
  166. package/scripts/verify-platform-package.ts +182 -0
  167. package/src/cli/main.ts +5 -1
  168. package/src/driver/runtime-lib.ts +73 -9
  169. package/src/driver/version.ts +1 -1
  170. package/src/extensions/gui/index.ts +6 -5
  171. package/src/extensions/media/index.ts +4 -3
  172. package/src/parser/context.ts +99 -4
  173. package/src/parser/literals.ts +6 -12
@@ -19,6 +19,7 @@
19
19
  #include "paint.h"
20
20
 
21
21
  #include <stdio.h>
22
+ #include <stdlib.h>
22
23
  #include <string.h>
23
24
 
24
25
  namespace guidev {
@@ -98,11 +99,32 @@ static void xt_gui_gc_scan_roots(void) {
98
99
 
99
100
  /* -- initialisation ------------------------------------------------------- */
100
101
 
101
- static int ensure_init(void) { if (g_sdl_initialized) return 1;
102
- xt_gc_register_root_provider(xt_gui_gc_scan_roots);
102
+ /**
103
+ * Exit status for "this host cannot run a GUI program": no video driver, or no
104
+ * GPU device the SDL_GPU backends accept. It is deliberately distinct from a
105
+ * failed program so a CI script (or a user) can skip the run instead of
106
+ * reporting a crash. 77 is the autotools "skip" convention; `ci.yml` treats it
107
+ * that way for the provisional macOS job.
108
+ */
109
+ const int kGuiUnavailableExit = 77;
110
+
111
+ /** Report a window that could not be created as a JavaScript `Error`. Returning
112
+ * `undefined` used to surface one line later as `Cannot read properties of
113
+ * undefined (reading 'on')`, which hid the real diagnostic. */
114
+ static void throw_gui_error(const char *message) {
115
+ xt_value text = xt_string_from_cstr(message);
116
+ xt_throw(xt_error_ctor_by_name(xt_string_from_cstr("Error"), 1, &text));
117
+ }
118
+
119
+ static void ensure_init(void) {
120
+ if (g_sdl_initialized) return;
103
121
  if (!SDL_Init(SDL_INIT_VIDEO)) {
104
122
  fprintf(stderr, "xt_gui: SDL_Init failed: %s\n", SDL_GetError());
105
- return 0;
123
+ fprintf(stderr,
124
+ "xt_gui: the GUI extension needs a display (or a virtual one such as Xvfb); "
125
+ "exiting with status %d.\n",
126
+ kGuiUnavailableExit);
127
+ exit(kGuiUnavailableExit);
106
128
  }
107
129
  /* Accept every shader format so the same code runs on Metal, Vulkan and
108
130
  * D3D12; the backend only needs the one it supports. The engine ships MSL,
@@ -113,11 +135,17 @@ static int ensure_init(void) { if (g_sdl_initialized) return 1;
113
135
  g_device = SDL_CreateGPUDevice(formats, false, NULL);
114
136
  if (g_device == NULL) {
115
137
  fprintf(stderr, "xt_gui: SDL_CreateGPUDevice failed: %s\n", SDL_GetError());
138
+ fprintf(stderr,
139
+ "xt_gui: the GUI extension needs a Metal (macOS), Vulkan or D3D12 device; "
140
+ "this host does not provide a usable one. Exiting with status %d.\n",
141
+ kGuiUnavailableExit);
116
142
  SDL_Quit();
117
- return 0;
143
+ exit(kGuiUnavailableExit);
118
144
  }
145
+ /* Registered only once a window can exist: a failed init has nothing to
146
+ * mark, and a caller that cannot retry never needs a second provider. */
147
+ xt_gc_register_root_provider(xt_gui_gc_scan_roots);
119
148
  g_sdl_initialized = 1;
120
- return 1;
121
149
  }
122
150
 
123
151
  /* -- window lookup -------------------------------------------------------- */
@@ -254,15 +282,18 @@ xt_value xt_gui_ensure_window(void) {
254
282
  for (int i = g_window_count - 1; i >= 0; i--) {
255
283
  if (g_windows[i].open) return g_windows[i].object;
256
284
  }
257
- if (!ensure_init()) return XT_UNDEFINED;
285
+ ensure_init();
258
286
  XtGuiWindow *record = create_window_record("xbintsc gui", 800, 600, true);
259
- if (record == NULL) return XT_UNDEFINED;
287
+ if (record == NULL) {
288
+ throw_gui_error("xbintsc gui: could not open the implicit window (see the diagnostic above)");
289
+ return XT_UNDEFINED;
290
+ }
260
291
  g_implicit_window = 1;
261
292
  return record->object;
262
293
  }
263
294
 
264
295
  extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
265
- if (!ensure_init()) return XT_UNDEFINED;
296
+ ensure_init();
266
297
 
267
298
  const char *title = "xbintsc gui";
268
299
  int width = 800;
@@ -304,7 +335,11 @@ extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
304
335
  }
305
336
 
306
337
  XtGuiWindow *record = create_window_record(title, width, height, high_density);
307
- return record != NULL ? record->object : XT_UNDEFINED;
338
+ if (record == NULL) {
339
+ throw_gui_error("xbintsc gui: could not open a window (see the diagnostic above)");
340
+ return XT_UNDEFINED;
341
+ }
342
+ return record->object;
308
343
  }
309
344
 
310
345
  /* -- run() / quit() ------------------------------------------------------- */
@@ -45,6 +45,9 @@ xt_value xt_browser_local_storage(int32_t argc, xt_value *argv);
45
45
  xt_value xt_browser_missing(int32_t argc, xt_value *argv);
46
46
  /** A DOM type name (`Image`, `HTMLCanvasElement`, …) as a constructor value. */
47
47
  xt_value xt_browser_type(int32_t argc, xt_value *argv);
48
+ /** `createImageBitmap(source)`, exposed as a global *value*: returns the
49
+ * closure the compiler stores for the bare identifier. */
50
+ xt_value xt_browser_create_image_bitmap(int32_t argc, xt_value *argv);
48
51
  xt_value xt_browser_request_animation_frame(int32_t argc, xt_value *argv);
49
52
  xt_value xt_browser_cancel_animation_frame(int32_t argc, xt_value *argv);
50
53
  xt_value xt_browser_match_media(int32_t argc, xt_value *argv);
@@ -63,11 +66,13 @@ void xt_browser_schedule_frame(void);
63
66
  /** Whether `run()` currently owns the frame loop (the pump then stays idle). */
64
67
  int xt_gui_run_active(void);
65
68
 
66
- /* Web Audio objects (`audio.cpp`). */
67
- /** `AudioContext.prototype` — also the `instanceof AudioContext` target. */
69
+ /* Web Audio objects (`audio.cpp`).
70
+ * `AudioContext.prototype` — also the `instanceof AudioContext` target. */
68
71
  xt_value xt_gui_audio_proto(void);
69
- /** `new AudioContext(...)`: it has no mixer backend yet, so the graph records
70
- * its shape and parameters but nothing is rendered to the sound device. */
72
+ /** `new AudioContext(...)`: builds a real graph (mixer in `audio_mixer.cpp`)
73
+ * and decodes through `audio_decode.cpp` + the optional media backend. The
74
+ * device opens on the first `start()`/`resume()`; a headless host stays silent
75
+ * while the graph still renders. */
71
76
  xt_value xt_gui_audio_create(int32_t argc, xt_value *argv);
72
77
 
73
78
  #ifdef __cplusplus
@@ -0,0 +1,28 @@
1
+ /*
2
+ * xbintsc GUI engine — `ImageBitmap` pixel storage.
3
+ */
4
+
5
+ #include "image_bitmap.h"
6
+
7
+ #include <unordered_map>
8
+
9
+ namespace xtgui {
10
+ namespace {
11
+
12
+ std::unordered_map<uint64_t, Image> g_bitmaps;
13
+ uint64_t g_next_bitmap = 1;
14
+
15
+ } // namespace
16
+
17
+ uint64_t xt_image_bitmap_add(const Image &image) {
18
+ const uint64_t id = g_next_bitmap++;
19
+ g_bitmaps[id] = image;
20
+ return id;
21
+ }
22
+
23
+ const Image *xt_image_bitmap_get(uint64_t id) {
24
+ auto found = g_bitmaps.find(id);
25
+ return found == g_bitmaps.end() ? nullptr : &found->second;
26
+ }
27
+
28
+ } // namespace xtgui
@@ -0,0 +1,26 @@
1
+ /*
2
+ * xbintsc GUI engine — `ImageBitmap` storage.
3
+ *
4
+ * `createImageBitmap(source)` returns a settled promise of an immutable
5
+ * bitmap. The pixels stay in C++ so `texImage2D` can consume them without
6
+ * materialising a JavaScript array; the handle object carries the numeric id
7
+ * under `__xt_bitmap`.
8
+ */
9
+ #ifndef XT_GUI_IMAGE_BITMAP_H
10
+ #define XT_GUI_IMAGE_BITMAP_H
11
+
12
+ #include <cstdint>
13
+
14
+ #include "image.h"
15
+
16
+ namespace xtgui {
17
+
18
+ /** Store a decoded bitmap and return its process-wide id. */
19
+ uint64_t xt_image_bitmap_add(const Image &image);
20
+
21
+ /** Look up a bitmap; null when the id is unknown. */
22
+ const Image *xt_image_bitmap_get(uint64_t id);
23
+
24
+ } // namespace xtgui
25
+
26
+ #endif /* XT_GUI_IMAGE_BITMAP_H */
@@ -0,0 +1,136 @@
1
+ /*
2
+ * xbintsc GUI engine — `createImageBitmap`.
3
+ *
4
+ * The browser entry point returns a promise (as it does on the web), so code
5
+ * that writes `const bitmap = await createImageBitmap(canvas)` works unchanged.
6
+ * The pixels are kept in `image_bitmap.cpp`; `texImage2D` reads the same
7
+ * registry through the bitmap's `__xt_bitmap` id.
8
+ */
9
+
10
+ #include "gui_engine.h"
11
+ #include "dom_api_internal.h"
12
+
13
+ #include <cstring>
14
+ #include <string>
15
+ #include <vector>
16
+
17
+ #include "image_bitmap.h"
18
+ #include "webgl.h"
19
+
20
+ namespace {
21
+
22
+ void flip_rows(std::vector<unsigned char> *pixels, int width, int height) {
23
+ const size_t stride = (size_t)width * 4;
24
+ std::vector<unsigned char> row(stride);
25
+ for (int y = 0; y < height / 2; y++) {
26
+ unsigned char *top = pixels->data() + (size_t)y * stride;
27
+ unsigned char *bottom = pixels->data() + (size_t)(height - 1 - y) * stride;
28
+ memcpy(row.data(), top, stride);
29
+ memcpy(top, bottom, stride);
30
+ memcpy(bottom, row.data(), stride);
31
+ }
32
+ }
33
+
34
+ bool source_pixels(xt_value source, xtgui::Image *out) {
35
+ /* ImageData / another bitmap-like object: width, height, data. */
36
+ xt_value width_value = xt_object_get_cstr(source, "width");
37
+ xt_value height_value = xt_object_get_cstr(source, "height");
38
+ xt_value data = xt_object_get_cstr(source, "data");
39
+ if (XT_IS_OBJECT(data) && XT_IS_NUMBER(width_value) && XT_IS_NUMBER(height_value)) {
40
+ const int width = (int)xt_to_number(width_value);
41
+ const int height = (int)xt_to_number(height_value);
42
+ const std::vector<uint8_t> bytes = domapi::webgl_bytes(data);
43
+ if (width <= 0 || height <= 0 || bytes.size() < (size_t)width * height * 4) return false;
44
+ out->width = width;
45
+ out->height = height;
46
+ out->pixels.assign(bytes.begin(), bytes.begin() + (size_t)width * height * 4);
47
+ return true;
48
+ }
49
+
50
+ XtGuiWindow *win = nullptr;
51
+ xtgui::Node *node = domapi::resolve_node(source, &win);
52
+ if (node == nullptr || !node->isElement()) return false;
53
+ if (node->isTag("img")) {
54
+ const std::string *src = node->attr("src");
55
+ const xtgui::Image *image = src != nullptr ? xtgui::xt_image_load(*src) : nullptr;
56
+ if (image == nullptr) return false;
57
+ *out = *image;
58
+ return true;
59
+ }
60
+ if (node->isTag("canvas")) {
61
+ if (xtgui::Canvas2D *canvas = xt_gui_canvas_find(win, node)) {
62
+ *out = canvas->image();
63
+ return true;
64
+ }
65
+ if (xtgui::WebGLContext *context = xt_gui_webgl_find(win, node)) {
66
+ out->width = context->width();
67
+ out->height = context->height();
68
+ out->pixels.resize((size_t)out->width * out->height * 4);
69
+ if (!context->read_pixels(0, 0, out->width, out->height, out->pixels.data())) {
70
+ return false;
71
+ }
72
+ /* readPixels is GL-ordered (bottom-up); bitmaps are stored top-down. */
73
+ flip_rows(&out->pixels, out->width, out->height);
74
+ return true;
75
+ }
76
+ return false;
77
+ }
78
+ if (node->isTag("video")) {
79
+ xtgui::MediaPlayer *player = xt_gui_media_player(win, node);
80
+ const xtgui::Image *frame = player != nullptr ? player->frame() : nullptr;
81
+ if (frame == nullptr || frame->width <= 0 || frame->height <= 0) return false;
82
+ *out = *frame;
83
+ return true;
84
+ }
85
+ return false;
86
+ }
87
+
88
+ xt_value bitmap_object(const xtgui::Image &image) {
89
+ const uint64_t id = xtgui::xt_image_bitmap_add(image);
90
+ xt_value name = xt_string_from_cstr("ImageBitmap");
91
+ xt_value constructor = xt_browser_type(1, &name);
92
+ xt_value prototype = xt_object_get_cstr(constructor, "prototype");
93
+ xt_value bitmap = XT_IS_OBJECT(prototype) ? xt_object_new_with_proto(prototype)
94
+ : xt_object_new();
95
+ xt_object_set(bitmap, xt_string_from_cstr("width"), xt_number(image.width));
96
+ xt_object_set(bitmap, xt_string_from_cstr("height"), xt_number(image.height));
97
+ xt_object_set(bitmap, xt_string_from_cstr("__xt_bitmap"), xt_number((double)id));
98
+ return bitmap;
99
+ }
100
+
101
+ } // namespace
102
+
103
+ namespace {
104
+
105
+ xt_value create_bitmap(int32_t argc, xt_value *argv) {
106
+ if (argc < 1) return xt_promise_reject(xt_string_from_cstr("TypeError"));
107
+ xtgui::Image image;
108
+ if (!source_pixels(xt_arg(argc, argv, 0), &image)) {
109
+ return xt_promise_reject(xt_string_from_cstr("InvalidStateError"));
110
+ }
111
+ return xt_promise_resolve(bitmap_object(image));
112
+ }
113
+
114
+ /** The closure ABI the runtime calls for a first-class function value. */
115
+ xt_value create_bitmap_call(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
116
+ (void)self;
117
+ (void)env;
118
+ return create_bitmap(argc, argv);
119
+ }
120
+
121
+ xt_value g_create_bitmap = XT_UNDEFINED;
122
+
123
+ } // namespace
124
+
125
+ /* `createImageBitmap` is a *global value*: the compiler calls this getter once
126
+ * and stores the returned closure, which is then called like any function. */
127
+ xt_value xt_browser_create_image_bitmap(int32_t argc, xt_value *argv) {
128
+ (void)argc;
129
+ (void)argv;
130
+ if (!XT_IS_OBJECT(g_create_bitmap)) {
131
+ g_create_bitmap = xt_closure_new((void *)create_bitmap_call, 0, nullptr);
132
+ xt_gc_add_root(&g_create_bitmap);
133
+ }
134
+ return g_create_bitmap;
135
+ }
136
+
@@ -55,6 +55,38 @@ void xt_media_shutdown() {
55
55
  if (g_api != nullptr && g_api->shutdown != nullptr) g_api->shutdown();
56
56
  }
57
57
 
58
+ bool xt_media_decode_audio(const uint8_t* data, size_t size, DecodedAudio* out,
59
+ std::string* error) {
60
+ if (g_api == nullptr || g_api->decode_audio == nullptr) {
61
+ if (error) {
62
+ *error = "cannot decode this audio format; enable the 'media' extension (pass --ext media)";
63
+ }
64
+ return false;
65
+ }
66
+ XtMediaAudioBuffer decoded = {0, 0, 0, nullptr};
67
+ char message[kErrorSize] = {0};
68
+ if (g_api->decode_audio(data, size, &decoded, message, sizeof(message)) == 0 ||
69
+ decoded.samples == nullptr) {
70
+ if (decoded.samples != nullptr && g_api->free_audio != nullptr) {
71
+ g_api->free_audio(decoded.samples);
72
+ }
73
+ if (error) *error = message[0] != '\0' ? message : "cannot decode audio data";
74
+ return false;
75
+ }
76
+ if (decoded.channels <= 0 || decoded.frames < 0 || decoded.sample_rate <= 0) {
77
+ if (g_api->free_audio != nullptr) g_api->free_audio(decoded.samples);
78
+ if (error) *error = "decoder produced an empty audio buffer";
79
+ return false;
80
+ }
81
+ out->channels = decoded.channels;
82
+ out->sample_rate = decoded.sample_rate;
83
+ out->frames = static_cast<uint32_t>(decoded.frames);
84
+ size_t count = static_cast<size_t>(decoded.frames) * static_cast<size_t>(decoded.channels);
85
+ out->interleaved.assign(decoded.samples, decoded.samples + count);
86
+ if (g_api->free_audio != nullptr) g_api->free_audio(decoded.samples);
87
+ return true;
88
+ }
89
+
58
90
  } // namespace xtgui
59
91
 
60
92
  extern "C" void xt_media_install(const XtMediaApi* api) { g_api = api; }
@@ -35,11 +35,24 @@
35
35
  #include <memory>
36
36
  #include <string>
37
37
 
38
+ #include "audio_decode.h"
38
39
  #include "image.h"
39
40
  #include "media_api.h"
40
41
 
41
42
  namespace xtgui {
42
43
 
44
+ /**
45
+ * Decode an in-memory audio file (`decodeAudioData`).
46
+ *
47
+ * The engine has its own WAV decoder (`audio_decode.cpp`); this entry point is
48
+ * the fallback for compressed containers and codecs, which only the optional
49
+ * `media` extension can decode. Without that extension it fails with a message
50
+ * naming the extension. Implemented in `media.cpp`, which forwards to the
51
+ * backend table installed by the media bridge.
52
+ */
53
+ bool xt_media_decode_audio(const uint8_t* data, size_t size, DecodedAudio* out,
54
+ std::string* error);
55
+
43
56
  /** DOM events a media element may fire after `tick()`. */
44
57
  enum MediaEvent {
45
58
  kMediaEventLoadedMetadata = 1 << 0,
@@ -40,10 +40,30 @@ typedef XtMediaPlayerHandle (*XtMediaByIdFn)(uint64_t id);
40
40
  /** Tear down every decoder and process-wide media state. */
41
41
  typedef void (*XtMediaShutdownFn)(void);
42
42
 
43
+ /** Interleaved float PCM returned by the memory decoder.
44
+ *
45
+ * `samples` is allocated by the backend and must be released with the table's
46
+ * `free_audio` entry point (both archives use the same static CRT). */
47
+ typedef struct XtMediaAudioBuffer {
48
+ int channels;
49
+ int sample_rate;
50
+ int frames;
51
+ float *samples;
52
+ } XtMediaAudioBuffer;
53
+
54
+ /** Decode an in-memory audio file (`decodeAudioData`). Returns 1 on success and
55
+ * fills `out`; on failure returns 0 and copies a message into `error`. */
56
+ typedef int (*XtMediaDecodeAudioFn)(const unsigned char *data, size_t size,
57
+ XtMediaAudioBuffer *out, char *error, size_t error_size);
58
+ /** Release the `samples` a successful `decode_audio` allocated. */
59
+ typedef void (*XtMediaFreeAudioFn)(void *samples);
60
+
43
61
  typedef struct XtMediaApi {
44
62
  XtMediaOpenFn open;
45
63
  XtMediaByIdFn by_id;
46
64
  XtMediaShutdownFn shutdown;
65
+ XtMediaDecodeAudioFn decode_audio;
66
+ XtMediaFreeAudioFn free_audio;
47
67
  } XtMediaApi;
48
68
 
49
69
  /** Install a decoder table. Implemented by the GUI engine; called once by the
@@ -57,6 +77,9 @@ XtMediaPlayerHandle xt_media_backend_open(const char *path, char *error,
57
77
  size_t error_size, void *audio_sink);
58
78
  XtMediaPlayerHandle xt_media_backend_by_id(uint64_t id);
59
79
  void xt_media_backend_shutdown(void);
80
+ int xt_media_backend_decode_audio(const unsigned char *data, size_t size,
81
+ XtMediaAudioBuffer *out, char *error, size_t error_size);
82
+ void xt_media_backend_free_audio(void *samples);
60
83
 
61
84
  #ifdef __cplusplus
62
85
  }