xbintsc 0.5.38 → 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 (117) 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/driver/version.d.ts +1 -1
  5. package/dist/src/driver/version.js +1 -1
  6. package/dist/src/extensions/gui/html.js +9 -2
  7. package/dist/src/extensions/gui/html.js.map +1 -1
  8. package/dist/tests/codegen/closures.test.js +7 -0
  9. package/dist/tests/codegen/closures.test.js.map +1 -1
  10. package/dist/tests/e2e/compile-objects-classes.test.js +31 -0
  11. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  12. package/dist/tests/e2e/gui-canvas-state.test.d.ts +9 -0
  13. package/dist/tests/e2e/gui-canvas-state.test.js +297 -0
  14. package/dist/tests/e2e/gui-canvas-state.test.js.map +1 -0
  15. package/dist/tests/e2e/gui-draw-perf.test.d.ts +22 -0
  16. package/dist/tests/e2e/gui-draw-perf.test.js +405 -0
  17. package/dist/tests/e2e/gui-draw-perf.test.js.map +1 -0
  18. package/dist/tests/e2e/gui-perf.test.d.ts +23 -0
  19. package/dist/tests/e2e/gui-perf.test.js +283 -0
  20. package/dist/tests/e2e/gui-perf.test.js.map +1 -0
  21. package/dist/tests/e2e/gui-script-import-order.test.js +9 -1
  22. package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -1
  23. package/dist/tests/e2e/gui-webgl.test.js +4 -4
  24. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  25. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.d.ts +8 -0
  26. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js +336 -0
  27. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js.map +1 -0
  28. package/dist/tests/extensions/gui.test.js +23 -0
  29. package/dist/tests/extensions/gui.test.js.map +1 -1
  30. package/doc/gui/api.md +28 -2
  31. package/doc/gui/canvas.md +110 -27
  32. package/doc/gui/known-issues.md +32 -10
  33. package/doc/gui/rendering.md +77 -0
  34. package/doc/gui/runtime.md +18 -3
  35. package/doc/gui/webgl.md +38 -19
  36. package/doc/gui.md +33 -1
  37. package/doc/zh-CN/gui/api.md +25 -2
  38. package/doc/zh-CN/gui/canvas.md +95 -26
  39. package/doc/zh-CN/gui/known-issues.md +21 -4
  40. package/doc/zh-CN/gui/rendering.md +59 -0
  41. package/doc/zh-CN/gui/runtime.md +13 -3
  42. package/doc/zh-CN/gui/webgl.md +27 -5
  43. package/doc/zh-CN/gui.md +25 -2
  44. package/package.json +7 -7
  45. package/runtime/ext_gui/browser.cpp +3 -1
  46. package/runtime/ext_gui/canvas.cpp +135 -89
  47. package/runtime/ext_gui/canvas.h +116 -0
  48. package/runtime/ext_gui/canvas_clip.cpp +143 -0
  49. package/runtime/ext_gui/canvas_composite.cpp +241 -0
  50. package/runtime/ext_gui/canvas_coverage.cpp +241 -0
  51. package/runtime/ext_gui/canvas_gradient.cpp +41 -4
  52. package/runtime/ext_gui/canvas_image_data.cpp +4 -0
  53. package/runtime/ext_gui/canvas_internal.h +34 -9
  54. package/runtime/ext_gui/canvas_raster.cpp +207 -178
  55. package/runtime/ext_gui/canvas_shadow.cpp +161 -0
  56. package/runtime/ext_gui/canvas_stroke.cpp +37 -9
  57. package/runtime/ext_gui/canvas_style.cpp +59 -0
  58. package/runtime/ext_gui/canvas_text.cpp +140 -0
  59. package/runtime/ext_gui/css.h +7 -0
  60. package/runtime/ext_gui/css_sheet.cpp +22 -0
  61. package/runtime/ext_gui/document.cpp +25 -1
  62. package/runtime/ext_gui/document.h +8 -0
  63. package/runtime/ext_gui/dom_api_canvas.cpp +16 -1
  64. package/runtime/ext_gui/dom_api_canvas_image.cpp +9 -0
  65. package/runtime/ext_gui/dom_api_canvas_state.cpp +217 -0
  66. package/runtime/ext_gui/dom_api_internal.h +36 -37
  67. package/runtime/ext_gui/dom_api_offscreen.cpp +213 -0
  68. package/runtime/ext_gui/dom_api_proto.cpp +18 -0
  69. package/runtime/ext_gui/dom_api_webgl.cpp +19 -9
  70. package/runtime/ext_gui/dom_api_webgl_internal.h +58 -0
  71. package/runtime/ext_gui/dom_api_webgl_source.cpp +18 -0
  72. package/runtime/ext_gui/gradient_values.cpp +12 -1
  73. package/runtime/ext_gui/gui.cpp +5 -0
  74. package/runtime/ext_gui/gui_canvas.cpp +4 -0
  75. package/runtime/ext_gui/gui_engine.h +30 -0
  76. package/runtime/ext_gui/gui_events.cpp +182 -58
  77. package/runtime/ext_gui/gui_media.cpp +3 -0
  78. package/runtime/ext_gui/image.cpp +69 -0
  79. package/runtime/ext_gui/image_bitmap_browser.cpp +11 -0
  80. package/runtime/ext_gui/layout.cpp +4 -0
  81. package/runtime/ext_gui/paint.cpp +19 -0
  82. package/runtime/ext_gui/paint_background.cpp +18 -1
  83. package/runtime/ext_gui/perf.cpp +171 -0
  84. package/runtime/ext_gui/perf.h +127 -0
  85. package/runtime/ext_gui/renderer.cpp +109 -41
  86. package/runtime/ext_gui/renderer.h +33 -9
  87. package/runtime/ext_gui/renderer_image.cpp +135 -65
  88. package/runtime/ext_gui/renderer_internal.h +15 -3
  89. package/runtime/ext_gui/renderer_text.cpp +24 -12
  90. package/runtime/ext_gui/style.cpp +101 -30
  91. package/runtime/ext_gui/style.h +6 -3
  92. package/runtime/ext_gui/style_print.cpp +7 -1
  93. package/runtime/ext_gui/text.cpp +23 -4
  94. package/runtime/ext_gui/text.h +29 -0
  95. package/runtime/ext_gui/text_cache.cpp +118 -0
  96. package/runtime/ext_gui/text_glyph_cache.cpp +100 -0
  97. package/runtime/ext_gui/webgl.h +48 -146
  98. package/runtime/ext_gui/webgl_blit.cpp +12 -1
  99. package/runtime/ext_gui/webgl_blit_depth.cpp +270 -0
  100. package/runtime/ext_gui/webgl_buffer.cpp +59 -1
  101. package/runtime/ext_gui/webgl_buffer_objects.cpp +106 -9
  102. package/runtime/ext_gui/webgl_clear.cpp +3 -0
  103. package/runtime/ext_gui/webgl_context.cpp +29 -3
  104. package/runtime/ext_gui/webgl_draw.cpp +75 -270
  105. package/runtime/ext_gui/webgl_draw_buffers.cpp +1 -0
  106. package/runtime/ext_gui/webgl_draw_setup.cpp +321 -0
  107. package/runtime/ext_gui/webgl_draw_state.h +130 -0
  108. package/runtime/ext_gui/webgl_glsl_scan.cpp +3 -8
  109. package/runtime/ext_gui/webgl_objects.h +148 -0
  110. package/runtime/ext_gui/webgl_pipeline.cpp +8 -8
  111. package/runtime/ext_gui/webgl_program_internal.h +35 -0
  112. package/runtime/ext_gui/webgl_shader.cpp +22 -30
  113. package/runtime/ext_gui/webgl_webgl2.cpp +36 -0
  114. package/runtime/ext_gui/window.cpp +56 -1
  115. package/src/codegen/generator/module.ts +22 -7
  116. package/src/driver/version.ts +1 -1
  117. package/src/extensions/gui/html.ts +9 -2
@@ -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);
@@ -331,15 +331,17 @@ std::vector<uint8_t> webgl_bytes(xt_value value) {
331
331
  const double length = xt_to_number(xt_get(value, xt_string_from_cstr("byteLength")));
332
332
  if (!(length > 0.0) || !xt_is_array_buffer(buffer)) return out;
333
333
  out.resize((size_t)length);
334
- for (uint32_t i = 0; i < (uint32_t)length; i++) {
335
- out[i] = xt_array_buffer_get_byte(buffer, offset + i);
336
- }
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));
337
339
  return out;
338
340
  }
339
341
  if (xt_is_array_buffer(value)) {
340
342
  const uint32_t length = xt_array_buffer_byte_length(value);
341
343
  out.resize(length);
342
- 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));
343
345
  return out;
344
346
  }
345
347
  const std::vector<double> numbers = webgl_numbers(value);
@@ -356,13 +358,21 @@ std::vector<uint8_t> webgl_bytes(xt_value value) {
356
358
  void webgl_write_bytes(xt_value target, const uint8_t *bytes, size_t count) {
357
359
  if (!XT_IS_OBJECT(target) || bytes == nullptr) return;
358
360
  const int kind = xt_typed_array_kind_of(target);
359
- for (size_t i = 0; i < count; i++) {
360
- if (kind >= 0) {
361
- xt_typed_array_write(target, (uint32_t)i, xt_number((double)bytes[i]));
362
- } else {
363
- 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;
364
367
  }
365
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
+ }
366
376
  }
367
377
 
368
378
  /* -- context handle members ---------------------------------------------- */
@@ -0,0 +1,58 @@
1
+ /*
2
+ * xbintsc GUI engine — helpers shared by the WebGL DOM bindings.
3
+ *
4
+ * Split out of `dom_api_internal.h` to keep that header under the repository's
5
+ * file-length budget; `dom_api_internal.h` includes this file, so every WebGL
6
+ * binding sees the same declarations.
7
+ */
8
+ #ifndef XT_GUI_DOM_API_WEBGL_INTERNAL_H
9
+ #define XT_GUI_DOM_API_WEBGL_INTERNAL_H
10
+
11
+ #include <cstdint>
12
+ #include <string>
13
+ #include <vector>
14
+
15
+ #include "gui_engine.h"
16
+
17
+ namespace domapi {
18
+
19
+ /** The engine WebGL context behind a context handle, or null. */
20
+ xtgui::WebGLContext *webgl_from_context(xt_value self);
21
+ /** Like `webgl_from_context`, but null while the context is lost: every GL
22
+ * entry point except `getError`/`isContextLost` is a no-op in that state. */
23
+ xtgui::WebGLContext *webgl_live_context(xt_value self);
24
+ /** The numeric context id stored on a context handle (0 when absent). */
25
+ uint64_t webgl_context_id(xt_value self);
26
+ /** Create the JavaScript handle for a canvas's WebGL context. */
27
+ xt_value webgl_context_handle(xt_value element, xtgui::WebGLContext *context);
28
+ /** The id stored on an object handle under `key`, or 0. */
29
+ uint32_t webgl_object_id(xt_value handle, const char *key);
30
+ /** Build an object handle (`{__xt_gl_context, __xt_gl_<key>}`). */
31
+ xt_value webgl_make_object(xt_value context, const char *key, uint32_t id,
32
+ const std::string &kind);
33
+ double webgl_number(int32_t argc, xt_value *argv, int32_t index, double fallback);
34
+ int webgl_int(int32_t argc, xt_value *argv, int32_t index, int fallback);
35
+ std::string webgl_string(int32_t argc, xt_value *argv, int32_t index);
36
+ /** Read an array/typed array of numbers. */
37
+ std::vector<double> webgl_numbers(xt_value value);
38
+ /** Read the raw bytes of a typed array / ArrayBuffer (or a byte array). */
39
+ std::vector<uint8_t> webgl_bytes(xt_value value);
40
+ /** Store `count` bytes into a typed array / array handle. */
41
+ void webgl_write_bytes(xt_value target, const uint8_t *bytes, size_t count);
42
+ /** Convert a source pixel array (RGBA/RGB/LUMINANCE/…) to RGBA8. */
43
+ void webgl_convert_pixels(const uint8_t *source, int count, int format, bool premultiply,
44
+ std::vector<uint8_t> *out);
45
+ /** Decode a `texImage2D` source (`<img>`, `<canvas>`, `<video>`, `ImageData`,
46
+ * `ImageBitmap`) into top-down RGBA8. */
47
+ bool webgl_read_source(xt_value source, int *width, int *height,
48
+ std::vector<uint8_t> *rgba, bool premultiply);
49
+ /** Read the `UNPACK_FLIP_Y_WEBGL` / `UNPACK_PREMULTIPLY_ALPHA_WEBGL` flags set
50
+ * through `pixelStorei`. */
51
+ void webgl_unpack_flags(xt_value context, bool *flip_y, bool *premultiply);
52
+ /** Extension object for a compressed-texture family (null when unsupported,
53
+ * undefined when the name is not one of those extensions). */
54
+ xt_value webgl_compressed_extension(const std::string &name, xtgui::WebGLContext *context);
55
+
56
+ } // namespace domapi
57
+
58
+ #endif /* XT_GUI_DOM_API_WEBGL_INTERNAL_H */