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.
- package/README.md +10 -7
- package/README.zh-CN.md +10 -8
- package/dist/scripts/check-version.d.ts +27 -0
- package/dist/scripts/check-version.js +71 -0
- package/dist/scripts/check-version.js.map +1 -0
- package/dist/scripts/package-platform.d.ts +58 -0
- package/dist/scripts/package-platform.js +142 -0
- package/dist/scripts/package-platform.js.map +1 -0
- package/dist/scripts/verify-platform-package.d.ts +34 -0
- package/dist/scripts/verify-platform-package.js +161 -0
- package/dist/scripts/verify-platform-package.js.map +1 -0
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/main.js +3 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/driver/runtime-lib.d.ts +29 -4
- package/dist/src/driver/runtime-lib.js +68 -10
- package/dist/src/driver/runtime-lib.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/index.js +6 -5
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/media/index.js +4 -3
- package/dist/src/extensions/media/index.js.map +1 -1
- package/dist/src/parser/context.d.ts +41 -0
- package/dist/src/parser/context.js +89 -4
- package/dist/src/parser/context.js.map +1 -1
- package/dist/src/parser/literals.js +6 -12
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/tests/driver/platform-package.test.d.ts +1 -0
- package/dist/tests/driver/platform-package.test.js +49 -0
- package/dist/tests/driver/platform-package.test.js.map +1 -0
- package/dist/tests/driver/runtime-lib.test.js +51 -4
- package/dist/tests/driver/runtime-lib.test.js.map +1 -1
- package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
- package/dist/tests/e2e/gui-audio.test.js +287 -0
- package/dist/tests/e2e/gui-audio.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +7 -0
- package/dist/tests/e2e/gui-helpers.js +1 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-media.test.js +78 -0
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/gui-style.test.js +55 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl.test.js +258 -0
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2.test.js +45 -1
- package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
- package/dist/tests/extensions/gui-index.test.js +1 -0
- package/dist/tests/extensions/gui-index.test.js.map +1 -1
- package/dist/tests/extensions/media.test.js +1 -0
- package/dist/tests/extensions/media.test.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +30 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/platform-tarball.test.d.ts +1 -0
- package/dist/tests/platform-tarball.test.js +111 -0
- package/dist/tests/platform-tarball.test.js.map +1 -0
- package/dist/tests/version.test.js +6 -0
- package/dist/tests/version.test.js.map +1 -1
- package/doc/ai/contributing.md +11 -0
- package/doc/ai/extensions.md +7 -4
- package/doc/ai/troubleshooting.md +7 -5
- package/doc/ai/zh-CN/contributing.md +9 -0
- package/doc/ai/zh-CN/extensions.md +4 -2
- package/doc/ai/zh-CN/troubleshooting.md +5 -3
- package/doc/gui/api.md +61 -13
- package/doc/gui/build.md +11 -4
- package/doc/gui/dom.md +6 -3
- package/doc/gui/media.md +4 -0
- package/doc/gui/webgl.md +105 -46
- package/doc/gui.md +7 -3
- package/doc/npm-package.md +55 -27
- package/doc/requirements.md +6 -2
- package/doc/zh-CN/gui/api.md +51 -10
- package/doc/zh-CN/gui/build.md +7 -2
- package/doc/zh-CN/gui/dom.md +3 -1
- package/doc/zh-CN/gui/media.md +3 -0
- package/doc/zh-CN/gui/webgl.md +22 -16
- package/doc/zh-CN/gui.md +3 -2
- package/doc/zh-CN/npm-package.md +44 -23
- package/doc/zh-CN/requirements.md +4 -1
- package/package.json +17 -1
- package/runtime/ext_gui/audio.cpp +365 -153
- package/runtime/ext_gui/audio_buffer.cpp +166 -0
- package/runtime/ext_gui/audio_buffer.h +37 -0
- package/runtime/ext_gui/audio_decode.cpp +219 -0
- package/runtime/ext_gui/audio_decode.h +39 -0
- package/runtime/ext_gui/audio_device.cpp +87 -0
- package/runtime/ext_gui/audio_effects.cpp +163 -0
- package/runtime/ext_gui/audio_effects.h +25 -0
- package/runtime/ext_gui/audio_internal.h +30 -0
- package/runtime/ext_gui/audio_mixer.cpp +494 -0
- package/runtime/ext_gui/audio_mixer.h +238 -0
- package/runtime/ext_gui/audio_param.cpp +104 -0
- package/runtime/ext_gui/audio_param_object.cpp +155 -0
- package/runtime/ext_gui/browser.cpp +11 -1
- package/runtime/ext_gui/css_selector.cpp +21 -3
- package/runtime/ext_gui/dom_api_internal.h +16 -0
- package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
- package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
- package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
- package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
- package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
- package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
- package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
- package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
- package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
- package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
- package/runtime/ext_gui/gui.cpp +44 -9
- package/runtime/ext_gui/gui_engine.h +9 -4
- package/runtime/ext_gui/image_bitmap.cpp +28 -0
- package/runtime/ext_gui/image_bitmap.h +26 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
- package/runtime/ext_gui/media.cpp +32 -0
- package/runtime/ext_gui/media.h +13 -0
- package/runtime/ext_gui/media_api.h +23 -0
- package/runtime/ext_gui/webgl.h +152 -18
- package/runtime/ext_gui/webgl_blit.cpp +95 -0
- package/runtime/ext_gui/webgl_buffer.cpp +178 -263
- package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
- package/runtime/ext_gui/webgl_clear.cpp +142 -0
- package/runtime/ext_gui/webgl_compressed.cpp +180 -0
- package/runtime/ext_gui/webgl_context.cpp +241 -156
- package/runtime/ext_gui/webgl_draw.cpp +68 -3
- package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
- package/runtime/ext_gui/webgl_glsl.h +40 -0
- package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
- package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
- package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
- package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
- package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
- package/runtime/ext_gui/webgl_program_internal.h +6 -0
- package/runtime/ext_gui/webgl_shader.cpp +13 -1
- package/runtime/ext_gui/webgl_state.cpp +228 -0
- package/runtime/ext_gui/webgl_texture.cpp +144 -17
- package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
- package/runtime/ext_gui/webgl_uniform_block.h +31 -0
- package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
- package/runtime/ext_media/decode_audio.cpp +308 -0
- package/runtime/ext_media/media_bridge.c +2 -0
- package/runtime/rt.h +17 -0
- package/runtime/xt_array_buffer.c +30 -0
- package/runtime/xt_typed_array/elements.inc +13 -0
- package/scripts/build-media.ts +14 -6
- package/scripts/build-runtime.ts +21 -16
- package/scripts/check-version.ts +39 -9
- package/scripts/package-platform.ts +183 -0
- package/scripts/set-platform-version.ts +62 -0
- package/scripts/verify-platform-package.ts +182 -0
- package/src/cli/main.ts +5 -1
- package/src/driver/runtime-lib.ts +73 -9
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/index.ts +6 -5
- package/src/extensions/media/index.ts +4 -3
- package/src/parser/context.ts +99 -4
- package/src/parser/literals.ts +6 -12
package/runtime/ext_gui/gui.cpp
CHANGED
|
@@ -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
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
285
|
+
ensure_init();
|
|
258
286
|
XtGuiWindow *record = create_window_record("xbintsc gui", 800, 600, true);
|
|
259
|
-
if (record == NULL)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(...)`:
|
|
70
|
-
*
|
|
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; }
|
package/runtime/ext_gui/media.h
CHANGED
|
@@ -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
|
}
|