xbintsc 0.5.37 → 0.5.41

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 (166) hide show
  1. package/dist/src/cli/help.d.ts +1 -1
  2. package/dist/src/codegen/generator/module.js +23 -7
  3. package/dist/src/codegen/generator/module.js.map +1 -1
  4. package/dist/src/codegen/generator/state.js +1 -0
  5. package/dist/src/codegen/generator/state.js.map +1 -1
  6. package/dist/src/codegen/generator/tables.js +1 -0
  7. package/dist/src/codegen/generator/tables.js.map +1 -1
  8. package/dist/src/driver/bundler/deferred.d.ts +63 -0
  9. package/dist/src/driver/bundler/deferred.js +279 -0
  10. package/dist/src/driver/bundler/deferred.js.map +1 -0
  11. package/dist/src/driver/bundler/merge.js +214 -2
  12. package/dist/src/driver/bundler/merge.js.map +1 -1
  13. package/dist/src/driver/runtime-objects.d.ts +1 -1
  14. package/dist/src/driver/runtime-objects.js +1 -0
  15. package/dist/src/driver/runtime-objects.js.map +1 -1
  16. package/dist/src/driver/version.d.ts +1 -1
  17. package/dist/src/driver/version.js +1 -1
  18. package/dist/src/extensions/gui/html.js +98 -15
  19. package/dist/src/extensions/gui/html.js.map +1 -1
  20. package/dist/src/extensions/gui/index.js +1 -0
  21. package/dist/src/extensions/gui/index.js.map +1 -1
  22. package/dist/src/extensions/gui/three.d.ts +4 -3
  23. package/dist/src/extensions/gui/three.js +4 -3
  24. package/dist/src/extensions/gui/three.js.map +1 -1
  25. package/dist/tests/codegen/closures.test.js +7 -0
  26. package/dist/tests/codegen/closures.test.js.map +1 -1
  27. package/dist/tests/e2e/compile-objects-classes.test.js +31 -0
  28. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  29. package/dist/tests/e2e/gui-canvas-state.test.d.ts +9 -0
  30. package/dist/tests/e2e/gui-canvas-state.test.js +297 -0
  31. package/dist/tests/e2e/gui-canvas-state.test.js.map +1 -0
  32. package/dist/tests/e2e/gui-draw-perf.test.d.ts +22 -0
  33. package/dist/tests/e2e/gui-draw-perf.test.js +405 -0
  34. package/dist/tests/e2e/gui-draw-perf.test.js.map +1 -0
  35. package/dist/tests/e2e/gui-perf.test.d.ts +23 -0
  36. package/dist/tests/e2e/gui-perf.test.js +283 -0
  37. package/dist/tests/e2e/gui-perf.test.js.map +1 -0
  38. package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
  39. package/dist/tests/e2e/gui-script-import-order.test.js +169 -0
  40. package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
  41. package/dist/tests/e2e/gui-webgl.test.js +4 -4
  42. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  43. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.d.ts +8 -0
  44. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js +336 -0
  45. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js.map +1 -0
  46. package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
  47. package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
  48. package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
  49. package/dist/tests/e2e/url.test.d.ts +6 -0
  50. package/dist/tests/e2e/url.test.js +114 -0
  51. package/dist/tests/e2e/url.test.js.map +1 -0
  52. package/dist/tests/extensions/gui.test.js +45 -10
  53. package/dist/tests/extensions/gui.test.js.map +1 -1
  54. package/doc/gui/api.md +28 -2
  55. package/doc/gui/canvas.md +110 -27
  56. package/doc/gui/known-issues.md +32 -10
  57. package/doc/gui/rendering.md +77 -0
  58. package/doc/gui/runtime.md +18 -3
  59. package/doc/gui/webgl.md +58 -29
  60. package/doc/gui-scripts.md +19 -19
  61. package/doc/gui.md +43 -9
  62. package/doc/implemented/fetch.md +5 -0
  63. package/doc/implemented/modules.md +13 -0
  64. package/doc/implemented/stdlib.md +17 -0
  65. package/doc/unimplemented/deviations.md +1 -0
  66. package/doc/zh-CN/gui/api.md +25 -2
  67. package/doc/zh-CN/gui/canvas.md +95 -26
  68. package/doc/zh-CN/gui/known-issues.md +21 -4
  69. package/doc/zh-CN/gui/rendering.md +59 -0
  70. package/doc/zh-CN/gui/runtime.md +13 -3
  71. package/doc/zh-CN/gui/webgl.md +30 -8
  72. package/doc/zh-CN/gui-scripts.md +14 -13
  73. package/doc/zh-CN/gui.md +32 -8
  74. package/doc/zh-CN/implemented/fetch.md +4 -0
  75. package/doc/zh-CN/implemented/modules.md +10 -0
  76. package/doc/zh-CN/implemented/stdlib.md +15 -0
  77. package/doc/zh-CN/unimplemented/deviations.md +1 -0
  78. package/package.json +7 -7
  79. package/runtime/ext_gui/browser.cpp +3 -1
  80. package/runtime/ext_gui/canvas.cpp +135 -89
  81. package/runtime/ext_gui/canvas.h +116 -0
  82. package/runtime/ext_gui/canvas_clip.cpp +143 -0
  83. package/runtime/ext_gui/canvas_composite.cpp +241 -0
  84. package/runtime/ext_gui/canvas_coverage.cpp +241 -0
  85. package/runtime/ext_gui/canvas_gradient.cpp +41 -4
  86. package/runtime/ext_gui/canvas_image_data.cpp +4 -0
  87. package/runtime/ext_gui/canvas_internal.h +34 -9
  88. package/runtime/ext_gui/canvas_raster.cpp +207 -178
  89. package/runtime/ext_gui/canvas_shadow.cpp +161 -0
  90. package/runtime/ext_gui/canvas_stroke.cpp +37 -9
  91. package/runtime/ext_gui/canvas_style.cpp +59 -0
  92. package/runtime/ext_gui/canvas_text.cpp +140 -0
  93. package/runtime/ext_gui/css.h +7 -0
  94. package/runtime/ext_gui/css_sheet.cpp +22 -0
  95. package/runtime/ext_gui/document.cpp +25 -1
  96. package/runtime/ext_gui/document.h +8 -0
  97. package/runtime/ext_gui/dom_api_canvas.cpp +16 -1
  98. package/runtime/ext_gui/dom_api_canvas_image.cpp +9 -0
  99. package/runtime/ext_gui/dom_api_canvas_state.cpp +217 -0
  100. package/runtime/ext_gui/dom_api_internal.h +36 -37
  101. package/runtime/ext_gui/dom_api_offscreen.cpp +213 -0
  102. package/runtime/ext_gui/dom_api_proto.cpp +18 -0
  103. package/runtime/ext_gui/dom_api_webgl.cpp +29 -9
  104. package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
  105. package/runtime/ext_gui/dom_api_webgl_internal.h +58 -0
  106. package/runtime/ext_gui/dom_api_webgl_source.cpp +18 -0
  107. package/runtime/ext_gui/gradient_values.cpp +12 -1
  108. package/runtime/ext_gui/gui.cpp +5 -0
  109. package/runtime/ext_gui/gui_canvas.cpp +4 -0
  110. package/runtime/ext_gui/gui_engine.h +32 -0
  111. package/runtime/ext_gui/gui_events.cpp +182 -58
  112. package/runtime/ext_gui/gui_media.cpp +3 -0
  113. package/runtime/ext_gui/image.cpp +69 -0
  114. package/runtime/ext_gui/image_bitmap_browser.cpp +11 -0
  115. package/runtime/ext_gui/layout.cpp +4 -0
  116. package/runtime/ext_gui/paint.cpp +19 -0
  117. package/runtime/ext_gui/paint_background.cpp +18 -1
  118. package/runtime/ext_gui/perf.cpp +171 -0
  119. package/runtime/ext_gui/perf.h +127 -0
  120. package/runtime/ext_gui/renderer.cpp +109 -41
  121. package/runtime/ext_gui/renderer.h +33 -9
  122. package/runtime/ext_gui/renderer_image.cpp +135 -65
  123. package/runtime/ext_gui/renderer_internal.h +15 -3
  124. package/runtime/ext_gui/renderer_text.cpp +24 -12
  125. package/runtime/ext_gui/script.cpp +31 -0
  126. package/runtime/ext_gui/style.cpp +101 -30
  127. package/runtime/ext_gui/style.h +6 -3
  128. package/runtime/ext_gui/style_print.cpp +7 -1
  129. package/runtime/ext_gui/text.cpp +23 -4
  130. package/runtime/ext_gui/text.h +29 -0
  131. package/runtime/ext_gui/text_cache.cpp +118 -0
  132. package/runtime/ext_gui/text_glyph_cache.cpp +100 -0
  133. package/runtime/ext_gui/webgl.h +48 -146
  134. package/runtime/ext_gui/webgl_blit.cpp +12 -1
  135. package/runtime/ext_gui/webgl_blit_depth.cpp +270 -0
  136. package/runtime/ext_gui/webgl_buffer.cpp +59 -1
  137. package/runtime/ext_gui/webgl_buffer_objects.cpp +106 -9
  138. package/runtime/ext_gui/webgl_clear.cpp +3 -0
  139. package/runtime/ext_gui/webgl_context.cpp +29 -3
  140. package/runtime/ext_gui/webgl_draw.cpp +75 -270
  141. package/runtime/ext_gui/webgl_draw_buffers.cpp +1 -0
  142. package/runtime/ext_gui/webgl_draw_setup.cpp +321 -0
  143. package/runtime/ext_gui/webgl_draw_state.h +130 -0
  144. package/runtime/ext_gui/webgl_glsl.h +3 -0
  145. package/runtime/ext_gui/webgl_glsl_scan.cpp +3 -8
  146. package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
  147. package/runtime/ext_gui/webgl_msl.cpp +4 -0
  148. package/runtime/ext_gui/webgl_objects.h +148 -0
  149. package/runtime/ext_gui/webgl_pipeline.cpp +8 -8
  150. package/runtime/ext_gui/webgl_program_internal.h +35 -0
  151. package/runtime/ext_gui/webgl_shader.cpp +34 -36
  152. package/runtime/ext_gui/webgl_webgl2.cpp +36 -0
  153. package/runtime/ext_gui/window.cpp +56 -1
  154. package/runtime/rt.h +3 -0
  155. package/runtime/xt_fetch/async.inc +60 -0
  156. package/runtime/xt_url_object.c +463 -0
  157. package/src/codegen/generator/module.ts +22 -7
  158. package/src/codegen/generator/state.ts +1 -0
  159. package/src/codegen/generator/tables.ts +1 -0
  160. package/src/driver/bundler/deferred.ts +351 -0
  161. package/src/driver/bundler/merge.ts +219 -2
  162. package/src/driver/runtime-objects.ts +1 -0
  163. package/src/driver/version.ts +1 -1
  164. package/src/extensions/gui/html.ts +100 -17
  165. package/src/extensions/gui/index.ts +1 -0
  166. package/src/extensions/gui/three.ts +4 -3
@@ -0,0 +1,217 @@
1
+ /*
2
+ * xbintsc GUI engine — the Canvas2D state additions.
3
+ *
4
+ * `globalCompositeOperation`, the four shadow properties, the image-smoothing
5
+ * flags, and the path/clip hit-testing entry points. They live apart from the
6
+ * original style bindings so both translation units stay small.
7
+ */
8
+
9
+ #include "dom_api_internal.h"
10
+
11
+ #include <string>
12
+ #include <vector>
13
+
14
+ #include "canvas.h"
15
+
16
+ #define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
17
+
18
+ namespace domapi {
19
+
20
+ namespace {
21
+
22
+ /** Parse a trailing fill-rule argument (`"evenodd"`/`"nonzero"`). */
23
+ bool fill_rule_arg(xt_value value, bool *even_odd) {
24
+ if (!XT_IS_STRING(value)) return false;
25
+ const std::string rule = to_string_value(value);
26
+ if (rule == "evenodd") {
27
+ *even_odd = true;
28
+ return true;
29
+ }
30
+ if (rule == "nonzero") {
31
+ *even_odd = false;
32
+ return true;
33
+ }
34
+ return false;
35
+ }
36
+
37
+ } // namespace
38
+
39
+ METHOD(canvas_global_composite_get) {
40
+ (void)env;
41
+ (void)argc;
42
+ (void)argv;
43
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
44
+ return xt_string_from_cstr(canvas != nullptr ? canvas->global_composite_operation().c_str()
45
+ : "source-over");
46
+ }
47
+
48
+ METHOD(canvas_global_composite_set) {
49
+ (void)env;
50
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
51
+ if (canvas != nullptr) canvas->set_global_composite_operation(canvas_string_arg(argc, argv, 0));
52
+ return XT_UNDEFINED;
53
+ }
54
+
55
+ METHOD(canvas_shadow_color_get) {
56
+ (void)env;
57
+ (void)argc;
58
+ (void)argv;
59
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
60
+ return xt_string_from_cstr(canvas != nullptr ? canvas->shadow_color().c_str()
61
+ : "rgba(0, 0, 0, 0)");
62
+ }
63
+
64
+ METHOD(canvas_shadow_color_set) {
65
+ (void)env;
66
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
67
+ if (canvas != nullptr) canvas->set_shadow_color(canvas_string_arg(argc, argv, 0));
68
+ return XT_UNDEFINED;
69
+ }
70
+
71
+ METHOD(canvas_shadow_blur_get) {
72
+ (void)env;
73
+ (void)argc;
74
+ (void)argv;
75
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
76
+ return xt_number(canvas != nullptr ? canvas->shadow_blur() : 0.0);
77
+ }
78
+
79
+ METHOD(canvas_shadow_blur_set) {
80
+ (void)env;
81
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
82
+ if (canvas != nullptr) canvas->set_shadow_blur((float)canvas_number_arg(argc, argv, 0, 0.0));
83
+ return XT_UNDEFINED;
84
+ }
85
+
86
+ METHOD(canvas_shadow_offset_x_get) {
87
+ (void)env;
88
+ (void)argc;
89
+ (void)argv;
90
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
91
+ return xt_number(canvas != nullptr ? canvas->shadow_offset_x() : 0.0);
92
+ }
93
+
94
+ METHOD(canvas_shadow_offset_x_set) {
95
+ (void)env;
96
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
97
+ if (canvas != nullptr) canvas->set_shadow_offset_x((float)canvas_number_arg(argc, argv, 0, 0.0));
98
+ return XT_UNDEFINED;
99
+ }
100
+
101
+ METHOD(canvas_shadow_offset_y_get) {
102
+ (void)env;
103
+ (void)argc;
104
+ (void)argv;
105
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
106
+ return xt_number(canvas != nullptr ? canvas->shadow_offset_y() : 0.0);
107
+ }
108
+
109
+ METHOD(canvas_shadow_offset_y_set) {
110
+ (void)env;
111
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
112
+ if (canvas != nullptr) canvas->set_shadow_offset_y((float)canvas_number_arg(argc, argv, 0, 0.0));
113
+ return XT_UNDEFINED;
114
+ }
115
+
116
+ METHOD(canvas_image_smoothing_enabled_get) {
117
+ (void)env;
118
+ (void)argc;
119
+ (void)argv;
120
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
121
+ return xt_bool(canvas == nullptr || canvas->image_smoothing_enabled());
122
+ }
123
+
124
+ METHOD(canvas_image_smoothing_enabled_set) {
125
+ (void)env;
126
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
127
+ if (canvas != nullptr) {
128
+ canvas->set_image_smoothing_enabled(value_truthy(xt_arg(argc, argv, 0)));
129
+ }
130
+ return XT_UNDEFINED;
131
+ }
132
+
133
+ METHOD(canvas_image_smoothing_quality_get) {
134
+ (void)env;
135
+ (void)argc;
136
+ (void)argv;
137
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
138
+ return xt_string_from_cstr(canvas != nullptr ? canvas->image_smoothing_quality().c_str()
139
+ : "low");
140
+ }
141
+
142
+ METHOD(canvas_image_smoothing_quality_set) {
143
+ (void)env;
144
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
145
+ if (canvas != nullptr) canvas->set_image_smoothing_quality(canvas_string_arg(argc, argv, 0));
146
+ return XT_UNDEFINED;
147
+ }
148
+
149
+ METHOD(canvas_clip) {
150
+ (void)env;
151
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
152
+ if (canvas == nullptr) return XT_UNDEFINED;
153
+ bool even_odd = false;
154
+ bool has_path = false;
155
+ std::vector<xtgui::CanvasSubpath> path;
156
+ for (int32_t i = 0; i < argc; i++) {
157
+ xt_value value = xt_arg(argc, argv, i);
158
+ bool rule = false;
159
+ if (fill_rule_arg(value, &rule)) {
160
+ even_odd = rule;
161
+ } else if (path2d_is(value)) {
162
+ path = path2d_subpaths(value);
163
+ has_path = true;
164
+ }
165
+ }
166
+ if (has_path) {
167
+ canvas->clip_paths(path, even_odd);
168
+ } else {
169
+ canvas->clip(even_odd);
170
+ }
171
+ return XT_UNDEFINED;
172
+ }
173
+
174
+ METHOD(canvas_is_point_in_path) {
175
+ (void)env;
176
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
177
+ if (canvas == nullptr || argc == 0) return XT_FALSE;
178
+ int32_t index = 0;
179
+ std::vector<xtgui::CanvasSubpath> path;
180
+ bool has_path = false;
181
+ if (path2d_is(xt_arg(argc, argv, 0))) {
182
+ path = path2d_subpaths(xt_arg(argc, argv, 0));
183
+ has_path = true;
184
+ index = 1;
185
+ }
186
+ const float x = (float)canvas_number_arg(argc, argv, index, 0.0);
187
+ const float y = (float)canvas_number_arg(argc, argv, index + 1, 0.0);
188
+ bool even_odd = false;
189
+ for (int32_t i = index + 2; i < argc; i++) fill_rule_arg(xt_arg(argc, argv, i), &even_odd);
190
+ const bool inside =
191
+ has_path ? canvas->is_point_in_paths(path, x, y, even_odd)
192
+ : canvas->is_point_in_path(x, y, even_odd);
193
+ return xt_bool(inside);
194
+ }
195
+
196
+ METHOD(canvas_is_point_in_stroke) {
197
+ (void)env;
198
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
199
+ if (canvas == nullptr || argc == 0) return XT_FALSE;
200
+ int32_t index = 0;
201
+ std::vector<xtgui::CanvasSubpath> path;
202
+ bool has_path = false;
203
+ if (path2d_is(xt_arg(argc, argv, 0))) {
204
+ path = path2d_subpaths(xt_arg(argc, argv, 0));
205
+ has_path = true;
206
+ index = 1;
207
+ }
208
+ const float x = (float)canvas_number_arg(argc, argv, index, 0.0);
209
+ const float y = (float)canvas_number_arg(argc, argv, index + 1, 0.0);
210
+ const bool inside =
211
+ has_path ? canvas->is_point_in_stroke_paths(path, x, y) : canvas->is_point_in_stroke(x, y);
212
+ return xt_bool(inside);
213
+ }
214
+
215
+ } // namespace domapi
216
+
217
+ #undef METHOD
@@ -17,6 +17,7 @@
17
17
  #include <vector>
18
18
 
19
19
  #include "gui_engine.h"
20
+ #include "dom_api_webgl_internal.h"
20
21
 
21
22
  namespace domapi {
22
23
 
@@ -52,6 +53,23 @@ xt_value class_list_proto();
52
53
  xt_value style_proto();
53
54
  xt_value computed_style_proto();
54
55
  xt_value canvas_proto();
56
+ /** The `getContextAttributes()` object for a freshly created 2D context. */
57
+ xt_value canvas_context_attributes(xt_value requested, bool alpha);
58
+ /** `OffscreenCanvas.prototype`. */
59
+ xt_value offscreen_canvas_proto();
60
+ /** `new OffscreenCanvas(width, height)`. */
61
+ xt_value offscreen_canvas_create(int32_t argc, xt_value *argv);
62
+ /** `canvas.transferControlToOffscreen()`. */
63
+ xt_value canvas_transfer_control_to_offscreen(xt_value self, xt_value env, int32_t argc,
64
+ xt_value *argv);
65
+ xt_value offscreen_get_context(xt_value self, xt_value env, int32_t argc, xt_value *argv);
66
+ xt_value offscreen_width_get(xt_value self, xt_value env, int32_t argc, xt_value *argv);
67
+ xt_value offscreen_width_set(xt_value self, xt_value env, int32_t argc, xt_value *argv);
68
+ xt_value offscreen_height_get(xt_value self, xt_value env, int32_t argc, xt_value *argv);
69
+ xt_value offscreen_height_set(xt_value self, xt_value env, int32_t argc, xt_value *argv);
70
+ xt_value offscreen_transfer_to_image_bitmap(xt_value self, xt_value env, int32_t argc,
71
+ xt_value *argv);
72
+ xt_value offscreen_convert_to_blob(xt_value self, xt_value env, int32_t argc, xt_value *argv);
55
73
  xt_value canvas_gradient_proto();
56
74
  xt_value webgl_proto();
57
75
  xt_value webgl2_proto();
@@ -91,43 +109,7 @@ xt_value canvas_gradient_handle(xt_value context, const xtgui::Canvas2D *canvas,
91
109
  uint64_t canvas_gradient_id(xt_value handle, const xtgui::Canvas2D *canvas);
92
110
 
93
111
  /* -- WebGL helpers shared by `dom_api_webgl*.cpp` ------------------------- */
94
-
95
- /** The engine WebGL context behind a context handle, or null. */
96
- xtgui::WebGLContext *webgl_from_context(xt_value self);
97
- /** Like `webgl_from_context`, but null while the context is lost: every GL
98
- * entry point except `getError`/`isContextLost` is a no-op in that state. */
99
- xtgui::WebGLContext *webgl_live_context(xt_value self);
100
- /** The numeric context id stored on a context handle (0 when absent). */
101
- uint64_t webgl_context_id(xt_value self);
102
- /** Create the JavaScript handle for a canvas's WebGL context. */
103
- xt_value webgl_context_handle(xt_value element, xtgui::WebGLContext *context);
104
- /** The id stored on an object handle under `key`, or 0. */
105
- uint32_t webgl_object_id(xt_value handle, const char *key);
106
- /** Build an object handle (`{__xt_gl_context, __xt_gl_<key>}`). */
107
- xt_value webgl_make_object(xt_value context, const char *key, uint32_t id,
108
- const std::string &kind);
109
- double webgl_number(int32_t argc, xt_value *argv, int32_t index, double fallback);
110
- int webgl_int(int32_t argc, xt_value *argv, int32_t index, int fallback);
111
- std::string webgl_string(int32_t argc, xt_value *argv, int32_t index);
112
- /** Read an array/typed array of numbers. */
113
- std::vector<double> webgl_numbers(xt_value value);
114
- /** Read the raw bytes of a typed array / ArrayBuffer (or a byte array). */
115
- std::vector<uint8_t> webgl_bytes(xt_value value);
116
- /** Store `count` bytes into a typed array / array handle. */
117
- void webgl_write_bytes(xt_value target, const uint8_t *bytes, size_t count);
118
- /** Convert a source pixel array (RGBA/RGB/LUMINANCE/…) to RGBA8. */
119
- void webgl_convert_pixels(const uint8_t *source, int count, int format, bool premultiply,
120
- std::vector<uint8_t> *out);
121
- /** Decode a `texImage2D` source (`<img>`, `<canvas>`, `<video>`, `ImageData`,
122
- * `ImageBitmap`) into top-down RGBA8. */
123
- bool webgl_read_source(xt_value source, int *width, int *height,
124
- std::vector<uint8_t> *rgba, bool premultiply);
125
- /** Read the `UNPACK_FLIP_Y_WEBGL` / `UNPACK_PREMULTIPLY_ALPHA_WEBGL` flags set
126
- * through `pixelStorei`. */
127
- void webgl_unpack_flags(xt_value context, bool *flip_y, bool *premultiply);
128
- /** Extension object for a compressed-texture family (null when unsupported,
129
- * undefined when the name is not one of those extensions). */
130
- xt_value webgl_compressed_extension(const std::string &name, xtgui::WebGLContext *context);
112
+ /* Declared in `dom_api_webgl_internal.h`, included above. */
131
113
 
132
114
  /** How a pending handle names the node it will bind to. */
133
115
  enum PendingKind { pending_by_id = 1, pending_by_selector = 2 };
@@ -285,6 +267,23 @@ METHOD(canvas_line_width_get);
285
267
  METHOD(canvas_line_width_set);
286
268
  METHOD(canvas_global_alpha_get);
287
269
  METHOD(canvas_global_alpha_set);
270
+ METHOD(canvas_global_composite_get);
271
+ METHOD(canvas_global_composite_set);
272
+ METHOD(canvas_shadow_color_get);
273
+ METHOD(canvas_shadow_color_set);
274
+ METHOD(canvas_shadow_blur_get);
275
+ METHOD(canvas_shadow_blur_set);
276
+ METHOD(canvas_shadow_offset_x_get);
277
+ METHOD(canvas_shadow_offset_x_set);
278
+ METHOD(canvas_shadow_offset_y_get);
279
+ METHOD(canvas_shadow_offset_y_set);
280
+ METHOD(canvas_image_smoothing_enabled_get);
281
+ METHOD(canvas_image_smoothing_enabled_set);
282
+ METHOD(canvas_image_smoothing_quality_get);
283
+ METHOD(canvas_image_smoothing_quality_set);
284
+ METHOD(canvas_clip);
285
+ METHOD(canvas_is_point_in_path);
286
+ METHOD(canvas_is_point_in_stroke);
288
287
  METHOD(canvas_font_get);
289
288
  METHOD(canvas_font_set);
290
289
  METHOD(canvas_text_align_get);
@@ -0,0 +1,213 @@
1
+ /*
2
+ * xbintsc GUI engine — `OffscreenCanvas`.
3
+ *
4
+ * An offscreen canvas is a `Canvas2D` bitmap with no DOM element behind it. It
5
+ * is created either by `new OffscreenCanvas(w, h)` or by transferring a live
6
+ * `<canvas>` element (`transferControlToOffscreen`), in which case the element
7
+ * keeps its bitmap and only the context ownership moves.
8
+ *
9
+ * Ownership stays with the window (`offscreen_canvases`), like every other
10
+ * canvas; the process-wide id table makes the bitmap reachable from
11
+ * `drawImage`/`createPattern`/`texImage2D` without a DOM node.
12
+ */
13
+
14
+ #include "dom_api_internal.h"
15
+
16
+ #include <string>
17
+ #include <vector>
18
+
19
+ #include "canvas.h"
20
+ #include "gui_engine.h"
21
+ #include "image_bitmap.h"
22
+
23
+ #define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
24
+
25
+ namespace domapi {
26
+
27
+ namespace {
28
+
29
+ xtgui::Canvas2D *offscreen_canvas(xt_value self) {
30
+ xt_value id = xt_object_get_cstr(self, "__xt_gui_canvas");
31
+ if (!XT_IS_NUMBER(id)) return nullptr;
32
+ return xtgui::xt_canvas_by_id((uint64_t)xt_to_number(id));
33
+ }
34
+
35
+ xt_value make_offscreen(xtgui::Canvas2D *canvas, xt_value window) {
36
+ xt_value handle = xt_object_new_with_proto(offscreen_canvas_proto());
37
+ xt_object_set(handle, xt_string_from_cstr("__xt_gui_canvas"), xt_number((double)canvas->id()));
38
+ xt_object_set(handle, xt_string_from_cstr("__xt_gui_window"), window);
39
+ xt_object_set(handle, xt_string_from_cstr("__xt_gui_offscreen"), XT_TRUE);
40
+ /* `ctx.canvas` of an offscreen context returns the offscreen object. */
41
+ xt_object_set(handle, xt_string_from_cstr("__xt_gui_node"), handle);
42
+ return handle;
43
+ }
44
+
45
+ xt_value image_bitmap_handle(const xtgui::Image &image) {
46
+ const uint64_t id = xtgui::xt_image_bitmap_add(image);
47
+ xt_value name = xt_string_from_cstr("ImageBitmap");
48
+ xt_value constructor = xt_browser_type(1, &name);
49
+ xt_value prototype = xt_object_get_cstr(constructor, "prototype");
50
+ xt_value bitmap = XT_IS_OBJECT(prototype) ? xt_object_new_with_proto(prototype)
51
+ : xt_object_new();
52
+ xt_object_set(bitmap, xt_string_from_cstr("width"), xt_number(image.width));
53
+ xt_object_set(bitmap, xt_string_from_cstr("height"), xt_number(image.height));
54
+ xt_object_set(bitmap, xt_string_from_cstr("__xt_bitmap"), xt_number((double)id));
55
+ return bitmap;
56
+ }
57
+
58
+ } // namespace
59
+
60
+ xt_value offscreen_canvas_proto() {
61
+ static xt_value proto = XT_UNDEFINED;
62
+ if (XT_IS_OBJECT(proto)) return proto;
63
+ proto = xt_object_new();
64
+ xt_gc_add_root(&proto);
65
+ struct Entry {
66
+ const char *name;
67
+ void *getter;
68
+ void *setter;
69
+ };
70
+ const Entry properties[] = {
71
+ {"width", (void *)offscreen_width_get, (void *)offscreen_width_set},
72
+ {"height", (void *)offscreen_height_get, (void *)offscreen_height_set},
73
+ };
74
+ for (const Entry &property : properties) {
75
+ xt_object_define_getter(proto, xt_string_from_cstr(property.name),
76
+ xt_closure_new(property.getter, 0, nullptr));
77
+ xt_object_define_setter(proto, xt_string_from_cstr(property.name),
78
+ xt_closure_new(property.setter, 0, nullptr));
79
+ }
80
+ struct Method {
81
+ const char *name;
82
+ void *fn;
83
+ };
84
+ const Method methods[] = {
85
+ {"getContext", (void *)offscreen_get_context},
86
+ {"transferToImageBitmap", (void *)offscreen_transfer_to_image_bitmap},
87
+ {"convertToBlob", (void *)offscreen_convert_to_blob},
88
+ };
89
+ for (const Method &method : methods) {
90
+ xt_object_set(proto, xt_string_from_cstr(method.name),
91
+ xt_closure_new(method.fn, 0, nullptr));
92
+ }
93
+ return proto;
94
+ }
95
+
96
+ xt_value offscreen_canvas_create(int32_t argc, xt_value *argv) {
97
+ xt_value window = xt_gui_ensure_window();
98
+ XtGuiWindow *win = xt_gui_window_from_this(window);
99
+ if (win == nullptr) return XT_NULL;
100
+ int width = argc >= 1 ? (int)xt_to_number(xt_arg(argc, argv, 0)) : 300;
101
+ int height = argc >= 2 ? (int)xt_to_number(xt_arg(argc, argv, 1)) : 150;
102
+ if (width < 1) width = 1;
103
+ if (height < 1) height = 1;
104
+ auto canvas = std::make_unique<xtgui::Canvas2D>(width, height);
105
+ xtgui::Canvas2D *raw = canvas.get();
106
+ win->offscreen_canvases[raw->id()] = std::move(canvas);
107
+ return make_offscreen(raw, window);
108
+ }
109
+
110
+ METHOD(offscreen_width_get) {
111
+ (void)env;
112
+ (void)argc;
113
+ (void)argv;
114
+ xtgui::Canvas2D *canvas = offscreen_canvas(self);
115
+ return xt_number(canvas != nullptr ? canvas->width() : 0);
116
+ }
117
+
118
+ METHOD(offscreen_width_set) {
119
+ (void)env;
120
+ xtgui::Canvas2D *canvas = offscreen_canvas(self);
121
+ if (canvas != nullptr) canvas->resize((int)canvas_number_arg(argc, argv, 0, 0.0), canvas->height());
122
+ return XT_UNDEFINED;
123
+ }
124
+
125
+ METHOD(offscreen_height_get) {
126
+ (void)env;
127
+ (void)argc;
128
+ (void)argv;
129
+ xtgui::Canvas2D *canvas = offscreen_canvas(self);
130
+ return xt_number(canvas != nullptr ? canvas->height() : 0);
131
+ }
132
+
133
+ METHOD(offscreen_height_set) {
134
+ (void)env;
135
+ xtgui::Canvas2D *canvas = offscreen_canvas(self);
136
+ if (canvas != nullptr) canvas->resize(canvas->width(), (int)canvas_number_arg(argc, argv, 0, 0.0));
137
+ return XT_UNDEFINED;
138
+ }
139
+
140
+ METHOD(offscreen_get_context) {
141
+ (void)env;
142
+ xtgui::Canvas2D *canvas = offscreen_canvas(self);
143
+ if (canvas == nullptr) return XT_NULL;
144
+ const std::string mode = canvas_string_arg(argc, argv, 0);
145
+ /* The GPU context needs a DOM node for its texture table; the 2D bitmap does
146
+ * not, so only "2d" resolves here. */
147
+ if (mode != "2d") return XT_NULL;
148
+ xt_value cached = xt_object_get_cstr(self, "__xt_gui_ctx");
149
+ if (XT_IS_OBJECT(cached)) return cached;
150
+ xt_value attributes = xt_arg(argc, argv, 1);
151
+ bool alpha = true;
152
+ if (XT_IS_OBJECT(attributes)) {
153
+ xt_value value = xt_object_get_cstr(attributes, "alpha");
154
+ if (XT_IS_BOOL(value)) alpha = value_truthy(value);
155
+ }
156
+ canvas->set_alpha(alpha);
157
+ xt_value context = xt_object_new_with_proto(canvas_proto());
158
+ xt_object_set(context, xt_string_from_cstr("__xt_gui_canvas"), xt_number((double)canvas->id()));
159
+ xt_object_set(context, xt_string_from_cstr("__xt_gui_node"), self);
160
+ xt_object_set(context, xt_string_from_cstr("__xt_gui_ctx_attributes"),
161
+ canvas_context_attributes(attributes, alpha));
162
+ xt_object_set(self, xt_string_from_cstr("__xt_gui_ctx"), context);
163
+ return context;
164
+ }
165
+
166
+ METHOD(offscreen_transfer_to_image_bitmap) {
167
+ (void)env;
168
+ (void)argc;
169
+ (void)argv;
170
+ xtgui::Canvas2D *canvas = offscreen_canvas(self);
171
+ if (canvas == nullptr || canvas->image().width <= 0) return XT_NULL;
172
+ return image_bitmap_handle(canvas->image());
173
+ }
174
+
175
+ METHOD(offscreen_convert_to_blob) {
176
+ (void)env;
177
+ (void)argc;
178
+ (void)argv;
179
+ xtgui::Canvas2D *canvas = offscreen_canvas(self);
180
+ if (canvas == nullptr) return xt_promise_reject(xt_string_from_cstr("InvalidStateError"));
181
+ std::vector<unsigned char> png;
182
+ if (!canvas->encode_png(&png)) return xt_promise_reject(xt_string_from_cstr("InvalidStateError"));
183
+ xt_value data = xt_array_new(0, nullptr);
184
+ for (unsigned char byte : png) xt_array_push(data, xt_number((double)byte));
185
+ xt_value blob = xt_object_new();
186
+ xt_object_set(blob, xt_string_from_cstr("type"), xt_string_from_cstr("image/png"));
187
+ xt_object_set(blob, xt_string_from_cstr("size"), xt_number((double)png.size()));
188
+ xt_object_set(blob, xt_string_from_cstr("__xt_gui_blob_data"), data);
189
+ return xt_promise_resolve(blob);
190
+ }
191
+
192
+ METHOD(canvas_transfer_control_to_offscreen) {
193
+ (void)env;
194
+ (void)argc;
195
+ (void)argv;
196
+ xt_value existing = xt_object_get_cstr(self, "__xt_gui_offscreen");
197
+ if (XT_IS_OBJECT(existing)) return existing;
198
+ XtGuiWindow *win = nullptr;
199
+ xtgui::Node *node = resolve_node(self, &win);
200
+ if (win == nullptr || node == nullptr || !node->isElement() || !node->isTag("canvas")) {
201
+ return XT_NULL;
202
+ }
203
+ if (xt_gui_webgl_find(win, node) != nullptr) return XT_NULL;
204
+ xtgui::Canvas2D *canvas = xt_gui_canvas_get(win, node);
205
+ if (canvas == nullptr) return XT_NULL;
206
+ xt_value handle = make_offscreen(canvas, win->object);
207
+ xt_object_set(self, xt_string_from_cstr("__xt_gui_offscreen"), handle);
208
+ return handle;
209
+ }
210
+
211
+ } // namespace domapi
212
+
213
+ #undef METHOD
@@ -111,6 +111,7 @@ xt_value node_proto() {
111
111
  define_method(proto, "pause", (void *)node_pause);
112
112
  define_method(proto, "load", (void *)node_load);
113
113
  define_method(proto, "getContext", (void *)node_get_context);
114
+ define_method(proto, "transferControlToOffscreen", (void *)canvas_transfer_control_to_offscreen);
114
115
  /* `toDataURL`/`toBlob` belong to the canvas *element*, as in the DOM. */
115
116
  define_method(proto, "toDataURL", (void *)canvas_to_data_url);
116
117
  define_method(proto, "toBlob", (void *)canvas_to_blob);
@@ -210,6 +211,20 @@ xt_value canvas_proto() {
210
211
  define_setter(proto, "lineWidth", (void *)canvas_line_width_set);
211
212
  define_getter(proto, "globalAlpha", (void *)canvas_global_alpha_get);
212
213
  define_setter(proto, "globalAlpha", (void *)canvas_global_alpha_set);
214
+ define_getter(proto, "globalCompositeOperation", (void *)canvas_global_composite_get);
215
+ define_setter(proto, "globalCompositeOperation", (void *)canvas_global_composite_set);
216
+ define_getter(proto, "shadowColor", (void *)canvas_shadow_color_get);
217
+ define_setter(proto, "shadowColor", (void *)canvas_shadow_color_set);
218
+ define_getter(proto, "shadowBlur", (void *)canvas_shadow_blur_get);
219
+ define_setter(proto, "shadowBlur", (void *)canvas_shadow_blur_set);
220
+ define_getter(proto, "shadowOffsetX", (void *)canvas_shadow_offset_x_get);
221
+ define_setter(proto, "shadowOffsetX", (void *)canvas_shadow_offset_x_set);
222
+ define_getter(proto, "shadowOffsetY", (void *)canvas_shadow_offset_y_get);
223
+ define_setter(proto, "shadowOffsetY", (void *)canvas_shadow_offset_y_set);
224
+ define_getter(proto, "imageSmoothingEnabled", (void *)canvas_image_smoothing_enabled_get);
225
+ define_setter(proto, "imageSmoothingEnabled", (void *)canvas_image_smoothing_enabled_set);
226
+ define_getter(proto, "imageSmoothingQuality", (void *)canvas_image_smoothing_quality_get);
227
+ define_setter(proto, "imageSmoothingQuality", (void *)canvas_image_smoothing_quality_set);
213
228
  define_getter(proto, "font", (void *)canvas_font_get);
214
229
  define_setter(proto, "font", (void *)canvas_font_set);
215
230
  define_getter(proto, "textAlign", (void *)canvas_text_align_get);
@@ -245,6 +260,9 @@ xt_value canvas_proto() {
245
260
  define_method(proto, "bezierCurveTo", (void *)canvas_bezier_curve_to);
246
261
  define_method(proto, "fill", (void *)canvas_fill);
247
262
  define_method(proto, "stroke", (void *)canvas_stroke);
263
+ define_method(proto, "clip", (void *)canvas_clip);
264
+ define_method(proto, "isPointInPath", (void *)canvas_is_point_in_path);
265
+ define_method(proto, "isPointInStroke", (void *)canvas_is_point_in_stroke);
248
266
  define_method(proto, "clearRect", (void *)canvas_clear_rect);
249
267
  define_method(proto, "fillRect", (void *)canvas_fill_rect);
250
268
  define_method(proto, "strokeRect", (void *)canvas_stroke_rect);
@@ -223,6 +223,16 @@ xt_value webgl_proto() {
223
223
  define_getter(proto, "canvas", (void *)webgl_canvas_get);
224
224
  define_getter(proto, "drawingBufferWidth", (void *)webgl_drawing_buffer_width_get);
225
225
  define_getter(proto, "drawingBufferHeight", (void *)webgl_drawing_buffer_height_get);
226
+ /* `gl.constructor.name` is how three.js tells a WebGL 2 context from a
227
+ * WebGL 1 one. A host-type value from `xt_browser_type` later replaces this
228
+ * with the real constructor (and its `prototype`), but the context must
229
+ * answer the name even when the page never read the global. */
230
+ {
231
+ xt_value constructor = xt_object_new();
232
+ xt_object_set(constructor, xt_string_from_cstr("name"),
233
+ xt_string_from_cstr("WebGLRenderingContext"));
234
+ xt_object_set(proto, xt_string_from_cstr("constructor"), constructor);
235
+ }
226
236
  g_webgl_proto = proto;
227
237
  return proto;
228
238
  }
@@ -321,15 +331,17 @@ std::vector<uint8_t> webgl_bytes(xt_value value) {
321
331
  const double length = xt_to_number(xt_get(value, xt_string_from_cstr("byteLength")));
322
332
  if (!(length > 0.0) || !xt_is_array_buffer(buffer)) return out;
323
333
  out.resize((size_t)length);
324
- for (uint32_t i = 0; i < (uint32_t)length; i++) {
325
- out[i] = xt_array_buffer_get_byte(buffer, offset + i);
326
- }
334
+ /* The runtime stores an ArrayBuffer as one byte value per array slot, so
335
+ * this is still an element loop — but one call into the runtime instead of
336
+ * one extern call per byte, which is what a multi-megabyte vertex buffer or
337
+ * `texImage2D` pays for. */
338
+ out.resize(xt_array_buffer_copy_bytes_out(buffer, offset, out.data(), (uint32_t)length));
327
339
  return out;
328
340
  }
329
341
  if (xt_is_array_buffer(value)) {
330
342
  const uint32_t length = xt_array_buffer_byte_length(value);
331
343
  out.resize(length);
332
- for (uint32_t i = 0; i < length; i++) out[i] = xt_array_buffer_get_byte(value, i);
344
+ out.resize(xt_array_buffer_copy_bytes_out(value, 0, out.data(), length));
333
345
  return out;
334
346
  }
335
347
  const std::vector<double> numbers = webgl_numbers(value);
@@ -346,13 +358,21 @@ std::vector<uint8_t> webgl_bytes(xt_value value) {
346
358
  void webgl_write_bytes(xt_value target, const uint8_t *bytes, size_t count) {
347
359
  if (!XT_IS_OBJECT(target) || bytes == nullptr) return;
348
360
  const int kind = xt_typed_array_kind_of(target);
349
- for (size_t i = 0; i < count; i++) {
350
- if (kind >= 0) {
351
- xt_typed_array_write(target, (uint32_t)i, xt_number((double)bytes[i]));
352
- } else {
353
- xt_set(target, xt_number((double)i), xt_number((double)bytes[i]));
361
+ if (kind >= 0) {
362
+ xt_value buffer = xt_typed_array_buffer_of(target);
363
+ const uint32_t offset = xt_typed_array_byte_offset_of(target);
364
+ if (xt_is_array_buffer(buffer)) {
365
+ xt_array_buffer_copy_bytes_in(buffer, offset, bytes, (uint32_t)count);
366
+ return;
354
367
  }
355
368
  }
369
+ if (xt_is_array_buffer(target)) {
370
+ xt_array_buffer_copy_bytes_in(target, 0, bytes, (uint32_t)count);
371
+ return;
372
+ }
373
+ for (size_t i = 0; i < count; i++) {
374
+ xt_set(target, xt_number((double)i), xt_number((double)bytes[i]));
375
+ }
356
376
  }
357
377
 
358
378
  /* -- context handle members ---------------------------------------------- */
@@ -121,12 +121,44 @@ xt_value uint_array(const unsigned int *values, int count) {
121
121
  xt_value webgl2_proto() {
122
122
  xt_gc_add_root(&g_webgl2_proto);
123
123
  if (XT_IS_OBJECT(g_webgl2_proto)) return g_webgl2_proto;
124
- xt_value proto = xt_object_new_with_proto(webgl_proto());
124
+ /* WebGL 2 shares the WebGL 1 surface but not its prototype: in a browser
125
+ * `gl2 instanceof WebGLRenderingContext` is false, which is how three.js and
126
+ * other libraries detect WebGL 2. Copy the WebGL 1 members instead of
127
+ * inheriting the WebGL 1 prototype. */
128
+ xt_value webgl1 = webgl_proto();
129
+ xt_value proto = xt_object_new();
130
+ xt_value keys = xt_object_keys(webgl1);
131
+ const double count = xt_to_number(xt_array_length(keys));
132
+ for (int32_t i = 0; i < (int32_t)count; i++) {
133
+ xt_value key = xt_array_get(keys, xt_number((double)i));
134
+ const char *name = xt_string_data(key);
135
+ if (name == nullptr) continue;
136
+ /* `constructor` gets the WebGL 2 name below. The three host getters are
137
+ * accessors, not values: reading them here would run them against the
138
+ * WebGL 1 prototype and copy `undefined`. Reinstall them as accessors. */
139
+ if (strcmp(name, "constructor") == 0 || strcmp(name, "canvas") == 0 ||
140
+ strcmp(name, "drawingBufferWidth") == 0 || strcmp(name, "drawingBufferHeight") == 0) {
141
+ continue;
142
+ }
143
+ xt_object_set(proto, key, xt_object_get(webgl1, key));
144
+ }
145
+ xt_object_define_getter(proto, xt_string_from_cstr("canvas"),
146
+ xt_closure_new((void *)webgl_canvas_get, 0, nullptr));
147
+ xt_object_define_getter(proto, xt_string_from_cstr("drawingBufferWidth"),
148
+ xt_closure_new((void *)webgl_drawing_buffer_width_get, 0, nullptr));
149
+ xt_object_define_getter(proto, xt_string_from_cstr("drawingBufferHeight"),
150
+ xt_closure_new((void *)webgl_drawing_buffer_height_get, 0, nullptr));
125
151
  #define XT_WEBGL2_CONSTANT(name, value) \
126
152
  xt_object_set(proto, xt_string_from_cstr(#name), xt_number((double)(value)));
127
153
  XT_WEBGL2_CONSTANTS(XT_WEBGL2_CONSTANT)
128
154
  #undef XT_WEBGL2_CONSTANT
129
155
  for (const MethodEntry &method : kMethods) define_method(proto, method.name, method.fn);
156
+ {
157
+ xt_value constructor = xt_object_new();
158
+ xt_object_set(constructor, xt_string_from_cstr("name"),
159
+ xt_string_from_cstr("WebGL2RenderingContext"));
160
+ xt_object_set(proto, xt_string_from_cstr("constructor"), constructor);
161
+ }
130
162
  g_webgl2_proto = proto;
131
163
  return proto;
132
164
  }