xbintsc 0.5.32 → 0.5.37

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 (222) 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 +18 -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-browser.test.js +65 -0
  38. package/dist/tests/e2e/gui-browser.test.js.map +1 -1
  39. package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
  40. package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
  41. package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
  42. package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
  43. package/dist/tests/e2e/gui-dom.test.js +215 -0
  44. package/dist/tests/e2e/gui-dom.test.js.map +1 -1
  45. package/dist/tests/e2e/gui-events.test.js +112 -0
  46. package/dist/tests/e2e/gui-events.test.js.map +1 -1
  47. package/dist/tests/e2e/gui-helpers.d.ts +7 -0
  48. package/dist/tests/e2e/gui-helpers.js +1 -0
  49. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  50. package/dist/tests/e2e/gui-media.test.js +78 -0
  51. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  52. package/dist/tests/e2e/gui-style.test.js +55 -0
  53. package/dist/tests/e2e/gui-style.test.js.map +1 -1
  54. package/dist/tests/e2e/gui-webgl.test.js +258 -0
  55. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  56. package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
  57. package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
  58. package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
  59. package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
  60. package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
  61. package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
  62. package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
  63. package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
  64. package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
  66. package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
  67. package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
  69. package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
  70. package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
  71. package/dist/tests/e2e/gui-webgl2.test.js +45 -1
  72. package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
  73. package/dist/tests/extensions/gui-index.test.js +1 -0
  74. package/dist/tests/extensions/gui-index.test.js.map +1 -1
  75. package/dist/tests/extensions/media.test.js +1 -0
  76. package/dist/tests/extensions/media.test.js.map +1 -1
  77. package/dist/tests/parser/expressions.test.js +30 -0
  78. package/dist/tests/parser/expressions.test.js.map +1 -1
  79. package/dist/tests/platform-tarball.test.d.ts +1 -0
  80. package/dist/tests/platform-tarball.test.js +111 -0
  81. package/dist/tests/platform-tarball.test.js.map +1 -0
  82. package/dist/tests/version.test.js +6 -0
  83. package/dist/tests/version.test.js.map +1 -1
  84. package/doc/ai/contributing.md +11 -0
  85. package/doc/ai/extensions.md +7 -4
  86. package/doc/ai/troubleshooting.md +7 -5
  87. package/doc/ai/zh-CN/contributing.md +9 -0
  88. package/doc/ai/zh-CN/extensions.md +4 -2
  89. package/doc/ai/zh-CN/troubleshooting.md +5 -3
  90. package/doc/gui/api.md +99 -22
  91. package/doc/gui/build.md +11 -4
  92. package/doc/gui/canvas.md +49 -18
  93. package/doc/gui/dom.md +57 -7
  94. package/doc/gui/media.md +4 -0
  95. package/doc/gui/webgl.md +105 -46
  96. package/doc/gui.md +7 -3
  97. package/doc/npm-package.md +55 -27
  98. package/doc/requirements.md +6 -2
  99. package/doc/zh-CN/gui/api.md +84 -14
  100. package/doc/zh-CN/gui/build.md +7 -2
  101. package/doc/zh-CN/gui/canvas.md +44 -19
  102. package/doc/zh-CN/gui/dom.md +48 -4
  103. package/doc/zh-CN/gui/media.md +3 -0
  104. package/doc/zh-CN/gui/webgl.md +22 -16
  105. package/doc/zh-CN/gui.md +3 -2
  106. package/doc/zh-CN/npm-package.md +44 -23
  107. package/doc/zh-CN/requirements.md +4 -1
  108. package/package.json +17 -1
  109. package/runtime/ext_gui/audio.cpp +365 -153
  110. package/runtime/ext_gui/audio_buffer.cpp +166 -0
  111. package/runtime/ext_gui/audio_buffer.h +37 -0
  112. package/runtime/ext_gui/audio_decode.cpp +219 -0
  113. package/runtime/ext_gui/audio_decode.h +39 -0
  114. package/runtime/ext_gui/audio_device.cpp +87 -0
  115. package/runtime/ext_gui/audio_effects.cpp +163 -0
  116. package/runtime/ext_gui/audio_effects.h +25 -0
  117. package/runtime/ext_gui/audio_internal.h +30 -0
  118. package/runtime/ext_gui/audio_mixer.cpp +494 -0
  119. package/runtime/ext_gui/audio_mixer.h +238 -0
  120. package/runtime/ext_gui/audio_param.cpp +104 -0
  121. package/runtime/ext_gui/audio_param_object.cpp +155 -0
  122. package/runtime/ext_gui/browser.cpp +45 -50
  123. package/runtime/ext_gui/browser_abort.cpp +249 -0
  124. package/runtime/ext_gui/browser_dialogs.cpp +76 -0
  125. package/runtime/ext_gui/browser_media.cpp +176 -0
  126. package/runtime/ext_gui/browser_navigation.cpp +130 -0
  127. package/runtime/ext_gui/canvas.cpp +88 -30
  128. package/runtime/ext_gui/canvas.h +74 -9
  129. package/runtime/ext_gui/canvas_image_data.cpp +143 -0
  130. package/runtime/ext_gui/canvas_internal.h +8 -5
  131. package/runtime/ext_gui/canvas_pattern.cpp +90 -0
  132. package/runtime/ext_gui/canvas_raster.cpp +31 -9
  133. package/runtime/ext_gui/canvas_style.cpp +4 -2
  134. package/runtime/ext_gui/css_selector.cpp +21 -3
  135. package/runtime/ext_gui/dom.h +4 -0
  136. package/runtime/ext_gui/dom_api.cpp +1 -0
  137. package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
  138. package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
  139. package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
  140. package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
  141. package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
  142. package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
  143. package/runtime/ext_gui/dom_api_document.cpp +58 -5
  144. package/runtime/ext_gui/dom_api_events.cpp +93 -17
  145. package/runtime/ext_gui/dom_api_internal.h +71 -2
  146. package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
  147. package/runtime/ext_gui/dom_api_node.cpp +40 -19
  148. package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
  149. package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
  150. package/runtime/ext_gui/dom_api_proto.cpp +35 -0
  151. package/runtime/ext_gui/dom_api_style.cpp +223 -13
  152. package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
  153. package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
  154. package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
  155. package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
  156. package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
  157. package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
  158. package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
  159. package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
  160. package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
  161. package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
  162. package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
  163. package/runtime/ext_gui/gui.cpp +103 -14
  164. package/runtime/ext_gui/gui_engine.h +56 -4
  165. package/runtime/ext_gui/gui_events.cpp +80 -9
  166. package/runtime/ext_gui/image_bitmap.cpp +28 -0
  167. package/runtime/ext_gui/image_bitmap.h +26 -0
  168. package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
  169. package/runtime/ext_gui/listener_options.h +43 -0
  170. package/runtime/ext_gui/media.cpp +32 -0
  171. package/runtime/ext_gui/media.h +13 -0
  172. package/runtime/ext_gui/media_api.h +23 -0
  173. package/runtime/ext_gui/paint.cpp +28 -0
  174. package/runtime/ext_gui/paint.h +4 -0
  175. package/runtime/ext_gui/webgl.h +152 -18
  176. package/runtime/ext_gui/webgl_blit.cpp +95 -0
  177. package/runtime/ext_gui/webgl_buffer.cpp +178 -263
  178. package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
  179. package/runtime/ext_gui/webgl_clear.cpp +142 -0
  180. package/runtime/ext_gui/webgl_compressed.cpp +180 -0
  181. package/runtime/ext_gui/webgl_context.cpp +241 -156
  182. package/runtime/ext_gui/webgl_draw.cpp +68 -3
  183. package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
  184. package/runtime/ext_gui/webgl_glsl.h +40 -0
  185. package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
  186. package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
  187. package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
  188. package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
  189. package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
  190. package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
  191. package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
  192. package/runtime/ext_gui/webgl_program_internal.h +6 -0
  193. package/runtime/ext_gui/webgl_shader.cpp +13 -1
  194. package/runtime/ext_gui/webgl_state.cpp +228 -0
  195. package/runtime/ext_gui/webgl_texture.cpp +144 -17
  196. package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
  197. package/runtime/ext_gui/webgl_uniform_block.h +31 -0
  198. package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
  199. package/runtime/ext_gui/window.cpp +14 -65
  200. package/runtime/ext_gui/window_events.cpp +125 -0
  201. package/runtime/ext_media/decode_audio.cpp +308 -0
  202. package/runtime/ext_media/media_bridge.c +2 -0
  203. package/runtime/rt.h +43 -0
  204. package/runtime/rt_internal.h +4 -0
  205. package/runtime/xt_array_buffer.c +30 -0
  206. package/runtime/xt_builtins.c +0 -21
  207. package/runtime/xt_containers/access.inc +94 -2
  208. package/runtime/xt_containers/objects.inc +29 -31
  209. package/runtime/xt_typed_array/elements.inc +13 -0
  210. package/scripts/build-media.ts +14 -6
  211. package/scripts/build-runtime.ts +21 -16
  212. package/scripts/check-version.ts +39 -9
  213. package/scripts/package-platform.ts +183 -0
  214. package/scripts/set-platform-version.ts +62 -0
  215. package/scripts/verify-platform-package.ts +182 -0
  216. package/src/cli/main.ts +5 -1
  217. package/src/driver/runtime-lib.ts +73 -9
  218. package/src/driver/version.ts +1 -1
  219. package/src/extensions/gui/index.ts +18 -5
  220. package/src/extensions/media/index.ts +4 -3
  221. package/src/parser/context.ts +99 -4
  222. package/src/parser/literals.ts +6 -12
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  #include "dom_api_internal.h"
7
+ #include "listener_options.h"
7
8
 
8
9
  #include <algorithm>
9
10
  #include <cmath>
@@ -217,13 +218,19 @@ METHOD(node_get_bounding_rect) {
217
218
  if (node == nullptr || win == nullptr || win->document == nullptr) return XT_UNDEFINED;
218
219
  const xtgui::LayoutBox *box = win->document->boxOf(node);
219
220
  if (box == nullptr) return XT_UNDEFINED;
220
- /* Like the DOM: the rectangle of the *transformed* border box. */
221
+ /* Like the DOM: the rectangle of the *transformed* border box. The legacy
222
+ * `top`/`right`/`bottom`/`left` members are the same rectangle in the other
223
+ * spelling; libraries read both. */
221
224
  xtgui::LayoutRect rect = xtgui::xt_layout_transformed_bounds(box);
222
225
  xt_value object = xt_object_new();
223
226
  xt_object_set(object, xt_string_from_cstr("x"), xt_number(rect.x));
224
227
  xt_object_set(object, xt_string_from_cstr("y"), xt_number(rect.y));
225
228
  xt_object_set(object, xt_string_from_cstr("width"), xt_number(rect.width));
226
229
  xt_object_set(object, xt_string_from_cstr("height"), xt_number(rect.height));
230
+ xt_object_set(object, xt_string_from_cstr("top"), xt_number(rect.y));
231
+ xt_object_set(object, xt_string_from_cstr("right"), xt_number(rect.x + rect.width));
232
+ xt_object_set(object, xt_string_from_cstr("bottom"), xt_number(rect.y + rect.height));
233
+ xt_object_set(object, xt_string_from_cstr("left"), xt_number(rect.x));
227
234
  return object;
228
235
  }
229
236
 
@@ -252,12 +259,30 @@ METHOD(node_contains) {
252
259
 
253
260
  METHOD(node_focus) {
254
261
  (void)env;
255
- (void)argc;
256
- (void)argv;
257
262
  XtGuiWindow *win = nullptr;
258
263
  xtgui::Node *node = resolve_node(self, &win);
259
264
  if (node == nullptr || win == nullptr || win->document == nullptr) return XT_UNDEFINED;
265
+ /* `{ preventScroll: true }` keeps the current page offset; otherwise the
266
+ * focused box is scrolled into view, like the browser default. */
267
+ bool prevent_scroll = false;
268
+ xt_value options = xt_arg(argc, argv, 0);
269
+ if (XT_IS_OBJECT(options)) {
270
+ prevent_scroll = value_truthy(xt_object_get_cstr(options, "preventScroll"));
271
+ }
260
272
  if (win->document->setFocus(node)) win->geometry.dirty = 1;
273
+ const xtgui::LayoutBox *box = win->document->boxOf(node);
274
+ if (!prevent_scroll && box != nullptr) {
275
+ float viewport_width = 0;
276
+ float viewport_height = 0;
277
+ xt_gui_window_viewport(win, &viewport_width, &viewport_height);
278
+ float x = win->scroll_x;
279
+ float y = win->scroll_y;
280
+ if (box->x < x) x = box->x;
281
+ else if (box->x + box->width > x + viewport_width) x = box->x + box->width - viewport_width;
282
+ if (box->y < y) y = box->y;
283
+ else if (box->y + box->height > y + viewport_height) y = box->y + box->height - viewport_height;
284
+ xt_gui_scroll_to(win, x, y);
285
+ }
261
286
  return XT_UNDEFINED;
262
287
  }
263
288
 
@@ -290,20 +315,18 @@ METHOD(node_add_event_listener) {
290
315
  if (node == nullptr || win == nullptr || argc < 2) return self;
291
316
  xt_value fn = xt_arg(argc, argv, 1);
292
317
  if (!XT_IS_FUNCTION(fn)) return self;
293
- bool capture = false;
294
- bool once = false;
295
- xt_value options = xt_arg(argc, argv, 2);
296
- if (XT_IS_OBJECT(options)) {
297
- capture = value_truthy(xt_object_get_cstr(options, "capture"));
298
- once = value_truthy(xt_object_get_cstr(options, "once"));
299
- } else if (XT_IS_BOOL(options)) {
300
- capture = value_truthy(options);
301
- }
318
+ XtGuiListenerOptions options = xt_gui_listener_options(xt_arg(argc, argv, 2));
319
+ /* `{ signal: controller.signal }` removes the listener: an already-aborted
320
+ * signal means it was never installed, and a later abort is honoured at
321
+ * dispatch time (see `call_node_listeners`). */
322
+ if (xt_gui_signal_aborted(options.signal)) return self;
302
323
  XtGuiNodeListener listener;
303
324
  listener.type = to_string_value(xt_arg(argc, argv, 0));
304
325
  listener.fn = fn;
305
- listener.once = once;
306
- listener.capture = capture;
326
+ listener.once = options.once;
327
+ listener.capture = options.capture;
328
+ listener.passive = options.passive;
329
+ listener.signal = options.signal;
307
330
  win->node_listeners[node].push_back(listener);
308
331
  return self;
309
332
  }
@@ -313,11 +336,9 @@ METHOD(node_remove_event_listener) {
313
336
  XtGuiWindow *win = nullptr;
314
337
  xtgui::Node *node = resolve_node(self, &win);
315
338
  if (node == nullptr || win == nullptr || argc < 2) return self;
316
- bool capture = false;
317
- xt_value options = xt_arg(argc, argv, 2);
318
- if (XT_IS_OBJECT(options)) capture = value_truthy(xt_object_get_cstr(options, "capture"));
319
- else if (XT_IS_BOOL(options)) capture = value_truthy(options);
320
- remove_listener(win, node, to_string_value(xt_arg(argc, argv, 0)), xt_arg(argc, argv, 1), capture);
339
+ XtGuiListenerOptions options = xt_gui_listener_options(xt_arg(argc, argv, 2));
340
+ remove_listener(win, node, to_string_value(xt_arg(argc, argv, 0)), xt_arg(argc, argv, 1),
341
+ options.capture);
321
342
  return self;
322
343
  }
323
344
 
@@ -34,7 +34,10 @@ METHOD(node_tag_name_get) {
34
34
  XtGuiWindow *win = nullptr;
35
35
  xtgui::Node *node = resolve_node(self, &win);
36
36
  if (node == nullptr || !node->isElement()) return XT_UNDEFINED;
37
- return xt_string_from_cstr(to_upper(node->tag).c_str());
37
+ /* `tagName` uppercases HTML element names but keeps the authored case in a
38
+ * foreign namespace (SVG's `linearGradient`, `clipPath`, …). */
39
+ const bool html = node->ns.empty() || node->ns == "http://www.w3.org/1999/xhtml";
40
+ return xt_string_from_cstr(html ? to_upper(node->tag).c_str() : node->tag.c_str());
38
41
  }
39
42
 
40
43
  METHOD(node_node_type_get) {
@@ -58,6 +61,38 @@ METHOD(node_id_get) {
58
61
  return xt_string_from_cstr(value != nullptr ? value->c_str() : "");
59
62
  }
60
63
 
64
+ /** `namespaceURI`: the namespace passed to `createElementNS`, or the HTML
65
+ * namespace for an ordinary element. */
66
+ METHOD(node_namespace_uri_get) {
67
+ (void)env;
68
+ (void)argc;
69
+ (void)argv;
70
+ xtgui::Node *node = resolve_node(self, nullptr);
71
+ if (node == nullptr || !node->isElement()) return XT_NULL;
72
+ if (!node->ns.empty()) return xt_string_from_cstr(node->ns.c_str());
73
+ return xt_string_from_cstr("http://www.w3.org/1999/xhtml");
74
+ }
75
+
76
+ METHOD(node_local_name_get) {
77
+ (void)env;
78
+ (void)argc;
79
+ (void)argv;
80
+ xtgui::Node *node = resolve_node(self, nullptr);
81
+ if (node == nullptr) return XT_UNDEFINED;
82
+ if (!node->isElement()) return XT_NULL;
83
+ return xt_string_from_cstr(node->tag.c_str());
84
+ }
85
+
86
+ METHOD(node_prefix_get) {
87
+ (void)env;
88
+ (void)argc;
89
+ (void)argv;
90
+ xtgui::Node *node = resolve_node(self, nullptr);
91
+ if (node == nullptr) return XT_UNDEFINED;
92
+ if (node->ns_prefix.empty()) return XT_NULL;
93
+ return xt_string_from_cstr(node->ns_prefix.c_str());
94
+ }
95
+
61
96
  METHOD(node_id_set) {
62
97
  (void)env;
63
98
  XtGuiWindow *win = nullptr;
@@ -295,6 +330,7 @@ METHOD(node_style_get) {
295
330
  if (XT_IS_OBJECT(cached)) return cached;
296
331
  xt_value style = xt_object_new_with_proto(style_proto());
297
332
  xt_object_set(style, xt_string_from_cstr("__xt_gui_node"), self);
333
+ install_style_hooks(style);
298
334
  xt_object_set(self, xt_string_from_cstr("__xt_gui_style"), style);
299
335
  return style;
300
336
  }
@@ -0,0 +1,358 @@
1
+ /*
2
+ * xbintsc GUI engine — `Path2D`.
3
+ *
4
+ * A path is stored on the runtime object itself as an array of subpaths, each a
5
+ * flat number list `[x0, y0, x1, y1, …]` (user space). Keeping the geometry in
6
+ * the JS heap means the collector owns it: a page that builds a path per frame
7
+ * does not grow a native registry. `ctx.fill(path)` / `ctx.stroke(path)` read it
8
+ * back with `path2d_subpaths` and draw it under the current transform.
9
+ */
10
+
11
+ #include "dom_api_internal.h"
12
+
13
+ #include <cmath>
14
+ #include <string>
15
+ #include <vector>
16
+
17
+ #include "canvas.h"
18
+
19
+ namespace domapi {
20
+
21
+ namespace {
22
+
23
+ xt_value g_path2d_proto = XT_UNDEFINED;
24
+ constexpr float kPi = 3.14159265358979323846f;
25
+
26
+ #define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
27
+
28
+ xt_value path_data(xt_value self, bool create) {
29
+ xt_value data = xt_object_get_cstr(self, "__xt_gui_path_data");
30
+ if (XT_IS_ARRAY(data) || !create) return data;
31
+ data = xt_array_new(0, nullptr);
32
+ xt_object_set(self, xt_string_from_cstr("__xt_gui_path_data"), data);
33
+ return data;
34
+ }
35
+
36
+ xt_value current_subpath(xt_value self, bool create) {
37
+ xt_value data = path_data(self, create);
38
+ if (!XT_IS_ARRAY(data)) return XT_UNDEFINED;
39
+ int32_t count = (int32_t)xt_to_number(xt_array_length(data));
40
+ if (count > 0) return xt_array_get(data, xt_number((double)(count - 1)));
41
+ if (!create) return XT_UNDEFINED;
42
+ xt_value subpath = xt_array_new(0, nullptr);
43
+ xt_array_push(data, subpath);
44
+ return subpath;
45
+ }
46
+
47
+ void push_point(xt_value subpath, float x, float y) {
48
+ xt_array_push(subpath, xt_number(x));
49
+ xt_array_push(subpath, xt_number(y));
50
+ }
51
+
52
+ float number_arg(int32_t argc, xt_value *argv, int32_t index, double fallback) {
53
+ if (index >= argc) return (float)fallback;
54
+ return (float)xt_to_number(xt_arg(argc, argv, index));
55
+ }
56
+
57
+ bool last_point(xt_value subpath, float *x, float *y) {
58
+ int32_t count = (int32_t)xt_to_number(xt_array_length(subpath));
59
+ if (count < 2) return false;
60
+ *x = (float)xt_to_number(xt_array_get(subpath, xt_number((double)(count - 2))));
61
+ *y = (float)xt_to_number(xt_array_get(subpath, xt_number((double)(count - 1))));
62
+ return true;
63
+ }
64
+
65
+ /** Append a flattened polyline, starting a new subpath when none is open. */
66
+ void append_points(xt_value self, const std::vector<float> &flat) {
67
+ if (flat.size() < 2) return;
68
+ xt_value subpath = current_subpath(self, true);
69
+ float lx = 0.0f, ly = 0.0f;
70
+ size_t start = 0;
71
+ if (last_point(subpath, &lx, &ly)) {
72
+ if (lx != flat[0] || ly != flat[1]) push_point(subpath, flat[0], flat[1]);
73
+ start = 2;
74
+ }
75
+ for (size_t i = start; i + 1 < flat.size(); i += 2) push_point(subpath, flat[i], flat[i + 1]);
76
+ }
77
+
78
+ void flatten_arc(std::vector<float> *out, float cx, float cy, float rx, float ry, float rotation,
79
+ float start, float sweep) {
80
+ if (sweep == 0.0f) return;
81
+ int segments = (int)std::ceil(std::fabs(sweep) / (kPi / 32.0f));
82
+ if (segments < 1) segments = 1;
83
+ if (segments > 2048) segments = 2048;
84
+ const float cosRotation = std::cos(rotation);
85
+ const float sinRotation = std::sin(rotation);
86
+ for (int i = 0; i <= segments; i++) {
87
+ float angle = start + sweep * ((float)i / (float)segments);
88
+ float px = rx * std::cos(angle);
89
+ float py = ry * std::sin(angle);
90
+ out->push_back(cx + px * cosRotation - py * sinRotation);
91
+ out->push_back(cy + px * sinRotation + py * cosRotation);
92
+ }
93
+ }
94
+
95
+ void normalize_sweep(float *sweep, bool counterclockwise) {
96
+ if (counterclockwise) {
97
+ while (*sweep > 0.0f) *sweep -= 2.0f * kPi;
98
+ while (*sweep < -2.0f * kPi) *sweep += 2.0f * kPi;
99
+ } else {
100
+ while (*sweep < 0.0f) *sweep += 2.0f * kPi;
101
+ while (*sweep > 2.0f * kPi) *sweep -= 2.0f * kPi;
102
+ }
103
+ }
104
+
105
+ METHOD(path2d_move_to) {
106
+ (void)env;
107
+ xt_value data = path_data(self, true);
108
+ xt_value subpath = xt_array_new(0, nullptr);
109
+ push_point(subpath, number_arg(argc, argv, 0, 0.0), number_arg(argc, argv, 1, 0.0));
110
+ xt_array_push(data, subpath);
111
+ return XT_UNDEFINED;
112
+ }
113
+
114
+ METHOD(path2d_line_to) {
115
+ (void)env;
116
+ xt_value subpath = current_subpath(self, true);
117
+ push_point(subpath, number_arg(argc, argv, 0, 0.0), number_arg(argc, argv, 1, 0.0));
118
+ return XT_UNDEFINED;
119
+ }
120
+
121
+ METHOD(path2d_close_path) {
122
+ (void)env;
123
+ (void)argc;
124
+ (void)argv;
125
+ xt_value subpath = current_subpath(self, false);
126
+ if (!XT_IS_ARRAY(subpath)) return XT_UNDEFINED;
127
+ int32_t count = (int32_t)xt_to_number(xt_array_length(subpath));
128
+ if (count < 4) return XT_UNDEFINED;
129
+ float firstX = (float)xt_to_number(xt_array_get(subpath, xt_number(0)));
130
+ float firstY = (float)xt_to_number(xt_array_get(subpath, xt_number(1)));
131
+ float lx = 0.0f, ly = 0.0f;
132
+ if (last_point(subpath, &lx, &ly) && (lx != firstX || ly != firstY)) {
133
+ push_point(subpath, firstX, firstY);
134
+ }
135
+ return XT_UNDEFINED;
136
+ }
137
+
138
+ METHOD(path2d_rect) {
139
+ (void)env;
140
+ float x = number_arg(argc, argv, 0, 0.0);
141
+ float y = number_arg(argc, argv, 1, 0.0);
142
+ float w = number_arg(argc, argv, 2, 0.0);
143
+ float h = number_arg(argc, argv, 3, 0.0);
144
+ xt_value data = path_data(self, true);
145
+ xt_value subpath = xt_array_new(0, nullptr);
146
+ push_point(subpath, x, y);
147
+ push_point(subpath, x + w, y);
148
+ push_point(subpath, x + w, y + h);
149
+ push_point(subpath, x, y + h);
150
+ push_point(subpath, x, y);
151
+ xt_array_push(data, subpath);
152
+ return XT_UNDEFINED;
153
+ }
154
+
155
+ METHOD(path2d_round_rect) {
156
+ (void)env;
157
+ float x = number_arg(argc, argv, 0, 0.0);
158
+ float y = number_arg(argc, argv, 1, 0.0);
159
+ float w = number_arg(argc, argv, 2, 0.0);
160
+ float h = number_arg(argc, argv, 3, 0.0);
161
+ float radius = number_arg(argc, argv, 4, 0.0);
162
+ if (w < 0.0f) {
163
+ x += w;
164
+ w = -w;
165
+ }
166
+ if (h < 0.0f) {
167
+ y += h;
168
+ h = -h;
169
+ }
170
+ float maxRadius = std::min(w, h) * 0.5f;
171
+ if (radius < 0.0f) radius = 0.0f;
172
+ if (radius > maxRadius) radius = maxRadius;
173
+ xt_value data = path_data(self, true);
174
+ xt_value subpath = xt_array_new(0, nullptr);
175
+ std::vector<float> points;
176
+ flatten_arc(&points, x + w - radius, y + radius, radius, radius, 0.0f, -kPi * 0.5f, kPi * 0.5f);
177
+ flatten_arc(&points, x + w - radius, y + h - radius, radius, radius, 0.0f, 0.0f, kPi * 0.5f);
178
+ flatten_arc(&points, x + radius, y + h - radius, radius, radius, 0.0f, kPi * 0.5f, kPi * 0.5f);
179
+ flatten_arc(&points, x + radius, y + radius, radius, radius, 0.0f, kPi, kPi * 0.5f);
180
+ push_point(subpath, x + radius, y);
181
+ for (size_t i = 0; i + 1 < points.size(); i += 2) push_point(subpath, points[i], points[i + 1]);
182
+ push_point(subpath, x + radius, y);
183
+ xt_array_push(data, subpath);
184
+ return XT_UNDEFINED;
185
+ }
186
+
187
+ METHOD(path2d_arc) {
188
+ (void)env;
189
+ float sweep = number_arg(argc, argv, 4, 0.0) - number_arg(argc, argv, 3, 0.0);
190
+ normalize_sweep(&sweep, value_truthy(xt_arg(argc, argv, 5)));
191
+ std::vector<float> points;
192
+ flatten_arc(&points, number_arg(argc, argv, 0, 0.0), number_arg(argc, argv, 1, 0.0),
193
+ number_arg(argc, argv, 2, 0.0), number_arg(argc, argv, 2, 0.0), 0.0f,
194
+ number_arg(argc, argv, 3, 0.0), sweep);
195
+ append_points(self, points);
196
+ return XT_UNDEFINED;
197
+ }
198
+
199
+ METHOD(path2d_ellipse) {
200
+ (void)env;
201
+ float sweep = number_arg(argc, argv, 6, 0.0) - number_arg(argc, argv, 5, 0.0);
202
+ normalize_sweep(&sweep, value_truthy(xt_arg(argc, argv, 7)));
203
+ std::vector<float> points;
204
+ flatten_arc(&points, number_arg(argc, argv, 0, 0.0), number_arg(argc, argv, 1, 0.0),
205
+ number_arg(argc, argv, 2, 0.0), number_arg(argc, argv, 3, 0.0),
206
+ number_arg(argc, argv, 4, 0.0), number_arg(argc, argv, 5, 0.0), sweep);
207
+ append_points(self, points);
208
+ return XT_UNDEFINED;
209
+ }
210
+
211
+ METHOD(path2d_quadratic_curve_to) {
212
+ (void)env;
213
+ xt_value subpath = current_subpath(self, true);
214
+ float lx = 0.0f, ly = 0.0f;
215
+ if (!last_point(subpath, &lx, &ly)) {
216
+ lx = number_arg(argc, argv, 0, 0.0);
217
+ ly = number_arg(argc, argv, 1, 0.0);
218
+ push_point(subpath, lx, ly);
219
+ }
220
+ float cpx = number_arg(argc, argv, 0, 0.0);
221
+ float cpy = number_arg(argc, argv, 1, 0.0);
222
+ float tx = number_arg(argc, argv, 2, 0.0);
223
+ float ty = number_arg(argc, argv, 3, 0.0);
224
+ for (int i = 1; i <= 16; i++) {
225
+ float t = (float)i / 16.0f;
226
+ float a = 1.0f - t;
227
+ push_point(subpath, a * a * lx + 2.0f * a * t * cpx + t * t * tx,
228
+ a * a * ly + 2.0f * a * t * cpy + t * t * ty);
229
+ }
230
+ return XT_UNDEFINED;
231
+ }
232
+
233
+ METHOD(path2d_bezier_curve_to) {
234
+ (void)env;
235
+ xt_value subpath = current_subpath(self, true);
236
+ float lx = 0.0f, ly = 0.0f;
237
+ if (!last_point(subpath, &lx, &ly)) {
238
+ lx = number_arg(argc, argv, 0, 0.0);
239
+ ly = number_arg(argc, argv, 1, 0.0);
240
+ push_point(subpath, lx, ly);
241
+ }
242
+ float c1x = number_arg(argc, argv, 0, 0.0);
243
+ float c1y = number_arg(argc, argv, 1, 0.0);
244
+ float c2x = number_arg(argc, argv, 2, 0.0);
245
+ float c2y = number_arg(argc, argv, 3, 0.0);
246
+ float tx = number_arg(argc, argv, 4, 0.0);
247
+ float ty = number_arg(argc, argv, 5, 0.0);
248
+ for (int i = 1; i <= 24; i++) {
249
+ float t = (float)i / 24.0f;
250
+ float u = 1.0f - t;
251
+ push_point(subpath,
252
+ u * u * u * lx + 3.0f * u * u * t * c1x + 3.0f * u * t * t * c2x + t * t * t * tx,
253
+ u * u * u * ly + 3.0f * u * u * t * c1y + 3.0f * u * t * t * c2y + t * t * t * ty);
254
+ }
255
+ return XT_UNDEFINED;
256
+ }
257
+
258
+ /** `path.addPath(other, matrix?)` copies another path, optionally transformed. */
259
+ METHOD(path2d_add_path) {
260
+ (void)env;
261
+ std::vector<xtgui::CanvasSubpath> source = path2d_subpaths(xt_arg(argc, argv, 0));
262
+ if (source.empty()) return XT_UNDEFINED;
263
+ xtgui::CanvasTransform transform;
264
+ if (argc >= 2 && XT_IS_OBJECT(xt_arg(argc, argv, 1))) {
265
+ xt_value matrix = xt_arg(argc, argv, 1);
266
+ static const char *const names[6] = {"a", "b", "c", "d", "e", "f"};
267
+ float values[6] = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
268
+ for (int i = 0; i < 6; i++) {
269
+ xt_value member = xt_object_get_cstr(matrix, names[i]);
270
+ if (XT_IS_NUMBER(member)) values[i] = (float)xt_to_number(member);
271
+ }
272
+ transform = xtgui::CanvasTransform{values[0], values[1], values[2],
273
+ values[3], values[4], values[5]};
274
+ }
275
+ xt_value data = path_data(self, true);
276
+ for (const xtgui::CanvasSubpath &subpath : source) {
277
+ xt_value copy = xt_array_new(0, nullptr);
278
+ for (const xtgui::CanvasPoint &point : subpath) {
279
+ float x = point.x, y = point.y;
280
+ transform.apply(point.x, point.y, &x, &y);
281
+ push_point(copy, x, y);
282
+ }
283
+ xt_array_push(data, copy);
284
+ }
285
+ return XT_UNDEFINED;
286
+ }
287
+
288
+ #undef METHOD
289
+
290
+ } // namespace
291
+
292
+ bool path2d_is(xt_value value) { return XT_IS_OBJECT(value) && value_truthy(xt_object_get_cstr(value, "__xt_gui_path2d")); }
293
+
294
+ std::vector<xtgui::CanvasSubpath> path2d_subpaths(xt_value value) {
295
+ std::vector<xtgui::CanvasSubpath> result;
296
+ if (!path2d_is(value)) return result;
297
+ xt_value data = xt_object_get_cstr(value, "__xt_gui_path_data");
298
+ if (!XT_IS_ARRAY(data)) return result;
299
+ int32_t count = (int32_t)xt_to_number(xt_array_length(data));
300
+ for (int32_t i = 0; i < count; i++) {
301
+ xt_value item = xt_array_get(data, xt_number((double)i));
302
+ if (!XT_IS_ARRAY(item)) continue;
303
+ xtgui::CanvasSubpath subpath;
304
+ int32_t numbers = (int32_t)xt_to_number(xt_array_length(item));
305
+ for (int32_t j = 0; j + 1 < numbers; j += 2) {
306
+ subpath.push_back(xtgui::CanvasPoint{
307
+ (float)xt_to_number(xt_array_get(item, xt_number((double)j))),
308
+ (float)xt_to_number(xt_array_get(item, xt_number((double)(j + 1))))});
309
+ }
310
+ if (!subpath.empty()) result.push_back(std::move(subpath));
311
+ }
312
+ return result;
313
+ }
314
+
315
+ xt_value path2d_proto() {
316
+ if (XT_IS_OBJECT(g_path2d_proto)) return g_path2d_proto;
317
+ g_path2d_proto = xt_object_new();
318
+ xt_gc_add_root(&g_path2d_proto);
319
+ struct Entry {
320
+ const char *name;
321
+ void *fn;
322
+ };
323
+ const Entry methods[] = {
324
+ {"moveTo", (void *)path2d_move_to},
325
+ {"lineTo", (void *)path2d_line_to},
326
+ {"closePath", (void *)path2d_close_path},
327
+ {"rect", (void *)path2d_rect},
328
+ {"roundRect", (void *)path2d_round_rect},
329
+ {"arc", (void *)path2d_arc},
330
+ {"ellipse", (void *)path2d_ellipse},
331
+ {"quadraticCurveTo", (void *)path2d_quadratic_curve_to},
332
+ {"bezierCurveTo", (void *)path2d_bezier_curve_to},
333
+ {"addPath", (void *)path2d_add_path},
334
+ };
335
+ for (const Entry &method : methods) {
336
+ xt_object_set(g_path2d_proto, xt_string_from_cstr(method.name),
337
+ xt_closure_new(method.fn, 0, nullptr));
338
+ }
339
+ return g_path2d_proto;
340
+ }
341
+
342
+ /** `new Path2D()` and `new Path2D(otherPath)`; a string (SVG path data) is not
343
+ * parsed, so it yields an empty path instead of silently drawing nonsense. */
344
+ xt_value path2d_create(xt_value source) {
345
+ xt_value path = xt_object_new_with_proto(path2d_proto());
346
+ xt_object_set(path, xt_string_from_cstr("__xt_gui_path2d"), XT_TRUE);
347
+ xt_object_set(path, xt_string_from_cstr("__xt_gui_path_data"), xt_array_new(0, nullptr));
348
+ if (path2d_is(source)) {
349
+ xt_value data = xt_object_get_cstr(source, "__xt_gui_path_data");
350
+ xt_value copy = xt_array_new(0, nullptr);
351
+ int32_t count = XT_IS_ARRAY(data) ? (int32_t)xt_to_number(xt_array_length(data)) : 0;
352
+ for (int32_t i = 0; i < count; i++) xt_array_push(copy, xt_array_get(data, xt_number((double)i)));
353
+ xt_object_set(path, xt_string_from_cstr("__xt_gui_path_data"), copy);
354
+ }
355
+ return path;
356
+ }
357
+
358
+ } // namespace domapi
@@ -27,6 +27,9 @@ xt_value node_proto() {
27
27
  define_getter(proto, "tagName", (void *)node_tag_name_get);
28
28
  define_getter(proto, "nodeName", (void *)node_tag_name_get);
29
29
  define_getter(proto, "nodeType", (void *)node_node_type_get);
30
+ define_getter(proto, "namespaceURI", (void *)node_namespace_uri_get);
31
+ define_getter(proto, "localName", (void *)node_local_name_get);
32
+ define_getter(proto, "prefix", (void *)node_prefix_get);
30
33
  define_getter(proto, "id", (void *)node_id_get);
31
34
  define_getter(proto, "className", (void *)node_class_name_get);
32
35
  define_getter(proto, "textContent", (void *)node_text_content_get);
@@ -44,6 +47,7 @@ xt_value node_proto() {
44
47
  define_getter(proto, "isConnected", (void *)node_is_connected_get);
45
48
  define_getter(proto, "classList", (void *)node_class_list_get);
46
49
  define_getter(proto, "style", (void *)node_style_get);
50
+ define_getter(proto, "dataset", (void *)node_dataset_get);
47
51
  define_getter(proto, "offsetWidth", (void *)node_offset_width_get);
48
52
  define_getter(proto, "offsetHeight", (void *)node_offset_height_get);
49
53
  /* Media element properties (`<video>`/`<audio>`; harmless on any element). */
@@ -86,6 +90,10 @@ xt_value node_proto() {
86
90
  define_method(proto, "querySelectorAll", (void *)node_query_selector_all);
87
91
  define_method(proto, "matches", (void *)node_matches);
88
92
  define_method(proto, "appendChild", (void *)node_append_child);
93
+ define_method(proto, "append", (void *)node_append);
94
+ define_method(proto, "prepend", (void *)node_prepend);
95
+ define_method(proto, "replaceChildren", (void *)node_replace_children);
96
+ define_method(proto, "insertAdjacentHTML", (void *)node_insert_adjacent_html);
89
97
  define_method(proto, "insertBefore", (void *)node_insert_before);
90
98
  define_method(proto, "removeChild", (void *)node_remove_child);
91
99
  define_method(proto, "replaceChild", (void *)node_replace_child);
@@ -103,6 +111,9 @@ xt_value node_proto() {
103
111
  define_method(proto, "pause", (void *)node_pause);
104
112
  define_method(proto, "load", (void *)node_load);
105
113
  define_method(proto, "getContext", (void *)node_get_context);
114
+ /* `toDataURL`/`toBlob` belong to the canvas *element*, as in the DOM. */
115
+ define_method(proto, "toDataURL", (void *)canvas_to_data_url);
116
+ define_method(proto, "toBlob", (void *)canvas_to_blob);
106
117
  g_node_proto = proto;
107
118
  return proto;
108
119
  }
@@ -121,7 +132,9 @@ xt_value document_proto() {
121
132
  define_getter(proto, "hidden", (void *)doc_hidden_get);
122
133
  define_getter(proto, "visibilityState", (void *)doc_visibility_state_get);
123
134
  define_getter(proto, "readyState", (void *)doc_ready_state_get);
135
+ define_method(proto, "hasFocus", (void *)doc_has_focus);
124
136
  define_getter(proto, "title", (void *)doc_title_get);
137
+ define_setter(proto, "title", (void *)doc_title_set);
125
138
  define_getter(proto, "defaultView", (void *)doc_default_view_get);
126
139
  define_method(proto, "createTextNode", (void *)doc_create_text_node);
127
140
  /* `document.addEventListener` / element listener plumbing come from the node
@@ -140,6 +153,7 @@ xt_value event_proto() {
140
153
  define_method(proto, "preventDefault", (void *)event_prevent_default);
141
154
  define_method(proto, "stopPropagation", (void *)event_stop_propagation);
142
155
  define_method(proto, "stopImmediatePropagation", (void *)event_stop_immediate_propagation);
156
+ define_method(proto, "composedPath", (void *)event_composed_path);
143
157
  g_event_proto = proto;
144
158
  return proto;
145
159
  }
@@ -162,11 +176,26 @@ xt_value style_proto() {
162
176
  xt_value proto = xt_object_new();
163
177
  define_method(proto, "setProperty", (void *)style_set_property);
164
178
  define_method(proto, "getPropertyValue", (void *)style_get_property_value);
179
+ define_method(proto, "getPropertyPriority", (void *)style_get_property_priority);
165
180
  define_method(proto, "removeProperty", (void *)style_remove_property);
181
+ define_getter(proto, "cssText", (void *)style_css_text_get);
182
+ define_setter(proto, "cssText", (void *)style_css_text_set);
183
+ define_method(proto, "item", (void *)style_item);
184
+ define_getter(proto, "length", (void *)style_length_get);
166
185
  g_style_proto = proto;
167
186
  return proto;
168
187
  }
169
188
 
189
+ xt_value computed_style_proto() {
190
+ xt_gc_add_root(&g_computed_style_proto);
191
+ if (XT_IS_OBJECT(g_computed_style_proto)) return g_computed_style_proto;
192
+ xt_value proto = xt_object_new();
193
+ define_method(proto, "getPropertyValue", (void *)computed_style_get_property_value);
194
+ define_method(proto, "getPropertyPriority", (void *)computed_style_get_property_priority);
195
+ g_computed_style_proto = proto;
196
+ return proto;
197
+ }
198
+
170
199
  xt_value canvas_proto() {
171
200
  xt_gc_add_root(&g_canvas_proto);
172
201
  if (XT_IS_OBJECT(g_canvas_proto)) return g_canvas_proto;
@@ -224,6 +253,12 @@ xt_value canvas_proto() {
224
253
  define_method(proto, "strokeText", (void *)canvas_stroke_text);
225
254
  define_method(proto, "drawImage", (void *)canvas_draw_image);
226
255
  define_method(proto, "getImageData", (void *)canvas_get_image_data);
256
+ define_method(proto, "getContextAttributes", (void *)canvas_get_context_attributes);
257
+ define_method(proto, "putImageData", (void *)canvas_put_image_data);
258
+ define_method(proto, "createImageData", (void *)canvas_create_image_data);
259
+ define_method(proto, "createPattern", (void *)canvas_create_pattern);
260
+ define_method(proto, "toDataURL", (void *)canvas_to_data_url);
261
+ define_method(proto, "toBlob", (void *)canvas_to_blob);
227
262
  define_method(proto, "setLineDash", (void *)canvas_line_dash_set);
228
263
  define_method(proto, "getLineDash", (void *)canvas_line_dash_get);
229
264
  define_method(proto, "getTransform", (void *)canvas_get_transform);