xbintsc 0.5.45 → 0.5.52

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 (286) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +5 -4
  3. package/README.zh-CN.md +4 -3
  4. package/dist/src/cli/extensions.d.ts +24 -0
  5. package/dist/src/cli/extensions.js +62 -0
  6. package/dist/src/cli/extensions.js.map +1 -0
  7. package/dist/src/cli/help.d.ts +1 -1
  8. package/dist/src/cli/help.js +3 -1
  9. package/dist/src/cli/help.js.map +1 -1
  10. package/dist/src/cli/hints.js +8 -3
  11. package/dist/src/cli/hints.js.map +1 -1
  12. package/dist/src/cli/main.js +21 -28
  13. package/dist/src/cli/main.js.map +1 -1
  14. package/dist/src/diagnostics/diagnostic.d.ts +3 -1
  15. package/dist/src/diagnostics/diagnostic.js +2 -0
  16. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  17. package/dist/src/driver/build-key.d.ts +2 -0
  18. package/dist/src/driver/build-key.js +1 -0
  19. package/dist/src/driver/build-key.js.map +1 -1
  20. package/dist/src/driver/compiler.js +61 -8
  21. package/dist/src/driver/compiler.js.map +1 -1
  22. package/dist/src/driver/config.d.ts +4 -0
  23. package/dist/src/driver/config.js +2 -0
  24. package/dist/src/driver/config.js.map +1 -1
  25. package/dist/src/driver/link-features.d.ts +37 -0
  26. package/dist/src/driver/link-features.js +123 -0
  27. package/dist/src/driver/link-features.js.map +1 -0
  28. package/dist/src/driver/options.d.ts +10 -0
  29. package/dist/src/driver/runtime-objects.d.ts +5 -0
  30. package/dist/src/driver/runtime-objects.js +20 -1
  31. package/dist/src/driver/runtime-objects.js.map +1 -1
  32. package/dist/src/driver/usage.d.ts +23 -0
  33. package/dist/src/driver/usage.js +353 -0
  34. package/dist/src/driver/usage.js.map +1 -0
  35. package/dist/src/driver/version.d.ts +1 -1
  36. package/dist/src/driver/version.js +1 -1
  37. package/dist/src/extensions/catalog.js +1 -2
  38. package/dist/src/extensions/catalog.js.map +1 -1
  39. package/dist/src/extensions/gui/features.d.ts +34 -0
  40. package/dist/src/extensions/gui/features.js +165 -0
  41. package/dist/src/extensions/gui/features.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +10 -4
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/link-feature.d.ts +70 -0
  45. package/dist/src/extensions/link-feature.js +18 -0
  46. package/dist/src/extensions/link-feature.js.map +1 -0
  47. package/dist/src/extensions/registry.d.ts +24 -4
  48. package/dist/src/extensions/registry.js +26 -3
  49. package/dist/src/extensions/registry.js.map +1 -1
  50. package/dist/src/index.d.ts +3 -2
  51. package/dist/src/index.js +0 -1
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/tests/cli/hints.test.js +5 -7
  54. package/dist/tests/cli/hints.test.js.map +1 -1
  55. package/dist/tests/driver/link-features.test.js +105 -0
  56. package/dist/tests/driver/link-features.test.js.map +1 -0
  57. package/dist/tests/driver/usage.test.d.ts +1 -0
  58. package/dist/tests/driver/usage.test.js +88 -0
  59. package/dist/tests/driver/usage.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-audio.test.js +4 -4
  61. package/dist/tests/e2e/gui-audio.test.js.map +1 -1
  62. package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
  63. package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
  64. package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
  66. package/dist/tests/e2e/gui-css-features.test.js +270 -0
  67. package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-effects.test.js +92 -11
  69. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  70. package/dist/tests/e2e/gui-feature-linking.test.d.ts +9 -0
  71. package/dist/tests/e2e/gui-feature-linking.test.js +68 -0
  72. package/dist/tests/e2e/gui-feature-linking.test.js.map +1 -0
  73. package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
  74. package/dist/tests/e2e/gui-font-face.test.js +142 -0
  75. package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
  76. package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
  77. package/dist/tests/e2e/gui-fonts.test.js +150 -0
  78. package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
  79. package/dist/tests/e2e/gui-helpers.d.ts +10 -1
  80. package/dist/tests/e2e/gui-helpers.js +6 -0
  81. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  82. package/dist/tests/e2e/gui-layout.test.js +7 -3
  83. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  84. package/dist/tests/e2e/gui-media-disabled.test.d.ts +5 -4
  85. package/dist/tests/e2e/gui-media-disabled.test.js +7 -6
  86. package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -1
  87. package/dist/tests/e2e/gui-media.test.d.ts +5 -4
  88. package/dist/tests/e2e/gui-media.test.js +9 -9
  89. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  90. package/dist/tests/e2e/gui-paint.test.js +5 -2
  91. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  92. package/dist/tests/e2e/gui-shaping.test.d.ts +5 -0
  93. package/dist/tests/e2e/gui-shaping.test.js +47 -0
  94. package/dist/tests/e2e/gui-shaping.test.js.map +1 -0
  95. package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
  96. package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
  97. package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
  98. package/dist/tests/e2e/gui-transform.test.js +3 -2
  99. package/dist/tests/e2e/gui-transform.test.js.map +1 -1
  100. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.d.ts +18 -0
  101. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js +120 -0
  102. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js.map +1 -0
  103. package/dist/tests/extensions/gui-features.test.d.ts +1 -0
  104. package/dist/tests/extensions/gui-features.test.js +175 -0
  105. package/dist/tests/extensions/gui-features.test.js.map +1 -0
  106. package/doc/ai/cli.md +5 -1
  107. package/doc/ai/extensions.md +56 -26
  108. package/doc/ai/troubleshooting.md +10 -7
  109. package/doc/ai/zh-CN/cli.md +5 -1
  110. package/doc/ai/zh-CN/extensions.md +48 -22
  111. package/doc/ai/zh-CN/troubleshooting.md +6 -5
  112. package/doc/gui/animation.md +39 -9
  113. package/doc/gui/api.md +2 -1
  114. package/doc/gui/build.md +137 -19
  115. package/doc/gui/canvas.md +1 -1
  116. package/doc/gui/dom.md +20 -4
  117. package/doc/gui/known-issues.md +63 -26
  118. package/doc/gui/layout.md +31 -8
  119. package/doc/gui/media.md +13 -7
  120. package/doc/gui/rendering.md +114 -39
  121. package/doc/gui/runtime.md +5 -0
  122. package/doc/gui/webgl.md +24 -6
  123. package/doc/gui.md +110 -47
  124. package/doc/implemented/cli.md +2 -0
  125. package/doc/implemented/extensions.md +9 -3
  126. package/doc/npm-package.md +4 -4
  127. package/doc/xbintsc.config.schema.json +10 -0
  128. package/doc/zh-CN/gui/animation.md +33 -10
  129. package/doc/zh-CN/gui/api.md +2 -1
  130. package/doc/zh-CN/gui/build.md +110 -16
  131. package/doc/zh-CN/gui/canvas.md +1 -1
  132. package/doc/zh-CN/gui/dom.md +16 -3
  133. package/doc/zh-CN/gui/known-issues.md +45 -15
  134. package/doc/zh-CN/gui/layout.md +23 -6
  135. package/doc/zh-CN/gui/media.md +11 -6
  136. package/doc/zh-CN/gui/rendering.md +84 -26
  137. package/doc/zh-CN/gui/runtime.md +3 -0
  138. package/doc/zh-CN/gui/webgl.md +3 -3
  139. package/doc/zh-CN/gui.md +92 -41
  140. package/doc/zh-CN/implemented/cli.md +2 -0
  141. package/doc/zh-CN/implemented/extensions.md +9 -3
  142. package/doc/zh-CN/npm-package.md +4 -3
  143. package/package.json +7 -7
  144. package/runtime/ext_gui/animation_values.cpp +425 -0
  145. package/runtime/ext_gui/animation_values.h +64 -0
  146. package/runtime/ext_gui/browser.cpp +22 -15
  147. package/runtime/ext_gui/browser_loop.cpp +13 -1
  148. package/runtime/ext_gui/canvas.h +1 -1
  149. package/runtime/ext_gui/canvas_raster.cpp +1 -1
  150. package/runtime/ext_gui/canvas_text.cpp +1 -1
  151. package/runtime/ext_gui/css.cpp +172 -21
  152. package/runtime/ext_gui/css.h +75 -4
  153. package/runtime/ext_gui/css_match.cpp +4 -0
  154. package/runtime/ext_gui/css_sheet.cpp +218 -2
  155. package/runtime/ext_gui/document.cpp +42 -192
  156. package/runtime/ext_gui/document.h +25 -12
  157. package/runtime/ext_gui/document_animation.cpp +428 -0
  158. package/runtime/ext_gui/dom_api_canvas.cpp +15 -8
  159. package/runtime/ext_gui/dom_api_canvas_extra.cpp +44 -20
  160. package/runtime/ext_gui/dom_api_canvas_image.cpp +3 -1
  161. package/runtime/ext_gui/dom_api_node_read.cpp +10 -0
  162. package/runtime/ext_gui/dom_api_offscreen.cpp +3 -1
  163. package/runtime/ext_gui/dom_api_style.cpp +7 -0
  164. package/runtime/ext_gui/dom_api_webgl_internal.h +4 -0
  165. package/runtime/ext_gui/dom_api_webgl_source.cpp +5 -2
  166. package/runtime/ext_gui/effect_values.cpp +245 -21
  167. package/runtime/ext_gui/gpu_bridge.c +28 -0
  168. package/runtime/ext_gui/gpu_feature.cpp +30 -0
  169. package/runtime/ext_gui/gpu_shader.cpp +22 -0
  170. package/runtime/ext_gui/gpu_shader_api.h +50 -0
  171. package/runtime/ext_gui/gradient_values.cpp +24 -0
  172. package/runtime/ext_gui/grid_values.cpp +23 -0
  173. package/runtime/ext_gui/gui.cpp +6 -37
  174. package/runtime/ext_gui/gui_canvas.cpp +14 -6
  175. package/runtime/ext_gui/gui_cursor.cpp +50 -0
  176. package/runtime/ext_gui/gui_engine.h +1 -18
  177. package/runtime/ext_gui/gui_events.cpp +19 -6
  178. package/runtime/ext_gui/gui_internal.h +3 -0
  179. package/runtime/ext_gui/gui_webgl.cpp +25 -10
  180. package/runtime/ext_gui/image_api.cpp +18 -0
  181. package/runtime/ext_gui/image_api.h +43 -0
  182. package/runtime/ext_gui/image_bitmap_browser.cpp +20 -11
  183. package/runtime/ext_gui/image_bridge.c +22 -0
  184. package/runtime/ext_gui/image_feature.cpp +107 -0
  185. package/runtime/ext_gui/inline_svg.cpp +95 -8
  186. package/runtime/ext_gui/inline_svg.h +5 -1
  187. package/runtime/ext_gui/layout.cpp +15 -9
  188. package/runtime/ext_gui/layout.h +8 -2
  189. package/runtime/ext_gui/layout_flex.cpp +134 -29
  190. package/runtime/ext_gui/layout_flow.cpp +58 -7
  191. package/runtime/ext_gui/layout_flow_internal.h +39 -4
  192. package/runtime/ext_gui/layout_grid.cpp +31 -0
  193. package/runtime/ext_gui/layout_inline.cpp +26 -2
  194. package/runtime/ext_gui/layout_vertical.cpp +196 -0
  195. package/runtime/ext_gui/media.cpp +4 -2
  196. package/runtime/ext_gui/media.h +3 -3
  197. package/runtime/ext_gui/media_api.h +7 -6
  198. package/runtime/ext_gui/paint.cpp +67 -9
  199. package/runtime/ext_gui/paint.h +61 -8
  200. package/runtime/ext_gui/paint_backdrop.cpp +211 -0
  201. package/runtime/ext_gui/paint_background.cpp +175 -97
  202. package/runtime/ext_gui/paint_effects.cpp +12 -6
  203. package/runtime/ext_gui/paint_internal.h +27 -0
  204. package/runtime/ext_gui/paint_outline.cpp +44 -0
  205. package/runtime/ext_gui/paint_shadow.cpp +368 -0
  206. package/runtime/ext_gui/renderer.cpp +15 -7
  207. package/runtime/ext_gui/renderer.h +11 -2
  208. package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
  209. package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
  210. package/runtime/ext_gui/renderer_gradient.cpp +51 -17
  211. package/runtime/ext_gui/renderer_image.cpp +19 -7
  212. package/runtime/ext_gui/renderer_internal.h +21 -0
  213. package/runtime/ext_gui/renderer_layer.cpp +37 -6
  214. package/runtime/ext_gui/renderer_text.cpp +2 -1
  215. package/runtime/ext_gui/stb_truetype.h +5077 -0
  216. package/runtime/ext_gui/style.cpp +62 -8
  217. package/runtime/ext_gui/style.h +111 -4
  218. package/runtime/ext_gui/style_cascade.cpp +83 -0
  219. package/runtime/ext_gui/style_internal.h +13 -1
  220. package/runtime/ext_gui/style_length.cpp +92 -0
  221. package/runtime/ext_gui/style_print.cpp +83 -2
  222. package/runtime/ext_gui/style_svg.cpp +40 -0
  223. package/runtime/ext_gui/style_text.cpp +302 -0
  224. package/runtime/ext_gui/style_ui.cpp +150 -0
  225. package/runtime/ext_gui/style_values.cpp +76 -62
  226. package/runtime/ext_gui/style_values_internal.h +23 -0
  227. package/runtime/ext_gui/text.cpp +27 -499
  228. package/runtime/ext_gui/text.h +18 -9
  229. package/runtime/ext_gui/text_api.h +45 -0
  230. package/runtime/ext_gui/text_bridge.c +23 -0
  231. package/runtime/ext_gui/text_cache.cpp +12 -3
  232. package/runtime/ext_gui/text_font_db.cpp +427 -0
  233. package/runtime/ext_gui/text_font_paths.cpp +250 -0
  234. package/runtime/ext_gui/text_font_paths.h +39 -0
  235. package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
  236. package/runtime/ext_gui/text_font_sfnt.h +44 -0
  237. package/runtime/ext_gui/text_fonts.cpp +348 -0
  238. package/runtime/ext_gui/text_fonts.h +78 -0
  239. package/runtime/ext_gui/text_fonts_internal.h +77 -0
  240. package/runtime/ext_gui/text_glyph_cache.cpp +2 -2
  241. package/runtime/ext_gui/text_hb.cpp +438 -0
  242. package/runtime/ext_gui/text_stb.cpp +369 -0
  243. package/runtime/ext_gui/transform.cpp +107 -9
  244. package/runtime/ext_gui/transform.h +5 -0
  245. package/runtime/ext_gui/webgl_api.cpp +19 -0
  246. package/runtime/ext_gui/webgl_api.h +53 -0
  247. package/runtime/ext_gui/webgl_bridge.c +25 -0
  248. package/runtime/ext_gui/webgl_draw_setup.cpp +42 -1
  249. package/runtime/ext_gui/webgl_feature.cpp +82 -0
  250. package/runtime/ext_gui/webgl_feature.h +28 -0
  251. package/runtime/ext_gui/webgl_msl.cpp +41 -0
  252. package/runtime/ext_gui/webgl_msl.h +6 -0
  253. package/runtime/ext_gui/webgl_shader.cpp +40 -32
  254. package/runtime/ext_gui/window.cpp +7 -7
  255. package/runtime/ext_media/decode_audio.cpp +1 -1
  256. package/runtime/ext_media/media.cpp +1 -1
  257. package/runtime/ext_media/media_bridge.c +7 -7
  258. package/scripts/build-gui.ts +97 -98
  259. package/scripts/check-file-length.ts +1 -0
  260. package/scripts/gui-build-options.ts +122 -0
  261. package/src/cli/extensions.ts +72 -0
  262. package/src/cli/help.ts +3 -1
  263. package/src/cli/hints.ts +9 -3
  264. package/src/cli/main.ts +21 -27
  265. package/src/diagnostics/diagnostic.ts +2 -0
  266. package/src/driver/build-key.ts +3 -0
  267. package/src/driver/compiler.ts +63 -8
  268. package/src/driver/config.ts +6 -0
  269. package/src/driver/link-features.ts +163 -0
  270. package/src/driver/options.ts +10 -0
  271. package/src/driver/runtime-objects.ts +26 -1
  272. package/src/driver/usage.ts +376 -0
  273. package/src/driver/version.ts +1 -1
  274. package/src/extensions/catalog.ts +1 -2
  275. package/src/extensions/gui/features.ts +166 -0
  276. package/src/extensions/gui/index.ts +10 -4
  277. package/src/extensions/link-feature.ts +72 -0
  278. package/src/extensions/registry.ts +45 -4
  279. package/src/index.ts +3 -2
  280. package/dist/src/extensions/media/index.d.ts +0 -28
  281. package/dist/src/extensions/media/index.js +0 -59
  282. package/dist/src/extensions/media/index.js.map +0 -1
  283. package/dist/tests/extensions/media.test.js +0 -63
  284. package/dist/tests/extensions/media.test.js.map +0 -1
  285. package/src/extensions/media/index.ts +0 -65
  286. /package/dist/tests/{extensions/media.test.d.ts → driver/link-features.test.d.ts} +0 -0
@@ -0,0 +1,369 @@
1
+ /*
2
+ * xbintsc GUI engine — default stb_truetype text backend.
3
+ *
4
+ * Text is shaped and rasterised with the bundled stb_truetype. The optional
5
+ * `shaping` feature installs `text_hb.cpp` (HarfBuzz + FreeType) instead; this
6
+ * backend is always linked so a Latin-only DOM program keeps the small binary.
7
+ *
8
+ * Trade-off: shaping is a per-codepoint mapping plus the font's `kern` table.
9
+ * It covers Latin/CJK/Cyrillic/Greek well, but it does not run OpenType
10
+ * ligatures or complex-script shaping (Arabic/Indic/Thai) and it only accepts
11
+ * TrueType outlines, not CFF/OTF or variable fonts. The fallback chain still
12
+ * switches faces for codepoints the primary lacks.
13
+ *
14
+ * Faces are selected by `text_fonts.cpp`: the CSS family list, weight and style
15
+ * resolve to a `(path, collection index)` pair, and only the ordered candidates
16
+ * this backend can actually load (TrueType outlines) are tried.
17
+ */
18
+
19
+ #include "text_api.h"
20
+ #include "text_fonts.h"
21
+
22
+ #define STB_TRUETYPE_IMPLEMENTATION
23
+ #include "stb_truetype.h"
24
+
25
+ #include <cstddef>
26
+ #include <cstdint>
27
+ #include <cstdio>
28
+ #include <memory>
29
+ #include <string>
30
+ #include <unordered_map>
31
+ #include <vector>
32
+
33
+ namespace xtgui {
34
+
35
+ /* Opaque to callers (declared in text.h); defined here so the loading helpers
36
+ * in the anonymous namespace can build it. */
37
+ struct Font {
38
+ stbtt_fontinfo info{};
39
+ /* The font file is shared by every pixel size of the same path; stbtt keeps
40
+ * pointers into it, so it must outlive this face. */
41
+ std::shared_ptr<const std::vector<unsigned char>> data;
42
+ std::string path;
43
+ int face_index = 0;
44
+ float pixel_size = 16.0f;
45
+ float scale = 1.0f;
46
+ float ascent = 0;
47
+ float descent = 0;
48
+ float line_height = 0;
49
+ };
50
+
51
+ namespace {
52
+
53
+ struct Engine {
54
+ std::unordered_map<std::string, std::shared_ptr<const std::vector<unsigned char>>> data;
55
+ std::unordered_map<std::string, std::unique_ptr<Font>> cache; // key -> face (may be null)
56
+ std::unordered_map<std::string, std::vector<Font *>> fallbacks; // face+size -> faces
57
+ std::unordered_map<std::string, Font *> primary; // spec -> face (may be null)
58
+ };
59
+
60
+ Engine &engine() {
61
+ static Engine instance;
62
+ return instance;
63
+ }
64
+
65
+ /* Decode one UTF-8 sequence starting at `index`; advances `index`. */
66
+ uint32_t decodeUtf8(const std::string &text, size_t &index) {
67
+ unsigned char first = (unsigned char)text[index];
68
+ if (first < 0x80) {
69
+ index += 1;
70
+ return first;
71
+ }
72
+ uint32_t code_point = 0;
73
+ size_t extra = 0;
74
+ if ((first & 0xe0) == 0xc0) {
75
+ code_point = first & 0x1f;
76
+ extra = 1;
77
+ } else if ((first & 0xf0) == 0xe0) {
78
+ code_point = first & 0x0f;
79
+ extra = 2;
80
+ } else if ((first & 0xf8) == 0xf0) {
81
+ code_point = first & 0x07;
82
+ extra = 3;
83
+ } else {
84
+ index += 1;
85
+ return 0xfffd;
86
+ }
87
+ if (index + extra >= text.size()) {
88
+ index = text.size();
89
+ return 0xfffd;
90
+ }
91
+ for (size_t i = 1; i <= extra; i++) {
92
+ unsigned char next = (unsigned char)text[index + i];
93
+ if ((next & 0xc0) != 0x80) {
94
+ index += 1;
95
+ return 0xfffd;
96
+ }
97
+ code_point = (code_point << 6) | (next & 0x3f);
98
+ }
99
+ index += extra + 1;
100
+ return code_point;
101
+ }
102
+
103
+ /* Read a font file once and share the bytes across every pixel size. */
104
+ std::shared_ptr<const std::vector<unsigned char>> fontData(const std::string &path) {
105
+ Engine &e = engine();
106
+ auto found = e.data.find(path);
107
+ if (found != e.data.end()) return found->second;
108
+
109
+ FILE *file = fopen(path.c_str(), "rb");
110
+ if (file == nullptr) return nullptr;
111
+ if (fseek(file, 0, SEEK_END) != 0) {
112
+ fclose(file);
113
+ return nullptr;
114
+ }
115
+ long length = ftell(file);
116
+ if (length <= 0 || fseek(file, 0, SEEK_SET) != 0) {
117
+ fclose(file);
118
+ return nullptr;
119
+ }
120
+ auto bytes = std::make_shared<std::vector<unsigned char>>((size_t)length);
121
+ size_t read = fread(bytes->data(), 1, bytes->size(), file);
122
+ fclose(file);
123
+ if (read != bytes->size()) return nullptr;
124
+ std::shared_ptr<const std::vector<unsigned char>> shared = bytes;
125
+ e.data.emplace(path, shared);
126
+ return shared;
127
+ }
128
+
129
+ std::unique_ptr<Font> createFont(const std::string &path, int face_index, float pixel_size) {
130
+ if (path.empty()) return nullptr;
131
+ std::shared_ptr<const std::vector<unsigned char>> data = fontData(path);
132
+ if (data == nullptr || data->empty()) return nullptr;
133
+
134
+ const int offset = stbtt_GetFontOffsetForIndex(data->data(), face_index);
135
+ if (offset < 0) return nullptr;
136
+ stbtt_fontinfo info;
137
+ if (!stbtt_InitFont(&info, data->data(), offset)) return nullptr;
138
+
139
+ auto font = std::make_unique<Font>();
140
+ font->info = info;
141
+ font->data = std::move(data);
142
+ font->path = path;
143
+ font->face_index = face_index;
144
+ font->pixel_size = pixel_size;
145
+ font->scale = stbtt_ScaleForPixelHeight(&font->info, pixel_size);
146
+
147
+ int ascent = 0;
148
+ int descent = 0;
149
+ int line_gap = 0;
150
+ stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
151
+ font->ascent = (float)ascent * font->scale;
152
+ font->descent = (float)-descent * font->scale;
153
+ font->line_height = (float)(ascent - descent + line_gap) * font->scale;
154
+ return font;
155
+ }
156
+
157
+ /* Resolve one face by path+index, keyed by path+collection index+size so the
158
+ * primary resolved through a `FontSpec` and the same face resolved as a
159
+ * fallback share one `Font` (and one glyph-atlas key). */
160
+ Font *resolveFacePath(const std::string &path, int face_index, float pixel_size) {
161
+ Engine &e = engine();
162
+ if (path.empty()) return nullptr;
163
+ char header[64];
164
+ std::snprintf(header, sizeof(header), "p|%d|%.2f|", face_index, (double)pixel_size);
165
+ std::string key = header;
166
+ key += path;
167
+ auto it = e.cache.find(key);
168
+ if (it != e.cache.end()) return it->second.get();
169
+ std::unique_ptr<Font> font = createFont(path, face_index, pixel_size);
170
+ Font *raw = font.get();
171
+ e.cache.emplace(std::move(key), std::move(font));
172
+ return raw;
173
+ }
174
+
175
+ /* The resolved fallback faces for a primary, in order, cached per face+size. */
176
+ const std::vector<Font *> &fallbackFaces(Font *font) {
177
+ static const std::vector<Font *> kEmpty;
178
+ if (font == nullptr) return kEmpty;
179
+ Engine &e = engine();
180
+ char header[64];
181
+ std::snprintf(header, sizeof(header), "f|%.2f|", (double)font->pixel_size);
182
+ std::string key = header;
183
+ key += font->path;
184
+ key += '#';
185
+ key += std::to_string(font->face_index);
186
+ auto it = e.fallbacks.find(key);
187
+ if (it != e.fallbacks.end()) return it->second;
188
+
189
+ std::vector<FontFaceRef> candidates;
190
+ xt_font_fallback_faces(FontFaceRef{font->path, font->face_index}, true, candidates);
191
+ std::vector<Font *> faces;
192
+ for (const FontFaceRef &ref : candidates) {
193
+ Font *face = resolveFacePath(ref.path, ref.index, font->pixel_size);
194
+ if (face != nullptr) faces.push_back(face);
195
+ }
196
+ return e.fallbacks.emplace(std::move(key), std::move(faces)).first->second;
197
+ }
198
+
199
+ /* The face that draws `code_point`: the primary when it has the glyph, else the
200
+ * first fallback that does, else the primary (so `.notdef` is still skipped). */
201
+ Font *faceForCodePoint(Font *primary, uint32_t code_point) {
202
+ if (primary != nullptr && stbtt_FindGlyphIndex(&primary->info, (int)code_point) != 0) {
203
+ return primary;
204
+ }
205
+ for (Font *fallback : fallbackFaces(primary)) {
206
+ if (stbtt_FindGlyphIndex(&fallback->info, (int)code_point) != 0) return fallback;
207
+ }
208
+ return primary;
209
+ }
210
+
211
+ /* Map one face's segment of a run to glyphs, using advances and the font's
212
+ * `kern` table. This is intentionally a simple shaper: no ligature/GSUB
213
+ * substitution and no complex-script positioning. */
214
+ float shapeSegment(const std::string &segment, Font *font, std::vector<ShapedGlyph> &out) {
215
+ if (font == nullptr || segment.empty()) return 0;
216
+ float total = 0;
217
+ size_t index = 0;
218
+ int previous = 0;
219
+ while (index < segment.size()) {
220
+ uint32_t code_point = decodeUtf8(segment, index);
221
+ int glyph = stbtt_FindGlyphIndex(&font->info, (int)code_point);
222
+ int advance = 0;
223
+ int left_side_bearing = 0;
224
+ stbtt_GetGlyphHMetrics(&font->info, glyph, &advance, &left_side_bearing);
225
+ float x_advance = (float)advance * font->scale;
226
+ if (previous != 0) {
227
+ int kerning = stbtt_GetGlyphKernAdvance(&font->info, previous, glyph);
228
+ if (kerning != 0) {
229
+ float shift = (float)kerning * font->scale;
230
+ if (!out.empty()) out.back().x_advance += shift;
231
+ total += shift;
232
+ }
233
+ }
234
+ ShapedGlyph shaped;
235
+ shaped.glyph = (uint32_t)glyph;
236
+ shaped.x_advance = x_advance;
237
+ shaped.font = font;
238
+ total += x_advance;
239
+ out.push_back(shaped);
240
+ previous = glyph;
241
+ }
242
+ return total;
243
+ }
244
+
245
+ } // namespace
246
+
247
+ Font *xt_text_stb_resolve(const FontSpec &spec) {
248
+ Engine &e = engine();
249
+ char suffix[80];
250
+ std::snprintf(suffix, sizeof(suffix), "|%d|%d|%.4f", spec.weight, spec.italic ? 1 : 0,
251
+ (double)spec.pixel_size);
252
+ std::string key = spec.family;
253
+ key += suffix;
254
+ auto cached = e.primary.find(key);
255
+ if (cached != e.primary.end()) return cached->second;
256
+
257
+ std::vector<FontFaceRef> faces;
258
+ xt_font_face_candidates(spec, true, faces);
259
+ Font *font = nullptr;
260
+ for (const FontFaceRef &ref : faces) {
261
+ font = resolveFacePath(ref.path, ref.index, spec.pixel_size);
262
+ if (font != nullptr) break;
263
+ }
264
+ e.primary.emplace(std::move(key), font);
265
+ return font;
266
+ }
267
+
268
+ Font *xt_text_stb_scale(Font *font, float pixel_size) {
269
+ if (font == nullptr) return nullptr;
270
+ return resolveFacePath(font->path, font->face_index, pixel_size);
271
+ }
272
+
273
+ float xt_text_stb_shape(const std::string &utf8, Font *font, std::vector<ShapedGlyph> &out) {
274
+ if (font == nullptr) return 0;
275
+ float total = 0;
276
+ size_t index = 0;
277
+ size_t segment_start = 0;
278
+ Font *segment_font = nullptr;
279
+ while (index < utf8.size()) {
280
+ size_t char_start = index;
281
+ uint32_t code_point = decodeUtf8(utf8, index);
282
+ Font *chosen = faceForCodePoint(font, code_point);
283
+ if (segment_font == nullptr) {
284
+ segment_font = chosen;
285
+ segment_start = char_start;
286
+ } else if (chosen != segment_font) {
287
+ total += shapeSegment(utf8.substr(segment_start, char_start - segment_start), segment_font, out);
288
+ segment_font = chosen;
289
+ segment_start = char_start;
290
+ }
291
+ }
292
+ if (segment_font != nullptr) {
293
+ total += shapeSegment(utf8.substr(segment_start), segment_font, out);
294
+ }
295
+ return total;
296
+ }
297
+
298
+ bool xt_text_stb_rasterize(Font *font, uint32_t glyph, GlyphImage &out) {
299
+ out.width = 0;
300
+ out.height = 0;
301
+ out.left = 0;
302
+ out.top = 0;
303
+ out.pixels.clear();
304
+ if (font == nullptr) return false;
305
+
306
+ int x0 = 0;
307
+ int y0 = 0;
308
+ int x1 = 0;
309
+ int y1 = 0;
310
+ stbtt_GetGlyphBitmapBox(&font->info, (int)glyph, font->scale, font->scale, &x0, &y0, &x1, &y1);
311
+ const int width = x1 - x0;
312
+ const int height = y1 - y0;
313
+ if (width <= 0 || height <= 0) return false;
314
+
315
+ out.width = width;
316
+ out.height = height;
317
+ out.left = x0;
318
+ out.top = -y0;
319
+ out.pixels.resize((size_t)width * height);
320
+ stbtt_MakeGlyphBitmap(&font->info, out.pixels.data(), width, height, width, font->scale,
321
+ font->scale, (int)glyph);
322
+ return true;
323
+ }
324
+
325
+ void xt_text_stb_metrics(const FontSpec &spec, float *ascent, float *descent, float *line_height) {
326
+ Font *font = xt_text_stb_resolve(spec);
327
+ if (font != nullptr) {
328
+ if (ascent != nullptr) *ascent = font->ascent;
329
+ if (descent != nullptr) *descent = font->descent;
330
+ if (line_height != nullptr) *line_height = font->line_height;
331
+ return;
332
+ }
333
+ if (ascent != nullptr) *ascent = spec.pixel_size * 0.8f;
334
+ if (descent != nullptr) *descent = spec.pixel_size * 0.2f;
335
+ if (line_height != nullptr) *line_height = spec.pixel_size * 1.2f;
336
+ }
337
+
338
+ bool xt_text_stb_ready() {
339
+ FontSpec spec;
340
+ return xt_text_stb_resolve(spec) != nullptr;
341
+ }
342
+
343
+ void xt_text_stb_shutdown() {
344
+ Engine &e = engine();
345
+ /* Cached shaped runs point at faces, so drop them before the faces go. */
346
+ e.primary.clear();
347
+ e.fallbacks.clear();
348
+ e.cache.clear();
349
+ e.data.clear();
350
+ }
351
+
352
+ } // namespace xtgui
353
+
354
+ namespace xtgui {
355
+
356
+ const TextApi *xt_text_stb_api() {
357
+ static const TextApi api = {
358
+ xt_text_stb_resolve,
359
+ xt_text_stb_scale,
360
+ xt_text_stb_shape,
361
+ xt_text_stb_rasterize,
362
+ xt_text_stb_metrics,
363
+ xt_text_stb_ready,
364
+ xt_text_stb_shutdown,
365
+ };
366
+ return &api;
367
+ }
368
+
369
+ } // namespace xtgui
@@ -32,16 +32,25 @@ bool isSpace(char c) { return xt_is_css_space(c); }
32
32
 
33
33
  std::string trim(const std::string &text) { return xt_css_trim(text); }
34
34
 
35
- /** Split `a, b` / `a b` / `a,b` into its arguments. */
35
+ /** Split `a, b` / `a b` / `a,b` into its arguments, keeping a nested
36
+ * `calc(10px + 2px)` argument together. */
36
37
  std::vector<std::string> splitArgs(const std::string &text) {
37
38
  std::vector<std::string> parts;
38
- size_t index = 0;
39
- while (index < text.size()) {
40
- while (index < text.size() && (isSpace(text[index]) || text[index] == ',')) index++;
41
- size_t start = index;
42
- while (index < text.size() && !isSpace(text[index]) && text[index] != ',') index++;
43
- if (index > start) parts.push_back(text.substr(start, index - start));
39
+ std::string current;
40
+ int depth = 0;
41
+ for (char c : text) {
42
+ if (c == '(') depth++;
43
+ if (c == ')') depth--;
44
+ if ((isSpace(c) || c == ',') && depth == 0) {
45
+ if (!current.empty()) {
46
+ parts.push_back(current);
47
+ current.clear();
48
+ }
49
+ } else {
50
+ current.push_back(c);
51
+ }
44
52
  }
53
+ if (!current.empty()) parts.push_back(current);
45
54
  return parts;
46
55
  }
47
56
 
@@ -134,6 +143,24 @@ bool applyFunction(const std::string &name, const std::vector<std::string> &args
134
143
  op->kind = TransformKind::Rotate;
135
144
  return parseAngleArg(args[0], &op->angle);
136
145
  }
146
+ if (name == "skew") {
147
+ if (args.empty() || args.size() > 2) return false;
148
+ op->kind = TransformKind::Skew;
149
+ if (!parseAngleArg(args[0], &op->skew_x)) return false;
150
+ op->skew_y = 0.0f;
151
+ if (args.size() > 1 && !parseAngleArg(args[1], &op->skew_y)) return false;
152
+ return true;
153
+ }
154
+ if (name == "skewx") {
155
+ if (args.size() != 1) return false;
156
+ op->kind = TransformKind::SkewX;
157
+ return parseAngleArg(args[0], &op->skew_x);
158
+ }
159
+ if (name == "skewy") {
160
+ if (args.size() != 1) return false;
161
+ op->kind = TransformKind::SkewY;
162
+ return parseAngleArg(args[0], &op->skew_y);
163
+ }
137
164
  if (name == "matrix") {
138
165
  if (args.size() != 6) return false;
139
166
  op->kind = TransformKind::Matrix;
@@ -142,7 +169,7 @@ bool applyFunction(const std::string &name, const std::vector<std::string> &args
142
169
  }
143
170
  return true;
144
171
  }
145
- return false; // skew/perspective/… are not supported yet
172
+ return false; // perspective/… are not supported yet
146
173
  }
147
174
 
148
175
  float resolveLength(const Length &length, float base, float em, float vw, float vh) {
@@ -150,7 +177,13 @@ float resolveLength(const Length &length, float base, float em, float vw, float
150
177
  }
151
178
 
152
179
  bool sameLength(const Length &left, const Length &right) {
153
- return left.unit == right.unit && left.value == right.value;
180
+ if (left.unit != right.unit || left.value != right.value || left.op != right.op ||
181
+ left.args.size() != right.args.size())
182
+ return false;
183
+ for (size_t i = 0; i < left.args.size(); i++) {
184
+ if (!sameLength(left.args[i], right.args[i])) return false;
185
+ }
186
+ return true;
154
187
  }
155
188
 
156
189
  bool sameOp(const TransformOp &left, const TransformOp &right) {
@@ -170,6 +203,12 @@ bool sameOp(const TransformOp &left, const TransformOp &right) {
170
203
  return left.scale_y == right.scale_y;
171
204
  case TransformKind::Rotate:
172
205
  return left.angle == right.angle;
206
+ case TransformKind::Skew:
207
+ return left.skew_x == right.skew_x && left.skew_y == right.skew_y;
208
+ case TransformKind::SkewX:
209
+ return left.skew_x == right.skew_x;
210
+ case TransformKind::SkewY:
211
+ return left.skew_y == right.skew_y;
173
212
  case TransformKind::Matrix:
174
213
  for (int i = 0; i < 6; i++) {
175
214
  if (left.matrix[i] != right.matrix[i]) return false;
@@ -181,6 +220,25 @@ bool sameOp(const TransformOp &left, const TransformOp &right) {
181
220
 
182
221
  bool mixLength(const Length &from, const Length &to, float t, Length *out) {
183
222
  if (from.unit != to.unit) return false;
223
+ if (from.unit == Unit::Calc) {
224
+ if (from.op != to.op || from.args.size() != to.args.size()) return false;
225
+ out->unit = Unit::Calc;
226
+ out->op = from.op;
227
+ out->args.resize(from.args.size());
228
+ for (size_t i = 0; i < from.args.size(); i++) {
229
+ if (!mixLength(from.args[i], to.args[i], t, &out->args[i])) return false;
230
+ }
231
+ return true;
232
+ }
233
+ if (from.unit == Unit::Clamp || from.unit == Unit::Min || from.unit == Unit::Max) {
234
+ if (from.args.size() != to.args.size()) return false;
235
+ out->unit = from.unit;
236
+ out->args.resize(from.args.size());
237
+ for (size_t i = 0; i < from.args.size(); i++) {
238
+ if (!mixLength(from.args[i], to.args[i], t, &out->args[i])) return false;
239
+ }
240
+ return true;
241
+ }
184
242
  out->unit = from.unit;
185
243
  out->value = from.value + (to.value - from.value) * t;
186
244
  return true;
@@ -308,6 +366,16 @@ bool xt_transform_interpolate(const std::vector<TransformOp> &from,
308
366
  case TransformKind::Rotate:
309
367
  op.angle = left.angle + (right.angle - left.angle) * t;
310
368
  break;
369
+ case TransformKind::Skew:
370
+ op.skew_x = left.skew_x + (right.skew_x - left.skew_x) * t;
371
+ op.skew_y = left.skew_y + (right.skew_y - left.skew_y) * t;
372
+ break;
373
+ case TransformKind::SkewX:
374
+ op.skew_x = left.skew_x + (right.skew_x - left.skew_x) * t;
375
+ break;
376
+ case TransformKind::SkewY:
377
+ op.skew_y = left.skew_y + (right.skew_y - left.skew_y) * t;
378
+ break;
311
379
  case TransformKind::Matrix:
312
380
  for (int entry = 0; entry < 6; entry++) {
313
381
  op.matrix[entry] = left.matrix[entry] + (right.matrix[entry] - left.matrix[entry]) * t;
@@ -349,6 +417,16 @@ Matrix2D xt_transform_matrix(const std::vector<TransformOp> &ops, float percent_
349
417
  case TransformKind::Rotate:
350
418
  step = Matrix2D::rotate(op.angle);
351
419
  break;
420
+ case TransformKind::Skew:
421
+ step.c = std::tan(op.skew_x);
422
+ step.b = std::tan(op.skew_y);
423
+ break;
424
+ case TransformKind::SkewX:
425
+ step.c = std::tan(op.skew_x);
426
+ break;
427
+ case TransformKind::SkewY:
428
+ step.b = std::tan(op.skew_y);
429
+ break;
352
430
  case TransformKind::Matrix:
353
431
  step.a = op.matrix[0];
354
432
  step.b = op.matrix[1];
@@ -396,6 +474,26 @@ std::string xt_transform_to_string(const std::vector<TransformOp> &ops) {
396
474
  text += buffer;
397
475
  break;
398
476
  }
477
+ case TransformKind::Skew: {
478
+ char buffer[96];
479
+ std::snprintf(buffer, sizeof(buffer), "skew(%.4gdeg, %.4gdeg)",
480
+ (double)(op.skew_x * 180.0f / kPi),
481
+ (double)(op.skew_y * 180.0f / kPi));
482
+ text += buffer;
483
+ break;
484
+ }
485
+ case TransformKind::SkewX: {
486
+ char buffer[64];
487
+ std::snprintf(buffer, sizeof(buffer), "skewX(%.4gdeg)", (double)(op.skew_x * 180.0f / kPi));
488
+ text += buffer;
489
+ break;
490
+ }
491
+ case TransformKind::SkewY: {
492
+ char buffer[64];
493
+ std::snprintf(buffer, sizeof(buffer), "skewY(%.4gdeg)", (double)(op.skew_y * 180.0f / kPi));
494
+ text += buffer;
495
+ break;
496
+ }
399
497
  case TransformKind::Matrix: {
400
498
  char buffer[160];
401
499
  std::snprintf(buffer, sizeof(buffer), "matrix(%.6g, %.6g, %.6g, %.6g, %.6g, %.6g)",
@@ -67,6 +67,9 @@ enum class TransformKind {
67
67
  ScaleX,
68
68
  ScaleY,
69
69
  Rotate,
70
+ Skew,
71
+ SkewX,
72
+ SkewY,
70
73
  Matrix,
71
74
  };
72
75
 
@@ -79,6 +82,8 @@ struct TransformOp {
79
82
  float scale_x = 1.0f; // scale x (a unitless factor)
80
83
  float scale_y = 1.0f; // scale y
81
84
  float angle = 0.0f; // rotate, in radians
85
+ float skew_x = 0.0f; // skew X angle, in radians
86
+ float skew_y = 0.0f; // skew Y angle, in radians
82
87
  float matrix[6] = {1, 0, 0, 1, 0, 0}; // a b c d e f
83
88
  };
84
89
 
@@ -0,0 +1,19 @@
1
+ /*
2
+ * xbintsc GUI engine — core-side registry for the optional WebGL table.
3
+ *
4
+ * Kept in `gui.a` and free of any `webgl.h`/SDL type: the table is installed by
5
+ * the `webgl` feature's bridge when that feature is linked, and stays null
6
+ * otherwise. See `webgl_api.h`.
7
+ */
8
+
9
+ #include "webgl_api.h"
10
+
11
+ namespace {
12
+
13
+ const XtWebglApi *g_api = nullptr;
14
+
15
+ } // namespace
16
+
17
+ void xt_webgl_api_install(const XtWebglApi *api) { g_api = api; }
18
+
19
+ const XtWebglApi *xt_webgl_api() { return g_api; }
@@ -0,0 +1,53 @@
1
+ /*
2
+ * xbintsc GUI engine — optional WebGL context/API table.
3
+ *
4
+ * The WebGL DOM bindings (`webgl_*.cpp`, `dom_api_webgl*.cpp`, `gui_webgl.cpp`)
5
+ * are a large part of the engine. A page that never calls
6
+ * `getContext("webgl…")` should not link them, so the core reaches them through
7
+ * this table instead of direct references. The table is installed by the
8
+ * `webgl` link feature's bridge; without it, `getContext("webgl")` answers null
9
+ * and the WebGL host types are undefined, while the 2D canvas works normally.
10
+ */
11
+ #ifndef XT_GUI_WEBGL_API_H
12
+ #define XT_GUI_WEBGL_API_H
13
+
14
+ #include <cstdint>
15
+
16
+ #include "rt.h"
17
+
18
+ struct XtGuiWindow;
19
+
20
+ struct XtWebglApi {
21
+ /** Create/find the context of a canvas; `node` is a `xtgui::Node*`. */
22
+ void *(*get)(XtGuiWindow *win, const void *node, bool webgl2);
23
+ /** The existing context of a canvas, or null (no side effects). */
24
+ void *(*find)(XtGuiWindow *win, const void *node);
25
+ /** Drop a canvas's context (called when its handle is forgotten). */
26
+ void (*forget)(XtGuiWindow *win, const void *node);
27
+ /** Release every context of a window. */
28
+ void (*shutdown)(XtGuiWindow *win);
29
+ /** Submit every live context's queued commands; true when any exists. */
30
+ bool (*submit_all)();
31
+ /** Width, height and render id of a context. */
32
+ void (*info)(void *context, float *width, float *height, uint64_t *id);
33
+ /** Resize a context (the `canvas.width`/`height` setters). */
34
+ void (*resize)(void *context, int width, int height);
35
+ /** Read RGBA pixels (GL order) from a context, for `createImageBitmap`. */
36
+ bool (*read_pixels)(void *context, int x, int y, int width, int height, unsigned char *out);
37
+ /** Wrap a context in a DOM handle; `self` is the canvas element handle. */
38
+ xt_value (*handle)(xt_value self, void *context);
39
+ /** The WebGL 1/2 context prototype. */
40
+ xt_value (*proto)(bool webgl2);
41
+ /** A WebGL object prototype (`buffer`, `texture`, `shader`, …). */
42
+ xt_value (*object_proto)(const char *kind);
43
+ /** The colour texture of a context by render id, for the paint pipeline. */
44
+ void *(*color_texture)(uint64_t id);
45
+ };
46
+
47
+ /** Install the table. Called by the `webgl` feature bridge before `main`. */
48
+ void xt_webgl_api_install(const XtWebglApi *api);
49
+
50
+ /** The installed table, or null when the `webgl` feature was not linked. */
51
+ const XtWebglApi *xt_webgl_api();
52
+
53
+ #endif /* XT_GUI_WEBGL_API_H */
@@ -0,0 +1,25 @@
1
+ /*
2
+ * xbintsc GUI engine — `webgl` link feature registration bridge.
3
+ *
4
+ * Compiled and linked only when the compiler detects that the program may ask
5
+ * for a WebGL context. Its constructor calls `xt_webgl_feature_register`, which
6
+ * installs `XtWebglApi` and references the WebGL context/DOM bindings, pulling
7
+ * them out of `gui.a`. Without this object the table stays null and those
8
+ * members are never linked.
9
+ */
10
+
11
+ extern void xt_webgl_feature_register(void);
12
+
13
+ #if defined(__GNUC__) || defined(__clang__)
14
+ /* clang targeting Windows (the toolchain xbintsc uses there) implements this
15
+ * attribute with a `.CRT$XCU` entry, which the MSVC CRT runs before `main`. */
16
+ __attribute__((constructor)) static void xt_webgl_feature_boot(void) {
17
+ xt_webgl_feature_register();
18
+ }
19
+ #elif defined(_MSC_VER)
20
+ void xt_webgl_feature_boot(void);
21
+ #pragma section(".CRT$XCU", read)
22
+ __declspec(allocate(".CRT$XCU")) static void (*const xt_webgl_feature_ctor)(void) =
23
+ xt_webgl_feature_boot;
24
+ void xt_webgl_feature_boot(void) { xt_webgl_feature_register(); }
25
+ #endif