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
@@ -0,0 +1,161 @@
1
+ /*
2
+ * xbintsc GUI engine — WebGL pixel sources.
3
+ *
4
+ * Decodes the things `texImage2D` accepts (`<img>`, `<canvas>`, `<video>`,
5
+ * `ImageData` and `ImageBitmap`) into tightly packed RGBA8, and converts the
6
+ * RGBA/RGB/LUMINANCE families to that layout.
7
+ */
8
+
9
+ #include "dom_api_internal.h"
10
+
11
+ #include <cstring>
12
+ #include <string>
13
+ #include <vector>
14
+
15
+ #include "dom_api_webgl_rt.h"
16
+ #include "image.h"
17
+ #include "image_bitmap.h"
18
+ #include "webgl.h"
19
+
20
+ namespace domapi {
21
+ namespace {
22
+
23
+ void flip_rows(std::vector<uint8_t> *pixels, int width, int height) {
24
+ const size_t stride = (size_t)width * 4;
25
+ std::vector<uint8_t> row(stride);
26
+ for (int y = 0; y < height / 2; y++) {
27
+ uint8_t *top = pixels->data() + (size_t)y * stride;
28
+ uint8_t *bottom = pixels->data() + (size_t)(height - 1 - y) * stride;
29
+ memcpy(row.data(), top, stride);
30
+ memcpy(top, bottom, stride);
31
+ memcpy(bottom, row.data(), stride);
32
+ }
33
+ }
34
+
35
+ } // namespace
36
+
37
+ void webgl_convert_pixels(const uint8_t *source, int count, int format, bool premultiply,
38
+ std::vector<uint8_t> *out) {
39
+ out->assign((size_t)count * 4, 255);
40
+ for (int i = 0; i < count; i++) {
41
+ uint8_t r = 0;
42
+ uint8_t g = 0;
43
+ uint8_t b = 0;
44
+ uint8_t a = 255;
45
+ switch (format) {
46
+ case GL_RGBA:
47
+ r = source[i * 4 + 0];
48
+ g = source[i * 4 + 1];
49
+ b = source[i * 4 + 2];
50
+ a = source[i * 4 + 3];
51
+ break;
52
+ case GL_RGB:
53
+ r = source[i * 3 + 0];
54
+ g = source[i * 3 + 1];
55
+ b = source[i * 3 + 2];
56
+ break;
57
+ case GL_LUMINANCE_ALPHA:
58
+ r = g = b = source[i * 2 + 0];
59
+ a = source[i * 2 + 1];
60
+ break;
61
+ case GL_ALPHA:
62
+ a = source[i];
63
+ r = g = b = 0;
64
+ break;
65
+ case GL_LUMINANCE:
66
+ default:
67
+ r = g = b = source[i];
68
+ break;
69
+ }
70
+ if (premultiply) {
71
+ r = (uint8_t)((r * a + 127) / 255);
72
+ g = (uint8_t)((g * a + 127) / 255);
73
+ b = (uint8_t)((b * a + 127) / 255);
74
+ }
75
+ (*out)[(size_t)i * 4 + 0] = r;
76
+ (*out)[(size_t)i * 4 + 1] = g;
77
+ (*out)[(size_t)i * 4 + 2] = b;
78
+ (*out)[(size_t)i * 4 + 3] = a;
79
+ }
80
+ }
81
+
82
+
83
+ /** Decode a DOM source into top-down RGBA8. */
84
+ bool webgl_read_source(xt_value source, int *width, int *height, std::vector<uint8_t> *rgba,
85
+ bool premultiply) {
86
+ if (XT_IS_OBJECT(source)) {
87
+ xt_value bitmap_id = xt_object_get_cstr(source, "__xt_bitmap");
88
+ if (XT_IS_NUMBER(bitmap_id)) {
89
+ const xtgui::Image *bitmap =
90
+ xtgui::xt_image_bitmap_get((uint64_t)xt_to_number(bitmap_id));
91
+ if (bitmap == nullptr || bitmap->width <= 0 || bitmap->height <= 0) return false;
92
+ *width = bitmap->width;
93
+ *height = bitmap->height;
94
+ if (premultiply) {
95
+ webgl_convert_pixels(bitmap->pixels.data(), bitmap->width * bitmap->height, GL_RGBA, true,
96
+ rgba);
97
+ } else {
98
+ *rgba = bitmap->pixels;
99
+ }
100
+ return true;
101
+ }
102
+ }
103
+ XtGuiWindow *win = nullptr;
104
+ xtgui::Node *node = resolve_node(source, &win);
105
+ const xtgui::Image *image = nullptr;
106
+ std::vector<uint8_t> owned;
107
+ if (node != nullptr && node->isElement() && node->isTag("img")) {
108
+ const std::string *src = node->attr("src");
109
+ if (src != nullptr) image = xtgui::xt_image_load(*src);
110
+ } else if (node != nullptr && node->isElement() && node->isTag("video")) {
111
+ xtgui::MediaPlayer *player = xt_gui_media_player(win, node);
112
+ if (player != nullptr) image = player->frame();
113
+ } else if (node != nullptr && node->isElement() && node->isTag("canvas")) {
114
+ if (xtgui::Canvas2D *canvas = xt_gui_canvas_find(win, node)) {
115
+ image = &canvas->image();
116
+ } else if (xtgui::WebGLContext *context = xt_gui_webgl_find(win, node)) {
117
+ owned.resize((size_t)context->width() * context->height() * 4);
118
+ if (!context->read_pixels(0, 0, context->width(), context->height(), owned.data())) {
119
+ return false;
120
+ }
121
+ /* `readPixels` is GL-ordered (bottom-up); textures are stored top-down. */
122
+ flip_rows(&owned, context->width(), context->height());
123
+ *width = context->width();
124
+ *height = context->height();
125
+ rgba->swap(owned);
126
+ return true;
127
+ }
128
+ } else if (XT_IS_OBJECT(source) && !xt_is_array_buffer(source)) {
129
+ xt_value data = xt_object_get_cstr(source, "data");
130
+ xt_value width_value = xt_object_get_cstr(source, "width");
131
+ xt_value height_value = xt_object_get_cstr(source, "height");
132
+ if (XT_IS_OBJECT(data) && XT_IS_NUMBER(width_value) && XT_IS_NUMBER(height_value)) {
133
+ const int w = (int)xt_to_number(width_value);
134
+ const int h = (int)xt_to_number(height_value);
135
+ const std::vector<uint8_t> bytes = webgl_bytes(data);
136
+ if (w <= 0 || h <= 0 || bytes.size() < (size_t)w * h * 4) return false;
137
+ webgl_convert_pixels(bytes.data(), w * h, GL_RGBA, premultiply, rgba);
138
+ *width = w;
139
+ *height = h;
140
+ return true;
141
+ }
142
+ /* `new Image()` is a plain object with a `src`. */
143
+ xt_value src = xt_object_get_cstr(source, "src");
144
+ if (XT_IS_STRING(src)) {
145
+ const char *path = xt_string_data(xt_to_string(src));
146
+ if (path != nullptr && path[0] != '\0') image = xtgui::xt_image_load(path);
147
+ }
148
+ }
149
+ if (image == nullptr || image->width <= 0 || image->height <= 0) return false;
150
+ *width = image->width;
151
+ *height = image->height;
152
+ if (premultiply) {
153
+ webgl_convert_pixels(image->pixels.data(), image->width * image->height, GL_RGBA, true, rgba);
154
+ } else {
155
+ *rgba = image->pixels;
156
+ }
157
+ return true;
158
+ }
159
+
160
+
161
+ } // namespace domapi
@@ -14,7 +14,16 @@
14
14
  namespace domapi {
15
15
  namespace {
16
16
 
17
- xtgui::WebGLContext *context_of(xt_value self) { return webgl_from_context(self); }
17
+ xtgui::WebGLContext *context_of(xt_value self) { return webgl_live_context(self); }
18
+
19
+ /** Fire a `webglcontextlost`/`webglcontextrestored` event on the canvas that
20
+ * owns the context. `self` is the context or extension handle. */
21
+ void dispatch_context_event(xt_value self, const char *type) {
22
+ XtGuiWindow *win = nullptr;
23
+ xtgui::Node *node =
24
+ resolve_node(xt_object_get_cstr(self, "__xt_gl_node"), &win);
25
+ if (win != nullptr && node != nullptr) xt_gui_dispatch_to_node(win, node, type);
26
+ }
18
27
 
19
28
  xt_value number_array(const double *values, int count) {
20
29
  xt_value array = xt_array_new(0, nullptr);
@@ -28,6 +37,9 @@ xt_value object_for(xt_value self, uint32_t id, const char *key, const char *kin
28
37
 
29
38
  } // namespace
30
39
 
40
+ METHOD(webgl_lose_context);
41
+ METHOD(webgl_restore_context);
42
+
31
43
  void webgl_unpack_flags(xt_value context, bool *flip_y, bool *premultiply) {
32
44
  *flip_y = value_truthy(xt_object_get_cstr(context, "__xt_unpack_flip_y"));
33
45
  *premultiply = value_truthy(xt_object_get_cstr(context, "__xt_unpack_premultiply"));
@@ -121,8 +133,13 @@ METHOD(webgl_depth_range) {
121
133
 
122
134
  METHOD(webgl_blend_color) {
123
135
  (void)env;
124
- (void)argc;
125
- (void)argv;
136
+ xtgui::WebGLContext *context = context_of(self);
137
+ if (context != nullptr) {
138
+ context->blend_color((float)webgl_number(argc, argv, 0, 0.0),
139
+ (float)webgl_number(argc, argv, 1, 0.0),
140
+ (float)webgl_number(argc, argv, 2, 0.0),
141
+ (float)webgl_number(argc, argv, 3, 0.0));
142
+ }
126
143
  return XT_UNDEFINED;
127
144
  }
128
145
 
@@ -258,7 +275,8 @@ METHOD(webgl_get_error) {
258
275
  (void)env;
259
276
  (void)argc;
260
277
  (void)argv;
261
- xtgui::WebGLContext *context = context_of(self);
278
+ xtgui::WebGLContext *context = webgl_from_context(self);
279
+ if (context != nullptr && context->lost()) return xt_number(GL_CONTEXT_LOST_WEBGL);
262
280
  return xt_number(context != nullptr ? context->error() : GL_NO_ERROR);
263
281
  }
264
282
 
@@ -280,6 +298,34 @@ METHOD(webgl_get_parameter) {
280
298
  const double values[4] = {(double)box[0], (double)box[1], (double)box[2], (double)box[3]};
281
299
  return number_array(values, 4);
282
300
  }
301
+ case GL_STENCIL_FUNC:
302
+ return xt_number(context->stencil_face(GL_FRONT).func);
303
+ case GL_STENCIL_REF:
304
+ return xt_number(context->stencil_face(GL_FRONT).ref);
305
+ case GL_STENCIL_VALUE_MASK:
306
+ return xt_number((double)context->stencil_face(GL_FRONT).value_mask);
307
+ case GL_STENCIL_WRITEMASK:
308
+ return xt_number((double)context->stencil_face(GL_FRONT).write_mask);
309
+ case GL_STENCIL_FAIL:
310
+ return xt_number(context->stencil_face(GL_FRONT).fail);
311
+ case GL_STENCIL_PASS_DEPTH_FAIL:
312
+ return xt_number(context->stencil_face(GL_FRONT).zfail);
313
+ case GL_STENCIL_PASS_DEPTH_PASS:
314
+ return xt_number(context->stencil_face(GL_FRONT).zpass);
315
+ case GL_STENCIL_BACK_FUNC:
316
+ return xt_number(context->stencil_face(GL_BACK).func);
317
+ case GL_STENCIL_BACK_REF:
318
+ return xt_number(context->stencil_face(GL_BACK).ref);
319
+ case GL_STENCIL_BACK_VALUE_MASK:
320
+ return xt_number((double)context->stencil_face(GL_BACK).value_mask);
321
+ case GL_STENCIL_BACK_WRITEMASK:
322
+ return xt_number((double)context->stencil_face(GL_BACK).write_mask);
323
+ case GL_STENCIL_BACK_FAIL:
324
+ return xt_number(context->stencil_face(GL_BACK).fail);
325
+ case GL_STENCIL_BACK_PASS_DEPTH_FAIL:
326
+ return xt_number(context->stencil_face(GL_BACK).zfail);
327
+ case GL_STENCIL_BACK_PASS_DEPTH_PASS:
328
+ return xt_number(context->stencil_face(GL_BACK).zpass);
283
329
  case GL_COLOR_CLEAR_VALUE: {
284
330
  const double values[4] = {0, 0, 0, 0};
285
331
  return number_array(values, 4);
@@ -289,8 +335,10 @@ METHOD(webgl_get_parameter) {
289
335
  return number_array(values, 2);
290
336
  }
291
337
  case GL_BLEND_COLOR: {
292
- const double values[4] = {0, 0, 0, 0};
293
- return number_array(values, 4);
338
+ float values[4] = {0.0f, 0.0f, 0.0f, 0.0f};
339
+ context->blend_color_value(values);
340
+ const double out[4] = {values[0], values[1], values[2], values[3]};
341
+ return number_array(out, 4);
294
342
  }
295
343
  case GL_ALIASED_LINE_WIDTH_RANGE:
296
344
  case GL_ALIASED_POINT_SIZE_RANGE: {
@@ -318,12 +366,36 @@ METHOD(webgl_get_parameter) {
318
366
  ? webgl_make_object(self, "__xt_gl_texture", texture, "texture")
319
367
  : XT_NULL;
320
368
  }
369
+ case GL_TEXTURE_BINDING_CUBE_MAP: {
370
+ const uint32_t texture = context->bound_cube_texture(0);
371
+ return texture != 0
372
+ ? webgl_make_object(self, "__xt_gl_texture", texture, "texture")
373
+ : XT_NULL;
374
+ }
375
+ case GL_TEXTURE_BINDING_2D_ARRAY: {
376
+ const uint32_t texture = context->bound_array_texture(0);
377
+ return texture != 0
378
+ ? webgl_make_object(self, "__xt_gl_texture", texture, "texture")
379
+ : XT_NULL;
380
+ }
381
+ case GL_TEXTURE_BINDING_3D: {
382
+ const uint32_t texture = context->bound_3d_texture(0);
383
+ return texture != 0
384
+ ? webgl_make_object(self, "__xt_gl_texture", texture, "texture")
385
+ : XT_NULL;
386
+ }
321
387
  case GL_FRAMEBUFFER_BINDING: {
322
388
  const uint32_t framebuffer = context->bound_framebuffer_id();
323
389
  return framebuffer != 0
324
390
  ? webgl_make_object(self, "__xt_gl_framebuffer", framebuffer, "framebuffer")
325
391
  : XT_NULL;
326
392
  }
393
+ case GL_READ_FRAMEBUFFER_BINDING: {
394
+ const uint32_t framebuffer = context->bound_read_framebuffer_id();
395
+ return framebuffer != 0
396
+ ? webgl_make_object(self, "__xt_gl_framebuffer", framebuffer, "framebuffer")
397
+ : XT_NULL;
398
+ }
327
399
  case GL_RENDERBUFFER_BINDING: {
328
400
  const uint32_t renderbuffer = context->bound_renderbuffer_id();
329
401
  return renderbuffer != 0
@@ -351,22 +423,50 @@ METHOD(webgl_get_supported_extensions) {
351
423
  xt_value list = xt_array_new(0, nullptr);
352
424
  xt_array_push(list, xt_string_from_cstr("OES_element_index_uint"));
353
425
  xt_array_push(list, xt_string_from_cstr("OES_standard_derivatives"));
426
+ xt_array_push(list, xt_string_from_cstr("OES_texture_float"));
427
+ xt_array_push(list, xt_string_from_cstr("OES_texture_half_float"));
428
+ xt_array_push(list, xt_string_from_cstr("WEBGL_depth_texture"));
429
+ xt_array_push(list, xt_string_from_cstr("EXT_color_buffer_float"));
430
+ xt_array_push(list, xt_string_from_cstr("EXT_color_buffer_half_float"));
354
431
  xt_array_push(list, xt_string_from_cstr("WEBGL_lose_context"));
432
+ xtgui::WebGLContext *context = webgl_from_context(self);
433
+ if (context != nullptr && context->supports_texture_format(0x83F1)) {
434
+ xt_array_push(list, xt_string_from_cstr("WEBGL_compressed_texture_s3tc"));
435
+ }
436
+ if (context != nullptr && context->supports_texture_format(0x8C4D)) {
437
+ xt_array_push(list, xt_string_from_cstr("WEBGL_compressed_texture_s3tc_srgb"));
438
+ }
439
+ if (context != nullptr && context->supports_texture_format(0x93B0)) {
440
+ xt_array_push(list, xt_string_from_cstr("WEBGL_compressed_texture_astc"));
441
+ }
355
442
  return list;
356
443
  }
357
444
 
358
445
  METHOD(webgl_get_extension) {
359
446
  (void)env;
360
447
  const std::string name = webgl_string(argc, argv, 0);
361
- if (name == "OES_element_index_uint" || name == "OES_standard_derivatives") {
448
+ if (name.rfind("WEBGL_compressed_texture_", 0) == 0) {
449
+ xt_value extension = webgl_compressed_extension(name, webgl_from_context(self));
450
+ if (!XT_IS_UNDEFINED(extension)) return extension;
451
+ }
452
+ if (name == "OES_element_index_uint" || name == "OES_standard_derivatives" ||
453
+ name == "OES_texture_float" || name == "OES_texture_half_float" ||
454
+ name == "WEBGL_depth_texture" || name == "EXT_color_buffer_float" ||
455
+ name == "EXT_color_buffer_half_float") {
362
456
  return xt_object_new();
363
457
  }
364
458
  if (name == "WEBGL_lose_context") {
365
459
  xt_value object = xt_object_new();
460
+ /* The closures receive this object as `this`, so carry the context id and
461
+ * the canvas handle over from the context that produced the extension. */
462
+ xt_object_set(object, xt_string_from_cstr("__xt_gl_context"),
463
+ xt_object_get_cstr(self, "__xt_gl_context"));
464
+ xt_object_set(object, xt_string_from_cstr("__xt_gl_node"),
465
+ xt_object_get_cstr(self, "__xt_gl_node"));
366
466
  xt_object_set(object, xt_string_from_cstr("loseContext"),
367
- xt_closure_new((void *)webgl_flush, 0, nullptr));
467
+ xt_closure_new((void *)webgl_lose_context, 0, nullptr));
368
468
  xt_object_set(object, xt_string_from_cstr("restoreContext"),
369
- xt_closure_new((void *)webgl_flush, 0, nullptr));
469
+ xt_closure_new((void *)webgl_restore_context, 0, nullptr));
370
470
  return object;
371
471
  }
372
472
  return XT_NULL;
@@ -379,7 +479,7 @@ METHOD(webgl_get_context_attributes) {
379
479
  xt_value attributes = xt_object_new();
380
480
  xt_object_set(attributes, xt_string_from_cstr("alpha"), XT_TRUE);
381
481
  xt_object_set(attributes, xt_string_from_cstr("depth"), XT_TRUE);
382
- xt_object_set(attributes, xt_string_from_cstr("stencil"), XT_FALSE);
482
+ xt_object_set(attributes, xt_string_from_cstr("stencil"), XT_TRUE);
383
483
  xt_object_set(attributes, xt_string_from_cstr("antialias"), XT_FALSE);
384
484
  xt_object_set(attributes, xt_string_from_cstr("premultipliedAlpha"), XT_TRUE);
385
485
  xt_object_set(attributes, xt_string_from_cstr("preserveDrawingBuffer"), XT_TRUE);
@@ -393,51 +493,97 @@ METHOD(webgl_is_context_lost) {
393
493
  (void)env;
394
494
  (void)argc;
395
495
  (void)argv;
396
- xtgui::WebGLContext *context = context_of(self);
496
+ xtgui::WebGLContext *context = webgl_from_context(self);
397
497
  return xt_bool(context == nullptr || context->lost());
398
498
  }
399
499
 
400
- /* Stencil operations are accepted for API completeness; the depth target has
401
- * no stencil attachment, so they do not change rendering. */
402
- METHOD(webgl_stencil_func) {
500
+ /* `WEBGL_lose_context.loseContext()` / `restoreContext()`: the simulator drops
501
+ * every object and reports `CONTEXT_LOST_WEBGL` until the restore, so a page
502
+ * exercises the same loss/restore path it would take on a real GPU reset. */
503
+ METHOD(webgl_lose_context) {
403
504
  (void)env;
404
505
  (void)argc;
405
506
  (void)argv;
507
+ xtgui::WebGLContext *context = webgl_from_context(self);
508
+ if (context == nullptr || context->lost()) return XT_UNDEFINED;
509
+ context->lose_context();
510
+ dispatch_context_event(self, "webglcontextlost");
406
511
  return XT_UNDEFINED;
407
512
  }
408
513
 
409
- METHOD(webgl_stencil_func_separate) {
514
+ METHOD(webgl_restore_context) {
410
515
  (void)env;
411
516
  (void)argc;
412
517
  (void)argv;
518
+ xtgui::WebGLContext *context = webgl_from_context(self);
519
+ if (context == nullptr || !context->lost()) return XT_UNDEFINED;
520
+ context->restore_context();
521
+ if (!context->lost()) dispatch_context_event(self, "webglcontextrestored");
522
+ return XT_UNDEFINED;
523
+ }
524
+
525
+ /* Stencil test/update state. SDL_GPU has one stencil reference per draw and no
526
+ * per-pipeline compare/write masks; the entry points store WebGL's state, the
527
+ * pipeline carries the compare function and ops, and `SDL_SetGPUStencilReference`
528
+ * supplies the reference. A write mask of 0 is emulated as KEEP. */
529
+ METHOD(webgl_stencil_func) {
530
+ (void)env;
531
+ xtgui::WebGLContext *context = context_of(self);
532
+ if (context != nullptr) {
533
+ context->stencil_func(webgl_int(argc, argv, 0, GL_ALWAYS), webgl_int(argc, argv, 1, 0),
534
+ (uint32_t)webgl_number(argc, argv, 2, 0xFFFFFFFF));
535
+ }
536
+ return XT_UNDEFINED;
537
+ }
538
+
539
+ METHOD(webgl_stencil_func_separate) {
540
+ (void)env;
541
+ xtgui::WebGLContext *context = context_of(self);
542
+ if (context != nullptr) {
543
+ const int face = webgl_int(argc, argv, 0, GL_FRONT_AND_BACK);
544
+ context->stencil_func_separate(face, webgl_int(argc, argv, 1, GL_ALWAYS),
545
+ webgl_int(argc, argv, 2, 0),
546
+ (uint32_t)webgl_number(argc, argv, 3, 0xFFFFFFFF));
547
+ }
413
548
  return XT_UNDEFINED;
414
549
  }
415
550
 
416
551
  METHOD(webgl_stencil_op) {
417
552
  (void)env;
418
- (void)argc;
419
- (void)argv;
553
+ xtgui::WebGLContext *context = context_of(self);
554
+ if (context != nullptr) {
555
+ context->stencil_op(webgl_int(argc, argv, 0, GL_KEEP), webgl_int(argc, argv, 1, GL_KEEP),
556
+ webgl_int(argc, argv, 2, GL_KEEP));
557
+ }
420
558
  return XT_UNDEFINED;
421
559
  }
422
560
 
423
561
  METHOD(webgl_stencil_op_separate) {
424
562
  (void)env;
425
- (void)argc;
426
- (void)argv;
563
+ xtgui::WebGLContext *context = context_of(self);
564
+ if (context != nullptr) {
565
+ const int face = webgl_int(argc, argv, 0, GL_FRONT_AND_BACK);
566
+ context->stencil_op_separate(face, webgl_int(argc, argv, 1, GL_KEEP),
567
+ webgl_int(argc, argv, 2, GL_KEEP),
568
+ webgl_int(argc, argv, 3, GL_KEEP));
569
+ }
427
570
  return XT_UNDEFINED;
428
571
  }
429
572
 
430
573
  METHOD(webgl_stencil_mask) {
431
574
  (void)env;
432
- (void)argc;
433
- (void)argv;
575
+ xtgui::WebGLContext *context = context_of(self);
576
+ if (context != nullptr) context->stencil_mask((uint32_t)webgl_number(argc, argv, 0, 0xFFFFFFFF));
434
577
  return XT_UNDEFINED;
435
578
  }
436
579
 
437
580
  METHOD(webgl_stencil_mask_separate) {
438
581
  (void)env;
439
- (void)argc;
440
- (void)argv;
582
+ xtgui::WebGLContext *context = context_of(self);
583
+ if (context != nullptr) {
584
+ context->stencil_mask_separate(webgl_int(argc, argv, 0, GL_FRONT_AND_BACK),
585
+ (uint32_t)webgl_number(argc, argv, 1, 0xFFFFFFFF));
586
+ }
441
587
  return XT_UNDEFINED;
442
588
  }
443
589