xbintsc 0.3.25 → 0.3.46

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 (187) hide show
  1. package/README.md +39 -3
  2. package/dist/src/cli/main.js +124 -30
  3. package/dist/src/cli/main.js.map +1 -1
  4. package/dist/src/codegen/generator/calls/access.js +8 -1
  5. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  6. package/dist/src/codegen/generator/module.js +10 -0
  7. package/dist/src/codegen/generator/module.js.map +1 -1
  8. package/dist/src/codegen/generator/state.js +24 -0
  9. package/dist/src/codegen/generator/state.js.map +1 -1
  10. package/dist/src/codegen/generator/tables.d.ts +6 -0
  11. package/dist/src/codegen/generator/tables.js +38 -0
  12. package/dist/src/codegen/generator/tables.js.map +1 -1
  13. package/dist/src/driver/bundler/commonjs.d.ts +52 -0
  14. package/dist/src/driver/bundler/commonjs.js +493 -0
  15. package/dist/src/driver/bundler/commonjs.js.map +1 -0
  16. package/dist/src/driver/bundler/graph.js +14 -0
  17. package/dist/src/driver/bundler/graph.js.map +1 -1
  18. package/dist/src/driver/bundler/merge.js +14 -0
  19. package/dist/src/driver/bundler/merge.js.map +1 -1
  20. package/dist/src/driver/bundler/types.d.ts +8 -0
  21. package/dist/src/driver/compiler.d.ts +8 -1
  22. package/dist/src/driver/compiler.js +73 -5
  23. package/dist/src/driver/compiler.js.map +1 -1
  24. package/dist/src/driver/config.d.ts +53 -0
  25. package/dist/src/driver/config.js +141 -0
  26. package/dist/src/driver/config.js.map +1 -0
  27. package/dist/src/driver/icon.d.ts +62 -0
  28. package/dist/src/driver/icon.js +151 -0
  29. package/dist/src/driver/icon.js.map +1 -0
  30. package/dist/src/driver/index.d.ts +4 -0
  31. package/dist/src/driver/index.js +4 -0
  32. package/dist/src/driver/index.js.map +1 -1
  33. package/dist/src/driver/mac-bundle.d.ts +41 -0
  34. package/dist/src/driver/mac-bundle.js +123 -0
  35. package/dist/src/driver/mac-bundle.js.map +1 -0
  36. package/dist/src/driver/win-icon.d.ts +27 -0
  37. package/dist/src/driver/win-icon.js +80 -0
  38. package/dist/src/driver/win-icon.js.map +1 -0
  39. package/dist/src/parser/statements.js +17 -4
  40. package/dist/src/parser/statements.js.map +1 -1
  41. package/dist/tests/cli/main.test.js +46 -1
  42. package/dist/tests/cli/main.test.js.map +1 -1
  43. package/dist/tests/driver/config.test.d.ts +1 -0
  44. package/dist/tests/driver/config.test.js +124 -0
  45. package/dist/tests/driver/config.test.js.map +1 -0
  46. package/dist/tests/driver/icon.test.d.ts +1 -0
  47. package/dist/tests/driver/icon.test.js +421 -0
  48. package/dist/tests/driver/icon.test.js.map +1 -0
  49. package/dist/tests/driver/modules.test.js +109 -0
  50. package/dist/tests/driver/modules.test.js.map +1 -1
  51. package/dist/tests/e2e/fetch.test.d.ts +12 -0
  52. package/dist/tests/e2e/fetch.test.js +272 -0
  53. package/dist/tests/e2e/fetch.test.js.map +1 -0
  54. package/dist/tests/e2e/gc.test.d.ts +1 -0
  55. package/dist/tests/e2e/gc.test.js +168 -0
  56. package/dist/tests/e2e/gc.test.js.map +1 -0
  57. package/dist/tests/e2e/gui-dom.test.d.ts +5 -0
  58. package/dist/tests/e2e/gui-dom.test.js +102 -0
  59. package/dist/tests/e2e/gui-dom.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-events.test.d.ts +5 -0
  61. package/dist/tests/e2e/gui-events.test.js +60 -0
  62. package/dist/tests/e2e/gui-events.test.js.map +1 -0
  63. package/dist/tests/e2e/gui-helpers.d.ts +31 -0
  64. package/dist/tests/e2e/gui-helpers.js +74 -0
  65. package/dist/tests/e2e/gui-helpers.js.map +1 -0
  66. package/dist/tests/e2e/gui-layout.test.d.ts +4 -0
  67. package/dist/tests/e2e/gui-layout.test.js +88 -0
  68. package/dist/tests/e2e/gui-layout.test.js.map +1 -0
  69. package/dist/tests/e2e/gui-lifecycle.test.d.ts +5 -0
  70. package/dist/tests/e2e/gui-lifecycle.test.js +58 -0
  71. package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -0
  72. package/dist/tests/e2e/gui-paint.test.d.ts +5 -0
  73. package/dist/tests/e2e/gui-paint.test.js +111 -0
  74. package/dist/tests/e2e/gui-paint.test.js.map +1 -0
  75. package/dist/tests/e2e/gui-script.test.d.ts +5 -0
  76. package/dist/tests/e2e/gui-script.test.js +78 -0
  77. package/dist/tests/e2e/gui-script.test.js.map +1 -0
  78. package/dist/tests/e2e/gui-style.test.d.ts +5 -0
  79. package/dist/tests/e2e/gui-style.test.js +175 -0
  80. package/dist/tests/e2e/gui-style.test.js.map +1 -0
  81. package/dist/tests/e2e/harness.d.ts +2 -0
  82. package/dist/tests/e2e/harness.js +1 -0
  83. package/dist/tests/e2e/harness.js.map +1 -1
  84. package/dist/tests/e2e/icon.test.d.ts +7 -0
  85. package/dist/tests/e2e/icon.test.js +80 -0
  86. package/dist/tests/e2e/icon.test.js.map +1 -0
  87. package/dist/tests/e2e/npm-modules.test.d.ts +4 -1
  88. package/dist/tests/e2e/npm-modules.test.js +47 -1
  89. package/dist/tests/e2e/npm-modules.test.js.map +1 -1
  90. package/dist/tests/e2e/runtime-coverage-typed-array.test.d.ts +9 -0
  91. package/dist/tests/e2e/runtime-coverage-typed-array.test.js +140 -0
  92. package/dist/tests/e2e/runtime-coverage-typed-array.test.js.map +1 -0
  93. package/dist/tests/parser/statements.test.js +11 -0
  94. package/dist/tests/parser/statements.test.js.map +1 -1
  95. package/package.json +3 -2
  96. package/runtime/ext_gui/css.cpp +4 -517
  97. package/runtime/ext_gui/css_internal.h +27 -0
  98. package/runtime/ext_gui/css_match.cpp +195 -0
  99. package/runtime/ext_gui/css_selector.cpp +207 -0
  100. package/runtime/ext_gui/css_sheet.cpp +129 -0
  101. package/runtime/ext_gui/dom.cpp +7 -403
  102. package/runtime/ext_gui/dom_api.cpp +63 -1146
  103. package/runtime/ext_gui/dom_api_document.cpp +96 -0
  104. package/runtime/ext_gui/dom_api_events.cpp +123 -0
  105. package/runtime/ext_gui/dom_api_internal.h +135 -0
  106. package/runtime/ext_gui/dom_api_node.cpp +329 -0
  107. package/runtime/ext_gui/dom_api_node_read.cpp +294 -0
  108. package/runtime/ext_gui/dom_api_proto.cpp +135 -0
  109. package/runtime/ext_gui/dom_api_style.cpp +192 -0
  110. package/runtime/ext_gui/dom_internal.h +39 -0
  111. package/runtime/ext_gui/dom_parse.cpp +348 -0
  112. package/runtime/ext_gui/dom_serialize.cpp +54 -0
  113. package/runtime/ext_gui/gui.cpp +56 -286
  114. package/runtime/ext_gui/gui_events.cpp +294 -0
  115. package/runtime/ext_gui/gui_internal.h +29 -0
  116. package/runtime/ext_gui/layout.cpp +6 -597
  117. package/runtime/ext_gui/layout_flex.cpp +200 -0
  118. package/runtime/ext_gui/layout_flow.cpp +191 -0
  119. package/runtime/ext_gui/layout_flow_internal.h +149 -0
  120. package/runtime/ext_gui/layout_inline.cpp +181 -0
  121. package/runtime/ext_gui/layout_internal.h +20 -0
  122. package/runtime/ext_gui/renderer.cpp +15 -679
  123. package/runtime/ext_gui/renderer_image.cpp +233 -0
  124. package/runtime/ext_gui/renderer_internal.h +112 -0
  125. package/runtime/ext_gui/renderer_shaders.h +108 -0
  126. package/runtime/ext_gui/renderer_text.cpp +342 -0
  127. package/runtime/ext_gui/style.cpp +12 -660
  128. package/runtime/ext_gui/style_internal.h +33 -0
  129. package/runtime/ext_gui/style_print.cpp +113 -0
  130. package/runtime/ext_gui/style_ua.cpp +98 -0
  131. package/runtime/ext_gui/style_values.cpp +468 -0
  132. package/runtime/ext_gui/window.cpp +1 -0
  133. package/runtime/ext_node/buffer/parts/prototype.inc +1 -0
  134. package/runtime/ext_node/dgram/dgram.c +1 -0
  135. package/runtime/ext_node/events/events.c +1 -0
  136. package/runtime/ext_node/fs/fs_ops.c +10 -25
  137. package/runtime/ext_node/fs/glob.c +13 -30
  138. package/runtime/ext_node/fs/promises.c +1 -0
  139. package/runtime/ext_node/http/parts/prototypes.inc +5 -0
  140. package/runtime/ext_node/net/parts/prototypes.inc +2 -0
  141. package/runtime/ext_node/process/process.c +9 -7
  142. package/runtime/ext_node/stream/stream.c +1 -0
  143. package/runtime/ext_node/util/util.c +6 -6
  144. package/runtime/rt.h +16 -0
  145. package/runtime/rt_internal.h +67 -2
  146. package/runtime/xt_alloc.c +442 -5
  147. package/runtime/xt_builtins.c +1 -0
  148. package/runtime/xt_containers/access.inc +32 -1
  149. package/runtime/xt_fetch/buffer.inc +64 -0
  150. package/runtime/xt_fetch/headers.inc +312 -0
  151. package/runtime/xt_fetch/request.inc +386 -0
  152. package/runtime/xt_fetch/response.inc +107 -0
  153. package/runtime/xt_fetch/socket.inc +72 -0
  154. package/runtime/xt_fetch/url.inc +108 -0
  155. package/runtime/xt_fetch.c +39 -0
  156. package/runtime/xt_generator.c +95 -1
  157. package/runtime/xt_loop.c +35 -1
  158. package/runtime/xt_promise.c +46 -0
  159. package/runtime/xt_stdlib2/error.inc +1 -0
  160. package/runtime/xt_stdlib2/regexp-match.inc +8 -7
  161. package/runtime/xt_stdlib2/set.inc +1 -1
  162. package/runtime/xt_symbol.c +2 -0
  163. package/runtime/xt_typed_array/construction.inc +142 -0
  164. package/runtime/xt_typed_array/elements.inc +92 -0
  165. package/runtime/xt_typed_array/methods.inc +329 -0
  166. package/runtime/xt_typed_array.c +72 -0
  167. package/runtime/xt_values/number-format.inc +26 -0
  168. package/scripts/check-file-length.ts +85 -0
  169. package/src/cli/main.ts +137 -29
  170. package/src/codegen/generator/calls/access.ts +9 -1
  171. package/src/codegen/generator/module.ts +10 -0
  172. package/src/codegen/generator/state.ts +24 -0
  173. package/src/codegen/generator/tables.ts +40 -0
  174. package/src/driver/bundler/commonjs.ts +553 -0
  175. package/src/driver/bundler/graph.ts +14 -0
  176. package/src/driver/bundler/merge.ts +15 -0
  177. package/src/driver/bundler/types.ts +8 -0
  178. package/src/driver/compiler.ts +83 -5
  179. package/src/driver/config.ts +173 -0
  180. package/src/driver/icon.ts +184 -0
  181. package/src/driver/index.ts +30 -0
  182. package/src/driver/mac-bundle.ts +156 -0
  183. package/src/driver/win-icon.ts +96 -0
  184. package/src/parser/statements.ts +17 -4
  185. package/dist/tests/e2e/gui.test.d.ts +0 -12
  186. package/dist/tests/e2e/gui.test.js +0 -702
  187. package/dist/tests/e2e/gui.test.js.map +0 -1
@@ -1,134 +1,26 @@
1
1
  /*
2
- * xbintsc GUI engine — SDL_GPU 2D renderer implementation.
2
+ * xbintsc GUI engine — SDL_GPU 2D renderer: shared state, shape pipeline and
3
+ * the public geometry API.
4
+ *
5
+ * Text (glyph atlas) lives in `renderer_text.cpp` and image textures in
6
+ * `renderer_image.cpp`; both are declared in `renderer_internal.h`.
3
7
  */
4
8
 
5
- #include "renderer.h"
9
+ #include "renderer_internal.h"
6
10
 
7
11
  #include <algorithm>
8
12
  #include <cstddef>
9
13
  #include <cstdint>
10
14
  #include <cstdio>
11
15
  #include <cstring>
12
- #include <functional>
13
- #include <unordered_map>
14
- #include <vector>
15
16
 
16
17
  #include "paint.h"
17
- #include "image.h"
18
18
  #include "text.h"
19
19
 
20
- namespace {
20
+ #include "renderer_shaders.h"
21
21
 
22
- /* Rounded-rectangle fill. Vertex attributes (matching SDL_GPUVertexAttribute):
23
- * 0 -> position (pixels, viewport-relative)
24
- * 1 -> local (pixels, relative to the rectangle centre)
25
- * 2 -> half (rectangle half extents, pixels)
26
- * 3 -> radius (pixels)
27
- * 4 -> colour
28
- * The push constant is the viewport size in logical pixels. */
29
- const char *const kFillMSL = R"MSL(
30
- #include <metal_stdlib>
31
- using namespace metal;
32
-
33
- struct FillIn {
34
- float2 position [[attribute(0)]];
35
- float2 local [[attribute(1)]];
36
- float2 halfsize [[attribute(2)]];
37
- float radius [[attribute(3)]];
38
- float4 color [[attribute(4)]];
39
- };
40
-
41
- struct FillOut {
42
- float4 position [[position]];
43
- float2 local;
44
- float2 halfsize;
45
- float radius;
46
- float4 color;
47
- };
48
-
49
- vertex FillOut vs_main(FillIn in [[stage_in]],
50
- constant float2 &viewport [[buffer(0)]]) {
51
- FillOut out;
52
- float2 ndc = float2(in.position.x / viewport.x * 2.0 - 1.0,
53
- 1.0 - in.position.y / viewport.y * 2.0);
54
- out.position = float4(ndc, 0.0, 1.0);
55
- out.local = in.local;
56
- out.halfsize = in.halfsize;
57
- out.radius = in.radius;
58
- out.color = in.color;
59
- return out;
60
- }
61
-
62
- fragment float4 fs_main(FillOut in [[stage_in]]) {
63
- float2 d = abs(in.local) - in.halfsize + in.radius;
64
- float dist = min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - in.radius;
65
- float aa = max(fwidth(dist), 0.0001);
66
- float alpha = clamp(0.5 - dist / aa, 0.0, 1.0);
67
- return float4(in.color.rgb, in.color.a * alpha);
68
- }
69
- )MSL";
70
-
71
- /* Glyph quads. Attributes:
72
- * 0 -> position (pixels, viewport-relative)
73
- * 1 -> uv (atlas coordinates in [0,1])
74
- * 2 -> colour
75
- * The fragment shader samples the single-channel atlas and modulates alpha. */
76
- const char *const kTextMSL = R"MSL(
77
- #include <metal_stdlib>
78
- using namespace metal;
79
-
80
- struct TextIn {
81
- float2 position [[attribute(0)]];
82
- float2 uv [[attribute(1)]];
83
- float4 color [[attribute(2)]];
84
- };
85
-
86
- struct TextOut {
87
- float4 position [[position]];
88
- float2 uv;
89
- float4 color;
90
- };
91
-
92
- vertex TextOut vs_text(TextIn in [[stage_in]],
93
- constant float2 &viewport [[buffer(0)]]) {
94
- TextOut out;
95
- float2 ndc = float2(in.position.x / viewport.x * 2.0 - 1.0,
96
- 1.0 - in.position.y / viewport.y * 2.0);
97
- out.position = float4(ndc, 0.0, 1.0);
98
- out.uv = in.uv;
99
- out.color = in.color;
100
- return out;
101
- }
102
-
103
- fragment float4 fs_text(TextOut in [[stage_in]],
104
- texture2d<float> atlas [[texture(0)]],
105
- sampler samp [[sampler(0)]]) {
106
- float coverage = atlas.sample(samp, in.uv).r;
107
- return float4(in.color.rgb, in.color.a * coverage);
108
- }
109
-
110
- /* Image quads reuse the text vertex layout but sample an RGBA texture. */
111
- fragment float4 fs_image(TextOut in [[stage_in]],
112
- texture2d<float> tex [[texture(0)]],
113
- sampler samp [[sampler(0)]]) {
114
- float4 c = tex.sample(samp, in.uv);
115
- return float4(c.rgb * in.color.rgb, c.a * in.color.a);
116
- }
117
- )MSL";
118
-
119
- struct Vertex {
120
- float x, y;
121
- float lx, ly;
122
- float hx, hy;
123
- float radius;
124
- float r, g, b, a;
125
- };
126
-
127
- struct TextVertex {
128
- float x, y;
129
- float u, v;
130
- float r, g, b, a;
131
- };
22
+ namespace xtgui {
23
+ namespace renderer_detail {
132
24
 
133
25
  SDL_GPUGraphicsPipeline *g_pipeline = nullptr;
134
26
  SDL_GPUGraphicsPipeline *g_text_pipeline = nullptr;
@@ -136,40 +28,8 @@ SDL_GPUGraphicsPipeline *g_image_pipeline = nullptr;
136
28
  SDL_GPUSampler *g_sampler = nullptr;
137
29
  int g_renderer_failed = 0;
138
30
  bool g_text_ok = false;
139
-
140
- /* Image quads are batched by texture; textures are cached by source path. */
141
- struct ImageBatch {
142
- SDL_GPUTexture *texture = nullptr;
143
- uint32_t first = 0;
144
- uint32_t count = 0;
145
- };
146
31
  std::vector<ImageBatch> g_image_batches;
147
32
  std::unordered_map<std::string, SDL_GPUTexture *> g_image_textures;
148
-
149
- /* -- glyph atlas ---------------------------------------------------------- */
150
-
151
- constexpr int kAtlasSize = 2048;
152
-
153
- struct GlyphEntry {
154
- float u0 = 0, v0 = 0, u1 = 0, v1 = 0;
155
- int width = 0, height = 0, left = 0, top = 0;
156
- };
157
-
158
- struct GlyphKey {
159
- const void *font = nullptr;
160
- uint32_t glyph = 0;
161
- bool operator==(const GlyphKey &other) const {
162
- return font == other.font && glyph == other.glyph;
163
- }
164
- };
165
-
166
- struct GlyphKeyHash {
167
- size_t operator()(const GlyphKey &key) const {
168
- return std::hash<const void *>()(key.font) * 1099511628211ull ^
169
- (size_t)(key.glyph * 2654435761u);
170
- }
171
- };
172
-
173
33
  SDL_GPUTexture *g_atlas_texture = nullptr;
174
34
  std::vector<unsigned char> g_atlas_mirror;
175
35
  std::unordered_map<GlyphKey, GlyphEntry, GlyphKeyHash> g_glyphs;
@@ -207,175 +67,6 @@ void pushRect(std::vector<Vertex> &verts, const xtgui::PaintRect &rect) {
207
67
  for (int i = 0; i < 6; i++) verts.push_back(corners[order[i]]);
208
68
  }
209
69
 
210
- void pushGlyph(std::vector<TextVertex> &verts, float x, float y, float width, float height,
211
- const GlyphEntry &entry, const xtgui::Color &color) {
212
- const float corners_x[4] = {x, x + width, x + width, x};
213
- const float corners_y[4] = {y, y, y + height, y + height};
214
- const float u[4] = {entry.u0, entry.u1, entry.u1, entry.u0};
215
- const float v[4] = {entry.v0, entry.v0, entry.v1, entry.v1};
216
- TextVertex corners[4];
217
- for (int i = 0; i < 4; i++) {
218
- corners[i].x = corners_x[i];
219
- corners[i].y = corners_y[i];
220
- corners[i].u = u[i];
221
- corners[i].v = v[i];
222
- corners[i].r = color.r;
223
- corners[i].g = color.g;
224
- corners[i].b = color.b;
225
- corners[i].a = color.a;
226
- }
227
- static const int order[6] = {0, 1, 2, 0, 2, 3};
228
- for (int i = 0; i < 6; i++) verts.push_back(corners[order[i]]);
229
- }
230
-
231
- bool ensureAtlasTexture(SDL_GPUDevice *device) {
232
- if (g_atlas_texture != nullptr) return true;
233
- SDL_GPUTextureCreateInfo info;
234
- memset(&info, 0, sizeof(info));
235
- info.type = SDL_GPU_TEXTURETYPE_2D;
236
- info.format = SDL_GPU_TEXTUREFORMAT_R8_UNORM;
237
- info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
238
- info.width = kAtlasSize;
239
- info.height = kAtlasSize;
240
- info.layer_count_or_depth = 1;
241
- info.num_levels = 1;
242
- info.sample_count = SDL_GPU_SAMPLECOUNT_1;
243
- g_atlas_texture = SDL_CreateGPUTexture(device, &info);
244
- if (g_atlas_texture == nullptr) {
245
- fprintf(stderr, "xt_gui: glyph atlas creation failed: %s\n", SDL_GetError());
246
- return false;
247
- }
248
- g_atlas_mirror.assign((size_t)kAtlasSize * kAtlasSize, 0);
249
- return true;
250
- }
251
-
252
- void markAtlasDirty(int x, int y, int width, int height) {
253
- if (!g_atlas_dirty) {
254
- g_atlas_dirty_x0 = x;
255
- g_atlas_dirty_y0 = y;
256
- g_atlas_dirty_x1 = x + width;
257
- g_atlas_dirty_y1 = y + height;
258
- g_atlas_dirty = true;
259
- return;
260
- }
261
- g_atlas_dirty_x0 = std::min(g_atlas_dirty_x0, x);
262
- g_atlas_dirty_y0 = std::min(g_atlas_dirty_y0, y);
263
- g_atlas_dirty_x1 = std::max(g_atlas_dirty_x1, x + width);
264
- g_atlas_dirty_y1 = std::max(g_atlas_dirty_y1, y + height);
265
- }
266
-
267
- /** Rasterise + pack `glyph` if new. Returns true and fills `out` when the glyph
268
- * has a bitmap; false for blank glyphs (space, missing) or a full atlas. */
269
- bool ensureGlyph(SDL_GPUDevice *device, xtgui::Font *font, uint32_t glyph, GlyphEntry &out) {
270
- GlyphKey key{font, glyph};
271
- auto found = g_glyphs.find(key);
272
- if (found != g_glyphs.end()) {
273
- out = found->second;
274
- return out.width > 0 && out.height > 0;
275
- }
276
-
277
- xtgui::GlyphImage image;
278
- if (!xtgui::xt_text_rasterize(font, glyph, image) || image.width <= 0 || image.height <= 0) {
279
- g_glyphs.emplace(key, GlyphEntry{});
280
- return false;
281
- }
282
- if (!ensureAtlasTexture(device)) return false;
283
-
284
- if (g_atlas_x + image.width > kAtlasSize) {
285
- g_atlas_x = 0;
286
- g_atlas_y += g_atlas_row_height + 1;
287
- g_atlas_row_height = 0;
288
- }
289
- if (g_atlas_y + image.height > kAtlasSize) {
290
- if (!g_atlas_full_logged) {
291
- fprintf(stderr, "xt_gui: glyph atlas full; some glyphs will not be drawn\n");
292
- g_atlas_full_logged = true;
293
- }
294
- g_glyphs.emplace(key, GlyphEntry{});
295
- return false;
296
- }
297
-
298
- int px = g_atlas_x;
299
- int py = g_atlas_y;
300
- for (int row = 0; row < image.height; row++) {
301
- std::memcpy(&g_atlas_mirror[(size_t)(py + row) * kAtlasSize + px],
302
- image.pixels.data() + (size_t)row * image.width, (size_t)image.width);
303
- }
304
- markAtlasDirty(px, py, image.width, image.height);
305
-
306
- GlyphEntry entry;
307
- entry.u0 = (float)px / kAtlasSize;
308
- entry.v0 = (float)py / kAtlasSize;
309
- entry.u1 = (float)(px + image.width) / kAtlasSize;
310
- entry.v1 = (float)(py + image.height) / kAtlasSize;
311
- entry.width = image.width;
312
- entry.height = image.height;
313
- entry.left = image.left;
314
- entry.top = image.top;
315
- g_glyphs.emplace(key, entry);
316
-
317
- g_atlas_x += image.width + 1;
318
- g_atlas_row_height = std::max(g_atlas_row_height, image.height);
319
- out = entry;
320
- return true;
321
- }
322
-
323
- void flushAtlas(SDL_GPUDevice *device) {
324
- if (!g_atlas_dirty || g_atlas_texture == nullptr) return;
325
- g_atlas_dirty = false;
326
- int x0 = g_atlas_dirty_x0;
327
- int y0 = g_atlas_dirty_y0;
328
- int width = g_atlas_dirty_x1 - g_atlas_dirty_x0;
329
- int height = g_atlas_dirty_y1 - g_atlas_dirty_y0;
330
- if (width <= 0 || height <= 0) return;
331
-
332
- uint32_t bytes = (uint32_t)(width * height);
333
- SDL_GPUTransferBufferCreateInfo transfer_info;
334
- memset(&transfer_info, 0, sizeof(transfer_info));
335
- transfer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
336
- transfer_info.size = bytes;
337
- SDL_GPUTransferBuffer *transfer = SDL_CreateGPUTransferBuffer(device, &transfer_info);
338
- if (transfer == nullptr) return;
339
- unsigned char *mapped = (unsigned char *)SDL_MapGPUTransferBuffer(device, transfer, false);
340
- if (mapped == nullptr) {
341
- SDL_ReleaseGPUTransferBuffer(device, transfer);
342
- return;
343
- }
344
- for (int row = 0; row < height; row++) {
345
- std::memcpy(mapped + (size_t)row * width,
346
- &g_atlas_mirror[(size_t)(y0 + row) * kAtlasSize + x0], (size_t)width);
347
- }
348
- SDL_UnmapGPUTransferBuffer(device, transfer);
349
-
350
- SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(device);
351
- if (cmd == nullptr) {
352
- SDL_ReleaseGPUTransferBuffer(device, transfer);
353
- return;
354
- }
355
- SDL_GPUTextureTransferInfo source;
356
- memset(&source, 0, sizeof(source));
357
- source.transfer_buffer = transfer;
358
- source.offset = 0;
359
- source.pixels_per_row = (Uint32)width;
360
- source.rows_per_layer = (Uint32)height;
361
- SDL_GPUTextureRegion destination;
362
- memset(&destination, 0, sizeof(destination));
363
- destination.texture = g_atlas_texture;
364
- destination.mip_level = 0;
365
- destination.layer = 0;
366
- destination.x = (Uint32)x0;
367
- destination.y = (Uint32)y0;
368
- destination.z = 0;
369
- destination.w = (Uint32)width;
370
- destination.h = (Uint32)height;
371
- destination.d = 1;
372
- SDL_GPUCopyPass *copy = SDL_BeginGPUCopyPass(cmd);
373
- SDL_UploadToGPUTexture(copy, &source, &destination, false);
374
- SDL_EndGPUCopyPass(copy);
375
- SDL_SubmitGPUCommandBuffer(cmd);
376
- SDL_ReleaseGPUTransferBuffer(device, transfer);
377
- }
378
-
379
70
  /** Upload `count` vertices (of `stride` bytes) into `geometry`, reusing or
380
71
  * growing its GPU buffer. Does nothing when `count` is zero. */
381
72
  void uploadVertices(SDL_GPUDevice *device, XtGuiGeometry *geometry, const void *data,
@@ -432,255 +123,11 @@ void uploadVertices(SDL_GPUDevice *device, XtGuiGeometry *geometry, const void *
432
123
  SDL_ReleaseGPUTransferBuffer(device, transfer);
433
124
  }
434
125
 
435
- SDL_GPUGraphicsPipeline *createTextPipeline(SDL_GPUDevice *device, SDL_Window *reference) {
436
- SDL_GPUShaderCreateInfo vs_info;
437
- memset(&vs_info, 0, sizeof(vs_info));
438
- vs_info.code_size = strlen(kTextMSL);
439
- vs_info.code = (const Uint8 *)kTextMSL;
440
- vs_info.entrypoint = "vs_text";
441
- vs_info.format = SDL_GPU_SHADERFORMAT_MSL;
442
- vs_info.stage = SDL_GPU_SHADERSTAGE_VERTEX;
443
- vs_info.num_uniform_buffers = 1;
444
- SDL_GPUShader *vs = SDL_CreateGPUShader(device, &vs_info);
445
-
446
- SDL_GPUShaderCreateInfo fs_info;
447
- memset(&fs_info, 0, sizeof(fs_info));
448
- fs_info.code_size = strlen(kTextMSL);
449
- fs_info.code = (const Uint8 *)kTextMSL;
450
- fs_info.entrypoint = "fs_text";
451
- fs_info.format = SDL_GPU_SHADERFORMAT_MSL;
452
- fs_info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
453
- fs_info.num_samplers = 1;
454
- SDL_GPUShader *fs = SDL_CreateGPUShader(device, &fs_info);
455
-
456
- if (vs == nullptr || fs == nullptr) {
457
- fprintf(stderr, "xt_gui: text shader creation failed: %s\n", SDL_GetError());
458
- if (vs != nullptr) SDL_ReleaseGPUShader(device, vs);
459
- if (fs != nullptr) SDL_ReleaseGPUShader(device, fs);
460
- return nullptr;
461
- }
462
-
463
- SDL_GPUVertexBufferDescription vb_desc;
464
- memset(&vb_desc, 0, sizeof(vb_desc));
465
- vb_desc.slot = 0;
466
- vb_desc.pitch = sizeof(TextVertex);
467
- vb_desc.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
468
-
469
- SDL_GPUVertexAttribute attrs[3];
470
- memset(attrs, 0, sizeof(attrs));
471
- attrs[0] = {0, 0, SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, (Uint32)offsetof(TextVertex, x)};
472
- attrs[1] = {1, 0, SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, (Uint32)offsetof(TextVertex, u)};
473
- attrs[2] = {2, 0, SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4, (Uint32)offsetof(TextVertex, r)};
474
-
475
- SDL_GPUColorTargetDescription color_target;
476
- memset(&color_target, 0, sizeof(color_target));
477
- color_target.format = SDL_GetGPUSwapchainTextureFormat(device, reference);
478
- color_target.blend_state.enable_blend = true;
479
- color_target.blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA;
480
- color_target.blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
481
- color_target.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD;
482
- color_target.blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE;
483
- color_target.blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
484
- color_target.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD;
485
-
486
- SDL_GPUGraphicsPipelineCreateInfo info;
487
- memset(&info, 0, sizeof(info));
488
- info.vertex_shader = vs;
489
- info.fragment_shader = fs;
490
- info.vertex_input_state.vertex_buffer_descriptions = &vb_desc;
491
- info.vertex_input_state.num_vertex_buffers = 1;
492
- info.vertex_input_state.vertex_attributes = attrs;
493
- info.vertex_input_state.num_vertex_attributes = 3;
494
- info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
495
- info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL;
496
- info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_NONE;
497
- info.rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE;
498
- info.multisample_state.sample_count = SDL_GPU_SAMPLECOUNT_1;
499
- info.target_info.color_target_descriptions = &color_target;
500
- info.target_info.num_color_targets = 1;
501
-
502
- SDL_GPUGraphicsPipeline *pipeline = SDL_CreateGPUGraphicsPipeline(device, &info);
503
- SDL_ReleaseGPUShader(device, vs);
504
- SDL_ReleaseGPUShader(device, fs);
505
- if (pipeline == nullptr) {
506
- fprintf(stderr, "xt_gui: text pipeline creation failed: %s\n", SDL_GetError());
507
- }
508
- return pipeline;
509
- }
510
-
511
- SDL_GPUSampler *createSampler(SDL_GPUDevice *device) {
512
- SDL_GPUSamplerCreateInfo info;
513
- memset(&info, 0, sizeof(info));
514
- info.min_filter = SDL_GPU_FILTER_LINEAR;
515
- info.mag_filter = SDL_GPU_FILTER_LINEAR;
516
- info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
517
- info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
518
- info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
519
- info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
520
- SDL_GPUSampler *sampler = SDL_CreateGPUSampler(device, &info);
521
- if (sampler == nullptr) {
522
- fprintf(stderr, "xt_gui: glyph sampler creation failed: %s\n", SDL_GetError());
523
- }
524
- return sampler;
525
- }
526
-
527
- SDL_GPUGraphicsPipeline *createImagePipeline(SDL_GPUDevice *device, SDL_Window *reference) {
528
- SDL_GPUShaderCreateInfo vs_info;
529
- memset(&vs_info, 0, sizeof(vs_info));
530
- vs_info.code_size = strlen(kTextMSL);
531
- vs_info.code = (const Uint8 *)kTextMSL;
532
- vs_info.entrypoint = "vs_text";
533
- vs_info.format = SDL_GPU_SHADERFORMAT_MSL;
534
- vs_info.stage = SDL_GPU_SHADERSTAGE_VERTEX;
535
- vs_info.num_uniform_buffers = 1;
536
- SDL_GPUShader *vs = SDL_CreateGPUShader(device, &vs_info);
537
-
538
- SDL_GPUShaderCreateInfo fs_info;
539
- memset(&fs_info, 0, sizeof(fs_info));
540
- fs_info.code_size = strlen(kTextMSL);
541
- fs_info.code = (const Uint8 *)kTextMSL;
542
- fs_info.entrypoint = "fs_image";
543
- fs_info.format = SDL_GPU_SHADERFORMAT_MSL;
544
- fs_info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
545
- fs_info.num_samplers = 1;
546
- SDL_GPUShader *fs = SDL_CreateGPUShader(device, &fs_info);
547
-
548
- if (vs == nullptr || fs == nullptr) {
549
- fprintf(stderr, "xt_gui: image shader creation failed: %s\n", SDL_GetError());
550
- if (vs != nullptr) SDL_ReleaseGPUShader(device, vs);
551
- if (fs != nullptr) SDL_ReleaseGPUShader(device, fs);
552
- return nullptr;
553
- }
554
-
555
- SDL_GPUVertexBufferDescription vb_desc;
556
- memset(&vb_desc, 0, sizeof(vb_desc));
557
- vb_desc.slot = 0;
558
- vb_desc.pitch = sizeof(TextVertex);
559
- vb_desc.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
560
-
561
- SDL_GPUVertexAttribute attrs[3];
562
- memset(attrs, 0, sizeof(attrs));
563
- attrs[0] = {0, 0, SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, (Uint32)offsetof(TextVertex, x)};
564
- attrs[1] = {1, 0, SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, (Uint32)offsetof(TextVertex, u)};
565
- attrs[2] = {2, 0, SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4, (Uint32)offsetof(TextVertex, r)};
566
-
567
- SDL_GPUColorTargetDescription color_target;
568
- memset(&color_target, 0, sizeof(color_target));
569
- color_target.format = SDL_GetGPUSwapchainTextureFormat(device, reference);
570
- color_target.blend_state.enable_blend = true;
571
- color_target.blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA;
572
- color_target.blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
573
- color_target.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD;
574
- color_target.blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE;
575
- color_target.blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
576
- color_target.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD;
577
-
578
- SDL_GPUGraphicsPipelineCreateInfo info;
579
- memset(&info, 0, sizeof(info));
580
- info.vertex_shader = vs;
581
- info.fragment_shader = fs;
582
- info.vertex_input_state.vertex_buffer_descriptions = &vb_desc;
583
- info.vertex_input_state.num_vertex_buffers = 1;
584
- info.vertex_input_state.vertex_attributes = attrs;
585
- info.vertex_input_state.num_vertex_attributes = 3;
586
- info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
587
- info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL;
588
- info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_NONE;
589
- info.rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE;
590
- info.multisample_state.sample_count = SDL_GPU_SAMPLECOUNT_1;
591
- info.target_info.color_target_descriptions = &color_target;
592
- info.target_info.num_color_targets = 1;
593
-
594
- SDL_GPUGraphicsPipeline *pipeline = SDL_CreateGPUGraphicsPipeline(device, &info);
595
- SDL_ReleaseGPUShader(device, vs);
596
- SDL_ReleaseGPUShader(device, fs);
597
- if (pipeline == nullptr) {
598
- fprintf(stderr, "xt_gui: image pipeline creation failed: %s\n", SDL_GetError());
599
- }
600
- return pipeline;
601
- }
602
-
603
- /** Decode `src` (cached) and upload it as an RGBA texture (cached by path). */
604
- SDL_GPUTexture *ensureImageTexture(SDL_GPUDevice *device, const std::string &src) {
605
- auto found = g_image_textures.find(src);
606
- if (found != g_image_textures.end()) return found->second;
607
-
608
- const xtgui::Image *image = xtgui::xt_image_load(src);
609
- if (image == nullptr || image->width <= 0 || image->height <= 0) {
610
- fprintf(stderr, "xt_gui: could not decode image '%s'\n", src.c_str());
611
- g_image_textures.emplace(src, nullptr);
612
- return nullptr;
613
- }
614
-
615
- SDL_GPUTextureCreateInfo info;
616
- memset(&info, 0, sizeof(info));
617
- info.type = SDL_GPU_TEXTURETYPE_2D;
618
- info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
619
- info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
620
- info.width = (Uint32)image->width;
621
- info.height = (Uint32)image->height;
622
- info.layer_count_or_depth = 1;
623
- info.num_levels = 1;
624
- info.sample_count = SDL_GPU_SAMPLECOUNT_1;
625
- SDL_GPUTexture *texture = SDL_CreateGPUTexture(device, &info);
626
- if (texture == nullptr) {
627
- fprintf(stderr, "xt_gui: image texture creation failed: %s\n", SDL_GetError());
628
- g_image_textures.emplace(src, nullptr);
629
- return nullptr;
630
- }
631
-
632
- uint32_t bytes = (uint32_t)(image->pixels.size());
633
- SDL_GPUTransferBufferCreateInfo transfer_info;
634
- memset(&transfer_info, 0, sizeof(transfer_info));
635
- transfer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
636
- transfer_info.size = bytes;
637
- SDL_GPUTransferBuffer *transfer = SDL_CreateGPUTransferBuffer(device, &transfer_info);
638
- if (transfer == nullptr) {
639
- SDL_ReleaseGPUTexture(device, texture);
640
- g_image_textures.emplace(src, nullptr);
641
- return nullptr;
642
- }
643
- void *mapped = SDL_MapGPUTransferBuffer(device, transfer, false);
644
- std::memcpy(mapped, image->pixels.data(), bytes);
645
- SDL_UnmapGPUTransferBuffer(device, transfer);
646
-
647
- SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(device);
648
- if (cmd == nullptr) {
649
- SDL_ReleaseGPUTransferBuffer(device, transfer);
650
- SDL_ReleaseGPUTexture(device, texture);
651
- g_image_textures.emplace(src, nullptr);
652
- return nullptr;
653
- }
654
- SDL_GPUTextureTransferInfo source;
655
- memset(&source, 0, sizeof(source));
656
- source.transfer_buffer = transfer;
657
- source.offset = 0;
658
- source.pixels_per_row = (Uint32)image->width;
659
- source.rows_per_layer = (Uint32)image->height;
660
- SDL_GPUTextureRegion destination;
661
- memset(&destination, 0, sizeof(destination));
662
- destination.texture = texture;
663
- destination.mip_level = 0;
664
- destination.layer = 0;
665
- destination.x = 0;
666
- destination.y = 0;
667
- destination.z = 0;
668
- destination.w = (Uint32)image->width;
669
- destination.h = (Uint32)image->height;
670
- destination.d = 1;
671
- SDL_GPUCopyPass *copy = SDL_BeginGPUCopyPass(cmd);
672
- SDL_UploadToGPUTexture(copy, &source, &destination, false);
673
- SDL_EndGPUCopyPass(copy);
674
- SDL_SubmitGPUCommandBuffer(cmd);
675
- SDL_ReleaseGPUTransferBuffer(device, transfer);
676
-
677
- g_image_textures.emplace(src, texture);
678
- return texture;
679
- }
680
-
681
- } // namespace
126
+ } // namespace renderer_detail
127
+ } // namespace xtgui
682
128
 
683
129
  bool xt_gui_renderer_ensure(SDL_GPUDevice *device, SDL_Window *reference) {
130
+ using namespace xtgui::renderer_detail;
684
131
  if (g_pipeline != nullptr) return true;
685
132
  if (g_renderer_failed) return false;
686
133
  if ((SDL_GetGPUShaderFormats(device) & SDL_GPU_SHADERFORMAT_MSL) == 0) {
@@ -776,6 +223,7 @@ bool xt_gui_renderer_ensure(SDL_GPUDevice *device, SDL_Window *reference) {
776
223
  }
777
224
 
778
225
  void xt_gui_renderer_destroy(SDL_GPUDevice *device) {
226
+ using namespace xtgui::renderer_detail;
779
227
  if (g_pipeline != nullptr) {
780
228
  SDL_ReleaseGPUGraphicsPipeline(device, g_pipeline);
781
229
  g_pipeline = nullptr;
@@ -824,53 +272,16 @@ void xt_gui_geometry_destroy(SDL_GPUDevice *device, XtGuiGeometry *geometry) {
824
272
 
825
273
  void xt_gui_geometry_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
826
274
  const xtgui::DisplayList &list) {
275
+ using namespace xtgui::renderer_detail;
827
276
  std::vector<Vertex> verts;
828
277
  verts.reserve(list.rects.size() * 6);
829
278
  for (const xtgui::PaintRect &rect : list.rects) pushRect(verts, rect);
830
279
  uploadVertices(device, geometry, verts.data(), (uint32_t)verts.size(), sizeof(Vertex));
831
280
  }
832
281
 
833
- void xt_gui_text_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
834
- const xtgui::DisplayList &list, float scale) {
835
- if (!g_text_ok) {
836
- geometry->vertex_count = 0;
837
- return;
838
- }
839
- if (scale <= 0.0f) scale = 1.0f;
840
-
841
- std::vector<TextVertex> verts;
842
- for (const xtgui::PaintText &run : list.texts) {
843
- if (run.text.empty() || run.color.a <= 0.0f) continue;
844
- xtgui::Font *logical = xtgui::xt_text_resolve(run.font);
845
- if (logical == nullptr) continue;
846
- xtgui::FontSpec raster_spec = run.font;
847
- raster_spec.pixel_size = run.font.pixel_size * scale;
848
- xtgui::Font *raster = xtgui::xt_text_resolve(raster_spec);
849
- if (raster == nullptr) raster = logical;
850
-
851
- std::vector<xtgui::ShapedGlyph> glyphs;
852
- xtgui::xt_text_shape_run(run.text, logical, glyphs);
853
- float pen_x = run.x;
854
- for (const xtgui::ShapedGlyph &glyph : glyphs) {
855
- if (glyph.glyph != 0) {
856
- GlyphEntry entry;
857
- if (ensureGlyph(device, raster, glyph.glyph, entry)) {
858
- float quad_x = pen_x + glyph.x_offset + (float)entry.left / scale;
859
- float quad_y = run.baseline + glyph.y_offset - (float)entry.top / scale;
860
- float quad_w = (float)entry.width / scale;
861
- float quad_h = (float)entry.height / scale;
862
- pushGlyph(verts, quad_x, quad_y, quad_w, quad_h, entry, run.color);
863
- }
864
- }
865
- pen_x += glyph.x_advance;
866
- }
867
- }
868
- uploadVertices(device, geometry, verts.data(), (uint32_t)verts.size(), sizeof(TextVertex));
869
- flushAtlas(device);
870
- }
871
-
872
282
  void xt_gui_renderer_draw(SDL_GPUCommandBuffer *cmd, SDL_GPURenderPass *pass,
873
283
  XtGuiGeometry *geometry, float viewport_width, float viewport_height) {
284
+ using namespace xtgui::renderer_detail;
874
285
  if (g_pipeline == nullptr || geometry->buffer == nullptr || geometry->vertex_count == 0) return;
875
286
  if (viewport_width <= 0.0f || viewport_height <= 0.0f) return;
876
287
  SDL_BindGPUGraphicsPipeline(pass, g_pipeline);
@@ -882,78 +293,3 @@ void xt_gui_renderer_draw(SDL_GPUCommandBuffer *cmd, SDL_GPURenderPass *pass,
882
293
  SDL_PushGPUVertexUniformData(cmd, 0, viewport, sizeof(viewport));
883
294
  SDL_DrawGPUPrimitives(pass, geometry->vertex_count, 1, 0, 0);
884
295
  }
885
-
886
- void xt_gui_text_draw(SDL_GPUCommandBuffer *cmd, SDL_GPURenderPass *pass, XtGuiGeometry *geometry,
887
- float viewport_width, float viewport_height) {
888
- if (!g_text_ok || geometry->buffer == nullptr || geometry->vertex_count == 0) return;
889
- if (g_atlas_texture == nullptr) return;
890
- if (viewport_width <= 0.0f || viewport_height <= 0.0f) return;
891
- SDL_BindGPUGraphicsPipeline(pass, g_text_pipeline);
892
- SDL_GPUBufferBinding binding;
893
- binding.buffer = geometry->buffer;
894
- binding.offset = 0;
895
- SDL_BindGPUVertexBuffers(pass, 0, &binding, 1);
896
- SDL_GPUTextureSamplerBinding atlas_binding;
897
- atlas_binding.texture = g_atlas_texture;
898
- atlas_binding.sampler = g_sampler;
899
- SDL_BindGPUFragmentSamplers(pass, 0, &atlas_binding, 1);
900
- float viewport[2] = {viewport_width, viewport_height};
901
- SDL_PushGPUVertexUniformData(cmd, 0, viewport, sizeof(viewport));
902
- SDL_DrawGPUPrimitives(pass, geometry->vertex_count, 1, 0, 0);
903
- }
904
-
905
- void xt_gui_image_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
906
- const xtgui::DisplayList &list) {
907
- g_image_batches.clear();
908
- if (g_image_pipeline == nullptr) {
909
- geometry->vertex_count = 0;
910
- return;
911
- }
912
- std::vector<TextVertex> verts;
913
- verts.reserve(list.images.size() * 6);
914
- xtgui::Color white{1.0f, 1.0f, 1.0f, 1.0f};
915
- GlyphEntry full;
916
- full.u0 = 0.0f;
917
- full.v0 = 0.0f;
918
- full.u1 = 1.0f;
919
- full.v1 = 1.0f;
920
- for (const xtgui::PaintImage &image : list.images) {
921
- if (image.width <= 0.0f || image.height <= 0.0f) continue;
922
- SDL_GPUTexture *texture = ensureImageTexture(device, image.src);
923
- if (texture == nullptr) continue;
924
- uint32_t first = (uint32_t)verts.size();
925
- pushGlyph(verts, image.x, image.y, image.width, image.height, full, white);
926
- g_image_batches.push_back(ImageBatch{texture, first, 6});
927
- }
928
- uploadVertices(device, geometry, verts.data(), (uint32_t)verts.size(), sizeof(TextVertex));
929
- }
930
-
931
- void xt_gui_image_draw(SDL_GPUCommandBuffer *cmd, SDL_GPURenderPass *pass, XtGuiGeometry *geometry,
932
- float viewport_width, float viewport_height) {
933
- if (g_image_pipeline == nullptr || g_sampler == nullptr) return;
934
- if (geometry->buffer == nullptr || geometry->vertex_count == 0 || g_image_batches.empty()) return;
935
- if (viewport_width <= 0.0f || viewport_height <= 0.0f) return;
936
- SDL_BindGPUGraphicsPipeline(pass, g_image_pipeline);
937
- SDL_GPUBufferBinding binding;
938
- binding.buffer = geometry->buffer;
939
- binding.offset = 0;
940
- SDL_BindGPUVertexBuffers(pass, 0, &binding, 1);
941
- float viewport[2] = {viewport_width, viewport_height};
942
- SDL_PushGPUVertexUniformData(cmd, 0, viewport, sizeof(viewport));
943
- for (const ImageBatch &batch : g_image_batches) {
944
- if (batch.texture == nullptr) continue;
945
- SDL_GPUTextureSamplerBinding texture_binding;
946
- texture_binding.texture = batch.texture;
947
- texture_binding.sampler = g_sampler;
948
- SDL_BindGPUFragmentSamplers(pass, 0, &texture_binding, 1);
949
- SDL_DrawGPUPrimitives(pass, batch.count, 1, batch.first, 0);
950
- }
951
- }
952
-
953
- void xt_gui_image_textures_destroy(SDL_GPUDevice *device) {
954
- for (auto &entry : g_image_textures) {
955
- if (entry.second != nullptr) SDL_ReleaseGPUTexture(device, entry.second);
956
- }
957
- g_image_textures.clear();
958
- g_image_batches.clear();
959
- }