xbintsc 0.5.36 → 0.5.38
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.
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/codegen/generator/state.js +1 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.js +1 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/deferred.d.ts +63 -0
- package/dist/src/driver/bundler/deferred.js +279 -0
- package/dist/src/driver/bundler/deferred.js.map +1 -0
- package/dist/src/driver/bundler/merge.js +214 -2
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/runtime-objects.d.ts +1 -1
- package/dist/src/driver/runtime-objects.js +1 -0
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/html.js +89 -13
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/gui/index.js +13 -0
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/gui/three.d.ts +4 -3
- package/dist/src/extensions/gui/three.js +4 -3
- package/dist/src/extensions/gui/three.js.map +1 -1
- package/dist/tests/e2e/gui-browser.test.js +65 -0
- package/dist/tests/e2e/gui-browser.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
- package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
- package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
- package/dist/tests/e2e/gui-dom.test.js +215 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -1
- package/dist/tests/e2e/gui-events.test.js +112 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -1
- package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
- package/dist/tests/e2e/gui-script-import-order.test.js +161 -0
- package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
- package/dist/tests/e2e/url.test.d.ts +6 -0
- package/dist/tests/e2e/url.test.js +114 -0
- package/dist/tests/e2e/url.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.js +22 -10
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/doc/gui/api.md +38 -9
- package/doc/gui/canvas.md +49 -18
- package/doc/gui/dom.md +51 -4
- package/doc/gui/webgl.md +20 -10
- package/doc/gui-scripts.md +19 -19
- package/doc/gui.md +10 -8
- package/doc/implemented/fetch.md +5 -0
- package/doc/implemented/modules.md +13 -0
- package/doc/implemented/stdlib.md +17 -0
- package/doc/unimplemented/deviations.md +1 -0
- package/doc/zh-CN/gui/api.md +33 -4
- package/doc/zh-CN/gui/canvas.md +44 -19
- package/doc/zh-CN/gui/dom.md +45 -3
- package/doc/zh-CN/gui/webgl.md +3 -3
- package/doc/zh-CN/gui-scripts.md +14 -13
- package/doc/zh-CN/gui.md +7 -6
- package/doc/zh-CN/implemented/fetch.md +4 -0
- package/doc/zh-CN/implemented/modules.md +10 -0
- package/doc/zh-CN/implemented/stdlib.md +15 -0
- package/doc/zh-CN/unimplemented/deviations.md +1 -0
- package/package.json +7 -7
- package/runtime/ext_gui/browser.cpp +34 -49
- package/runtime/ext_gui/browser_abort.cpp +249 -0
- package/runtime/ext_gui/browser_dialogs.cpp +76 -0
- package/runtime/ext_gui/browser_media.cpp +176 -0
- package/runtime/ext_gui/browser_navigation.cpp +130 -0
- package/runtime/ext_gui/canvas.cpp +88 -30
- package/runtime/ext_gui/canvas.h +74 -9
- package/runtime/ext_gui/canvas_image_data.cpp +143 -0
- package/runtime/ext_gui/canvas_internal.h +8 -5
- package/runtime/ext_gui/canvas_pattern.cpp +90 -0
- package/runtime/ext_gui/canvas_raster.cpp +31 -9
- package/runtime/ext_gui/canvas_style.cpp +4 -2
- package/runtime/ext_gui/dom.h +4 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
- package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
- package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
- package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
- package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
- package/runtime/ext_gui/dom_api_document.cpp +58 -5
- package/runtime/ext_gui/dom_api_events.cpp +93 -17
- package/runtime/ext_gui/dom_api_internal.h +55 -2
- package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
- package/runtime/ext_gui/dom_api_node.cpp +40 -19
- package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
- package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
- package/runtime/ext_gui/dom_api_proto.cpp +35 -0
- package/runtime/ext_gui/dom_api_style.cpp +223 -13
- package/runtime/ext_gui/dom_api_webgl.cpp +10 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
- package/runtime/ext_gui/gui.cpp +59 -5
- package/runtime/ext_gui/gui_engine.h +49 -0
- package/runtime/ext_gui/gui_events.cpp +80 -9
- package/runtime/ext_gui/listener_options.h +43 -0
- package/runtime/ext_gui/paint.cpp +28 -0
- package/runtime/ext_gui/paint.h +4 -0
- package/runtime/ext_gui/script.cpp +31 -0
- package/runtime/ext_gui/webgl_glsl.h +3 -0
- package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
- package/runtime/ext_gui/webgl_msl.cpp +4 -0
- package/runtime/ext_gui/webgl_shader.cpp +12 -6
- package/runtime/ext_gui/window.cpp +14 -65
- package/runtime/ext_gui/window_events.cpp +125 -0
- package/runtime/rt.h +29 -0
- package/runtime/rt_internal.h +4 -0
- package/runtime/xt_builtins.c +0 -21
- package/runtime/xt_containers/access.inc +94 -2
- package/runtime/xt_containers/objects.inc +29 -31
- package/runtime/xt_fetch/async.inc +60 -0
- package/runtime/xt_url_object.c +463 -0
- package/src/codegen/generator/state.ts +1 -0
- package/src/codegen/generator/tables.ts +1 -0
- package/src/driver/bundler/deferred.ts +351 -0
- package/src/driver/bundler/merge.ts +219 -2
- package/src/driver/runtime-objects.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/html.ts +91 -15
- package/src/extensions/gui/index.ts +13 -0
- package/src/extensions/gui/three.ts +4 -3
package/runtime/ext_gui/canvas.h
CHANGED
|
@@ -91,15 +91,41 @@ class CanvasGradient {
|
|
|
91
91
|
std::vector<Stop> stops_;
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
class CanvasGradient;
|
|
95
|
+
class CanvasPattern;
|
|
96
|
+
|
|
97
|
+
/** What a fill or stroke paints with: a solid colour, a gradient sampled through
|
|
98
|
+
* the inverse of the current transform, or a repeating image pattern. `color`
|
|
99
|
+
* points into the canvas state and stays valid for one draw call. */
|
|
97
100
|
struct CanvasPaint {
|
|
98
101
|
const float *color = nullptr;
|
|
99
102
|
const CanvasGradient *gradient = nullptr;
|
|
103
|
+
const CanvasPattern *pattern = nullptr;
|
|
100
104
|
CanvasTransform inverse;
|
|
101
105
|
};
|
|
102
106
|
|
|
107
|
+
/**
|
|
108
|
+
* A `CanvasPattern`: an image tiled in user space with one of the four
|
|
109
|
+
* repetition modes. Owned by the canvas that created it, like a gradient, so a
|
|
110
|
+
* stale handle resolves to no pattern instead of a dangling pointer.
|
|
111
|
+
*/
|
|
112
|
+
class CanvasPattern {
|
|
113
|
+
public:
|
|
114
|
+
enum class Repeat { Both, X, Y, None };
|
|
115
|
+
|
|
116
|
+
CanvasPattern(uint64_t id, const Image &image, Repeat repeat);
|
|
117
|
+
uint64_t id() const { return id_; }
|
|
118
|
+
Repeat repeat() const { return repeat_; }
|
|
119
|
+
/** Pattern colour at a user-space point; false when the point falls outside a
|
|
120
|
+
* non-repeating pattern (the caller then paints nothing). */
|
|
121
|
+
bool color_at(float x, float y, float out[4]) const;
|
|
122
|
+
|
|
123
|
+
private:
|
|
124
|
+
uint64_t id_ = 0;
|
|
125
|
+
Image image_;
|
|
126
|
+
Repeat repeat_ = Repeat::Both;
|
|
127
|
+
};
|
|
128
|
+
|
|
103
129
|
class Canvas2D {
|
|
104
130
|
public:
|
|
105
131
|
Canvas2D(int width, int height);
|
|
@@ -114,6 +140,10 @@ class Canvas2D {
|
|
|
114
140
|
/** Resize the bitmap, discarding its contents (the DOM `canvas.width = …`
|
|
115
141
|
* behaviour). */
|
|
116
142
|
void resize(int width, int height);
|
|
143
|
+
/** `getContextAttributes().alpha`: an opaque canvas clears to opaque black
|
|
144
|
+
* instead of transparent, as the WebGL/2D context attribute specifies. */
|
|
145
|
+
void set_alpha(bool alpha);
|
|
146
|
+
bool alpha() const { return alpha_; }
|
|
117
147
|
|
|
118
148
|
/* -- state ------------------------------------------------------------- */
|
|
119
149
|
void save();
|
|
@@ -138,6 +168,11 @@ class Canvas2D {
|
|
|
138
168
|
void set_stroke_gradient(uint64_t id);
|
|
139
169
|
uint64_t fill_gradient_id() const { return state_.fill_gradient; }
|
|
140
170
|
uint64_t stroke_gradient_id() const { return state_.stroke_gradient; }
|
|
171
|
+
/** Use a pattern created by this canvas (`0` restores the solid colour). */
|
|
172
|
+
void set_fill_pattern(uint64_t id);
|
|
173
|
+
void set_stroke_pattern(uint64_t id);
|
|
174
|
+
uint64_t fill_pattern_id() const { return state_.fill_pattern; }
|
|
175
|
+
uint64_t stroke_pattern_id() const { return state_.stroke_pattern; }
|
|
141
176
|
void set_line_width(float width) { state_.line_width = width > 0.0f ? width : 0.0f; }
|
|
142
177
|
float line_width() const { return state_.line_width; }
|
|
143
178
|
void set_line_cap(const std::string &value);
|
|
@@ -165,20 +200,32 @@ class Canvas2D {
|
|
|
165
200
|
float r1);
|
|
166
201
|
CanvasGradient *find_gradient(uint64_t id) const;
|
|
167
202
|
|
|
203
|
+
/* -- patterns ---------------------------------------------------------- */
|
|
204
|
+
/** Create a tiled pattern from `image`; the canvas owns it. */
|
|
205
|
+
CanvasPattern *create_pattern(const Image &image, CanvasPattern::Repeat repeat);
|
|
206
|
+
CanvasPattern *find_pattern(uint64_t id) const;
|
|
207
|
+
|
|
168
208
|
/* -- paths ------------------------------------------------------------- */
|
|
169
209
|
void begin_path();
|
|
170
210
|
void close_path();
|
|
171
211
|
void move_to(float x, float y);
|
|
172
212
|
void line_to(float x, float y);
|
|
173
213
|
void rect(float x, float y, float width, float height);
|
|
174
|
-
|
|
214
|
+
/** Four corner radii, clockwise from the top-left (CSS/`roundRect` order). */
|
|
215
|
+
void round_rect(float x, float y, float width, float height, float radius_tl, float radius_tr,
|
|
216
|
+
float radius_br, float radius_bl);
|
|
175
217
|
void arc(float cx, float cy, float radius, float start, float end, bool counterclockwise);
|
|
176
218
|
void ellipse(float cx, float cy, float rx, float ry, float rotation, float start, float end,
|
|
177
219
|
bool counterclockwise);
|
|
178
220
|
void quadratic_curve_to(float cpx, float cpy, float x, float y);
|
|
179
221
|
void bezier_curve_to(float c1x, float c1y, float c2x, float c2y, float x, float y);
|
|
180
|
-
|
|
222
|
+
/** Fill the current path (`even_odd` selects the even-odd rule). */
|
|
223
|
+
void fill(bool even_odd = false);
|
|
181
224
|
void stroke();
|
|
225
|
+
/** Fill/stroke a path supplied by the caller (`Path2D`); the points are user
|
|
226
|
+
* space and are transformed with the current state, like the current path. */
|
|
227
|
+
void fill_paths(const std::vector<CanvasSubpath> &paths, bool even_odd);
|
|
228
|
+
void stroke_paths(const std::vector<CanvasSubpath> &paths);
|
|
182
229
|
|
|
183
230
|
/* -- rectangles -------------------------------------------------------- */
|
|
184
231
|
void clear_rect(float x, float y, float width, float height);
|
|
@@ -187,8 +234,11 @@ class Canvas2D {
|
|
|
187
234
|
|
|
188
235
|
/* -- text -------------------------------------------------------------- */
|
|
189
236
|
float measure_text(const std::string &text) const;
|
|
190
|
-
|
|
191
|
-
|
|
237
|
+
/** Draw text; a positive `max_width` condenses it horizontally so the run is
|
|
238
|
+
* at most that wide, as the canvas `fillText`/`strokeText` fourth argument
|
|
239
|
+
* requires. */
|
|
240
|
+
void fill_text(const std::string &text, float x, float y, float max_width = 0.0f);
|
|
241
|
+
void stroke_text(const std::string &text, float x, float y, float max_width = 0.0f);
|
|
192
242
|
|
|
193
243
|
/* -- images ------------------------------------------------------------ */
|
|
194
244
|
/** Draw `source` scaled into the destination rectangle, sampling the source
|
|
@@ -196,6 +246,15 @@ class Canvas2D {
|
|
|
196
246
|
void draw_image(const Image &source, float dx, float dy, float dw, float dh, float sx, float sy,
|
|
197
247
|
float sw, float sh);
|
|
198
248
|
|
|
249
|
+
/* -- pixel writes and export ------------------------------------------- */
|
|
250
|
+
/** `putImageData`: copy RGBA bytes straight into the bitmap (no transform,
|
|
251
|
+
* blending or globalAlpha), clipped to the canvas and the optional dirty
|
|
252
|
+
* rectangle. */
|
|
253
|
+
void set_image_data(const std::vector<unsigned char> &rgba, int data_width, int data_height,
|
|
254
|
+
int dx, int dy, int dirty_x, int dirty_y, int dirty_width, int dirty_height);
|
|
255
|
+
/** Encode the bitmap as a PNG (used by `toDataURL`/`toBlob`). */
|
|
256
|
+
bool encode_png(std::vector<unsigned char> *out) const;
|
|
257
|
+
|
|
199
258
|
/* -- test hook --------------------------------------------------------- */
|
|
200
259
|
/** Copy an `(x, y, w, h)` region to `out` (RGBA8, row-major). Out-of-bounds
|
|
201
260
|
* pixels are transparent. Returns false when the region is empty. */
|
|
@@ -223,6 +282,8 @@ class Canvas2D {
|
|
|
223
282
|
float stroke_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
224
283
|
uint64_t fill_gradient = 0;
|
|
225
284
|
uint64_t stroke_gradient = 0;
|
|
285
|
+
uint64_t fill_pattern = 0;
|
|
286
|
+
uint64_t stroke_pattern = 0;
|
|
226
287
|
std::string line_cap_string = "butt";
|
|
227
288
|
std::string line_join_string = "miter";
|
|
228
289
|
};
|
|
@@ -231,6 +292,8 @@ class Canvas2D {
|
|
|
231
292
|
void to_device(float x, float y, float *outX, float *outY) const;
|
|
232
293
|
/** The current path transformed into device space. */
|
|
233
294
|
std::vector<CanvasSubpath> device_subpaths() const;
|
|
295
|
+
/** Any user-space path transformed into device space. */
|
|
296
|
+
std::vector<CanvasSubpath> device_subpaths_of(const std::vector<CanvasSubpath> &paths) const;
|
|
234
297
|
/** Add one device-space polygon loop to `out`. */
|
|
235
298
|
void flatten_arc(float cx, float cy, float radius, float start, float end, bool ccw,
|
|
236
299
|
CanvasSubpath *out) const;
|
|
@@ -240,7 +303,7 @@ class Canvas2D {
|
|
|
240
303
|
/** Paint descriptors for the current state. */
|
|
241
304
|
CanvasPaint fill_paint() const;
|
|
242
305
|
CanvasPaint stroke_paint() const;
|
|
243
|
-
CanvasPaint make_paint(uint64_t gradient_id, const float color[4]) const;
|
|
306
|
+
CanvasPaint make_paint(uint64_t gradient_id, uint64_t pattern_id, const float color[4]) const;
|
|
244
307
|
void stroke_subpath(const CanvasSubpath &points, bool closed, const CanvasPaint &paint);
|
|
245
308
|
/** Stroke one polyline with caps and joins; `half` is half the device-space
|
|
246
309
|
* line width. Used by `stroke_subpath` for both solid and dashed paths. */
|
|
@@ -250,15 +313,17 @@ class Canvas2D {
|
|
|
250
313
|
void dash_polyline(const CanvasSubpath &points, bool closed,
|
|
251
314
|
std::vector<CanvasSubpath> *out) const;
|
|
252
315
|
void paint_text(const std::string &text, float x, float y, const CanvasPaint &paint,
|
|
253
|
-
bool stroke);
|
|
316
|
+
bool stroke, float max_width);
|
|
254
317
|
|
|
255
318
|
Image image_;
|
|
319
|
+
bool alpha_ = true;
|
|
256
320
|
uint64_t id_ = 0;
|
|
257
321
|
uint64_t generation_ = 0;
|
|
258
322
|
State state_;
|
|
259
323
|
std::vector<State> stack_;
|
|
260
324
|
std::vector<CanvasSubpath> path_;
|
|
261
325
|
std::unordered_map<uint64_t, std::unique_ptr<CanvasGradient>> gradients_;
|
|
326
|
+
std::unordered_map<uint64_t, std::unique_ptr<CanvasPattern>> patterns_;
|
|
262
327
|
};
|
|
263
328
|
|
|
264
329
|
/** The canvas with the given process-wide id, or null. Used by the renderer. */
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `putImageData` and PNG export for `<canvas>`.
|
|
3
|
+
*
|
|
4
|
+
* `putImageData` writes RGBA bytes straight into the bitmap: it ignores the
|
|
5
|
+
* transform, `globalAlpha` and compositing, exactly as the canvas API requires.
|
|
6
|
+
* PNG export writes the bitmap back out for `toDataURL`/`toBlob`; it uses a
|
|
7
|
+
* zlib "stored" stream (no compression) so the engine needs no deflate
|
|
8
|
+
* dependency, which is enough for the small UI snapshots pages take.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include "canvas.h"
|
|
12
|
+
|
|
13
|
+
#include <algorithm>
|
|
14
|
+
#include <cstdint>
|
|
15
|
+
#include <cstring>
|
|
16
|
+
#include <vector>
|
|
17
|
+
|
|
18
|
+
namespace xtgui {
|
|
19
|
+
|
|
20
|
+
void Canvas2D::set_image_data(const std::vector<unsigned char> &rgba, int data_width,
|
|
21
|
+
int data_height, int dx, int dy, int dirty_x, int dirty_y,
|
|
22
|
+
int dirty_width, int dirty_height) {
|
|
23
|
+
if (data_width <= 0 || data_height <= 0) return;
|
|
24
|
+
if (rgba.size() < (size_t)data_width * (size_t)data_height * 4) return;
|
|
25
|
+
int source_x0 = 0;
|
|
26
|
+
int source_y0 = 0;
|
|
27
|
+
int source_x1 = data_width;
|
|
28
|
+
int source_y1 = data_height;
|
|
29
|
+
/* The optional dirty rectangle is in image coordinates. */
|
|
30
|
+
if (dirty_width > 0 && dirty_height > 0) {
|
|
31
|
+
source_x0 = std::max(0, dirty_x);
|
|
32
|
+
source_y0 = std::max(0, dirty_y);
|
|
33
|
+
source_x1 = std::min(data_width, dirty_x + dirty_width);
|
|
34
|
+
source_y1 = std::min(data_height, dirty_y + dirty_height);
|
|
35
|
+
}
|
|
36
|
+
if (source_x1 <= source_x0 || source_y1 <= source_y0) return;
|
|
37
|
+
int dest_x0 = std::max(0, dx + source_x0);
|
|
38
|
+
int dest_y0 = std::max(0, dy + source_y0);
|
|
39
|
+
int dest_x1 = std::min(image_.width, dx + source_x1);
|
|
40
|
+
int dest_y1 = std::min(image_.height, dy + source_y1);
|
|
41
|
+
for (int py = dest_y0; py < dest_y1; py++) {
|
|
42
|
+
int source_row = py - dy;
|
|
43
|
+
const unsigned char *src =
|
|
44
|
+
rgba.data() + ((size_t)source_row * (size_t)data_width + (size_t)(dest_x0 - dx)) * 4;
|
|
45
|
+
unsigned char *dst =
|
|
46
|
+
image_.pixels.data() + ((size_t)py * (size_t)image_.width + (size_t)dest_x0) * 4;
|
|
47
|
+
memcpy(dst, src, (size_t)(dest_x1 - dest_x0) * 4);
|
|
48
|
+
}
|
|
49
|
+
generation_++;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
namespace {
|
|
53
|
+
|
|
54
|
+
uint32_t crc32_update(uint32_t crc, const unsigned char *data, size_t length) {
|
|
55
|
+
crc = ~crc;
|
|
56
|
+
for (size_t i = 0; i < length; i++) {
|
|
57
|
+
crc ^= data[i];
|
|
58
|
+
for (int bit = 0; bit < 8; bit++) {
|
|
59
|
+
crc = (crc >> 1) ^ (0xEDB88320u & (uint32_t)(-(int32_t)(crc & 1u)));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return ~crc;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
void append_u32(std::vector<unsigned char> *out, uint32_t value) {
|
|
66
|
+
out->push_back((unsigned char)(value >> 24));
|
|
67
|
+
out->push_back((unsigned char)(value >> 16));
|
|
68
|
+
out->push_back((unsigned char)(value >> 8));
|
|
69
|
+
out->push_back((unsigned char)value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
void append_chunk(std::vector<unsigned char> *out, const char type[4],
|
|
73
|
+
const std::vector<unsigned char> &data) {
|
|
74
|
+
append_u32(out, (uint32_t)data.size());
|
|
75
|
+
size_t crc_start = out->size();
|
|
76
|
+
out->insert(out->end(), type, type + 4);
|
|
77
|
+
out->insert(out->end(), data.begin(), data.end());
|
|
78
|
+
uint32_t crc = crc32_update(0, out->data() + crc_start, out->size() - crc_start);
|
|
79
|
+
append_u32(out, crc);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
uint32_t adler32(const std::vector<unsigned char> &data) {
|
|
83
|
+
uint32_t a = 1;
|
|
84
|
+
uint32_t b = 0;
|
|
85
|
+
for (unsigned char byte : data) {
|
|
86
|
+
a = (a + byte) % 65521u;
|
|
87
|
+
b = (b + a) % 65521u;
|
|
88
|
+
}
|
|
89
|
+
return (b << 16) | a;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** zlib stream with uncompressed (stored) deflate blocks. */
|
|
93
|
+
std::vector<unsigned char> zlib_store(const std::vector<unsigned char> &data) {
|
|
94
|
+
std::vector<unsigned char> out;
|
|
95
|
+
out.push_back(0x78);
|
|
96
|
+
out.push_back(0x01);
|
|
97
|
+
size_t offset = 0;
|
|
98
|
+
do {
|
|
99
|
+
size_t chunk = std::min<size_t>(65535, data.size() - offset);
|
|
100
|
+
bool last = offset + chunk >= data.size();
|
|
101
|
+
out.push_back(last ? 1 : 0);
|
|
102
|
+
out.push_back((unsigned char)(chunk & 0xFF));
|
|
103
|
+
out.push_back((unsigned char)(chunk >> 8));
|
|
104
|
+
out.push_back((unsigned char)(~chunk & 0xFF));
|
|
105
|
+
out.push_back((unsigned char)((~chunk >> 8) & 0xFF));
|
|
106
|
+
out.insert(out.end(), data.begin() + offset, data.begin() + offset + chunk);
|
|
107
|
+
offset += chunk;
|
|
108
|
+
} while (offset < data.size());
|
|
109
|
+
append_u32(&out, adler32(data));
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
} // namespace
|
|
114
|
+
|
|
115
|
+
bool Canvas2D::encode_png(std::vector<unsigned char> *out) const {
|
|
116
|
+
if (out == nullptr || image_.width <= 0 || image_.height <= 0) return false;
|
|
117
|
+
out->clear();
|
|
118
|
+
static const unsigned char signature[8] = {0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A};
|
|
119
|
+
out->insert(out->end(), signature, signature + 8);
|
|
120
|
+
|
|
121
|
+
std::vector<unsigned char> header;
|
|
122
|
+
append_u32(&header, (uint32_t)image_.width);
|
|
123
|
+
append_u32(&header, (uint32_t)image_.height);
|
|
124
|
+
header.push_back(8); // bit depth
|
|
125
|
+
header.push_back(6); // colour type: RGBA
|
|
126
|
+
header.push_back(0); // compression
|
|
127
|
+
header.push_back(0); // filter
|
|
128
|
+
header.push_back(0); // interlace
|
|
129
|
+
append_chunk(out, "IHDR", header);
|
|
130
|
+
|
|
131
|
+
std::vector<unsigned char> raw;
|
|
132
|
+
raw.reserve(((size_t)image_.width * 4 + 1) * (size_t)image_.height);
|
|
133
|
+
for (int y = 0; y < image_.height; y++) {
|
|
134
|
+
raw.push_back(0); // filter type: none
|
|
135
|
+
const unsigned char *row = image_.pixels.data() + (size_t)y * (size_t)image_.width * 4;
|
|
136
|
+
raw.insert(raw.end(), row, row + (size_t)image_.width * 4);
|
|
137
|
+
}
|
|
138
|
+
append_chunk(out, "IDAT", zlib_store(raw));
|
|
139
|
+
append_chunk(out, "IEND", std::vector<unsigned char>());
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
} // namespace xtgui
|
|
@@ -24,13 +24,14 @@ void blend_pixel(unsigned char *pixel, const float color[4], float coverage, flo
|
|
|
24
24
|
/** Fill one closed polygon loop (already in device space) with `paint`,
|
|
25
25
|
* anti-aliased, blending source-over. */
|
|
26
26
|
void fill_loop(std::vector<unsigned char> &pixels, int width, int height,
|
|
27
|
-
const std::vector<CanvasPoint> &loop, const CanvasPaint &paint, float alpha
|
|
27
|
+
const std::vector<CanvasPoint> &loop, const CanvasPaint &paint, float alpha,
|
|
28
|
+
bool even_odd = false);
|
|
28
29
|
|
|
29
30
|
/** Fill several loops as one path with the nonzero winding rule (so a contour
|
|
30
|
-
* inside another punches a hole). */
|
|
31
|
+
* inside another punches a hole), or the even-odd rule. */
|
|
31
32
|
void fill_loops(std::vector<unsigned char> &pixels, int width, int height,
|
|
32
33
|
const std::vector<std::vector<CanvasPoint>> &loops, const CanvasPaint &paint,
|
|
33
|
-
float alpha);
|
|
34
|
+
float alpha, bool even_odd = false);
|
|
34
35
|
|
|
35
36
|
/** Fill a device-space axis-aligned rectangle, anti-aliased. Used by the
|
|
36
37
|
* `fillRect`/`clearRect` fast path when the transform preserves axis
|
|
@@ -58,10 +59,12 @@ void draw_image_affine(std::vector<unsigned char> &pixels, int width, int height
|
|
|
58
59
|
float sy, float sw, float sh, const CanvasTransform &transform,
|
|
59
60
|
float alpha);
|
|
60
61
|
|
|
61
|
-
/** Composite one grayscale glyph bitmap at `(x, baselineY)` with `paint`.
|
|
62
|
+
/** Composite one grayscale glyph bitmap at `(x, baselineY)` with `paint`.
|
|
63
|
+
* `scale_x` < 1 condenses the bitmap horizontally (`fillText`'s `maxWidth`). */
|
|
62
64
|
void blit_glyph(std::vector<unsigned char> &pixels, int width, int height,
|
|
63
65
|
const std::vector<unsigned char> &glyph, int glyphWidth, int glyphHeight, int left,
|
|
64
|
-
int top, float x, float baselineY, const CanvasPaint &paint,
|
|
66
|
+
int top, float x, float baselineY, float scale_x, const CanvasPaint &paint,
|
|
67
|
+
float alpha);
|
|
65
68
|
|
|
66
69
|
} // namespace canvas_detail
|
|
67
70
|
} // namespace xtgui
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `CanvasPattern`.
|
|
3
|
+
*
|
|
4
|
+
* A pattern is an image tiled in user space with the four repetition modes.
|
|
5
|
+
* Like a gradient it is owned by the canvas that created it (and addressed by
|
|
6
|
+
* id), so a stale context handle resolves to "no pattern" instead of a dangling
|
|
7
|
+
* pointer. The rasteriser samples it through the inverse transform, which keeps
|
|
8
|
+
* the tiles attached to the shape when it is translated, scaled or rotated.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include "canvas.h"
|
|
12
|
+
|
|
13
|
+
#include <atomic>
|
|
14
|
+
#include <cmath>
|
|
15
|
+
#include <memory>
|
|
16
|
+
|
|
17
|
+
#include "canvas_internal.h"
|
|
18
|
+
|
|
19
|
+
namespace xtgui {
|
|
20
|
+
|
|
21
|
+
namespace {
|
|
22
|
+
|
|
23
|
+
std::atomic<uint64_t> g_next_pattern_id{1};
|
|
24
|
+
|
|
25
|
+
} // namespace
|
|
26
|
+
|
|
27
|
+
CanvasPattern::CanvasPattern(uint64_t id, const Image &image, Repeat repeat)
|
|
28
|
+
: id_(id), image_(image), repeat_(repeat) {}
|
|
29
|
+
|
|
30
|
+
bool CanvasPattern::color_at(float x, float y, float out[4]) const {
|
|
31
|
+
const int width = image_.width;
|
|
32
|
+
const int height = image_.height;
|
|
33
|
+
if (width <= 0 || height <= 0) return false;
|
|
34
|
+
/* Tile in user space: wrap the coordinate into one tile for the enabled
|
|
35
|
+
* axes, and reject points outside the tile for the disabled ones. */
|
|
36
|
+
bool repeatX = repeat_ == Repeat::Both || repeat_ == Repeat::X;
|
|
37
|
+
bool repeatY = repeat_ == Repeat::Both || repeat_ == Repeat::Y;
|
|
38
|
+
float u = x;
|
|
39
|
+
float v = y;
|
|
40
|
+
if (repeatX) {
|
|
41
|
+
u = x - std::floor(x / (float)width) * (float)width;
|
|
42
|
+
if (u < 0.0f) u += (float)width;
|
|
43
|
+
} else if (x < 0.0f || x >= (float)width) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (repeatY) {
|
|
47
|
+
v = y - std::floor(y / (float)height) * (float)height;
|
|
48
|
+
if (v < 0.0f) v += (float)height;
|
|
49
|
+
} else if (y < 0.0f || y >= (float)height) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
int px = (int)u;
|
|
53
|
+
int py = (int)v;
|
|
54
|
+
if (px < 0) px = 0;
|
|
55
|
+
if (py < 0) py = 0;
|
|
56
|
+
if (px >= width) px = width - 1;
|
|
57
|
+
if (py >= height) py = height - 1;
|
|
58
|
+
const unsigned char *texel =
|
|
59
|
+
image_.pixels.data() + ((size_t)py * (size_t)width + (size_t)px) * 4;
|
|
60
|
+
out[0] = (float)texel[0] / 255.0f;
|
|
61
|
+
out[1] = (float)texel[1] / 255.0f;
|
|
62
|
+
out[2] = (float)texel[2] / 255.0f;
|
|
63
|
+
out[3] = (float)texel[3] / 255.0f;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
CanvasPattern *Canvas2D::create_pattern(const Image &image, CanvasPattern::Repeat repeat) {
|
|
68
|
+
auto pattern =
|
|
69
|
+
std::make_unique<CanvasPattern>(g_next_pattern_id.fetch_add(1), image, repeat);
|
|
70
|
+
CanvasPattern *raw = pattern.get();
|
|
71
|
+
patterns_[raw->id()] = std::move(pattern);
|
|
72
|
+
return raw;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
CanvasPattern *Canvas2D::find_pattern(uint64_t id) const {
|
|
76
|
+
auto found = patterns_.find(id);
|
|
77
|
+
return found == patterns_.end() ? nullptr : found->second.get();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
void Canvas2D::set_fill_pattern(uint64_t id) {
|
|
81
|
+
state_.fill_pattern = find_pattern(id) != nullptr ? id : 0;
|
|
82
|
+
if (state_.fill_pattern != 0) state_.fill_gradient = 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void Canvas2D::set_stroke_pattern(uint64_t id) {
|
|
86
|
+
state_.stroke_pattern = find_pattern(id) != nullptr ? id : 0;
|
|
87
|
+
if (state_.stroke_pattern != 0) state_.stroke_gradient = 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
} // namespace xtgui
|
|
@@ -37,11 +37,15 @@ struct Crossing {
|
|
|
37
37
|
/** Resolve `paint` at a device-space pixel centre. Gradients are defined in
|
|
38
38
|
* user space, so the pixel is mapped back through the inverse transform. */
|
|
39
39
|
void paint_color_at(const CanvasPaint &paint, float x, float y, float out[4]) {
|
|
40
|
-
if (paint.gradient != nullptr) {
|
|
40
|
+
if (paint.pattern != nullptr || paint.gradient != nullptr) {
|
|
41
41
|
const CanvasTransform &inverse = paint.inverse;
|
|
42
42
|
float userX = inverse.a * x + inverse.c * y + inverse.e;
|
|
43
43
|
float userY = inverse.b * x + inverse.d * y + inverse.f;
|
|
44
|
-
if (paint.
|
|
44
|
+
if (paint.pattern != nullptr) {
|
|
45
|
+
if (paint.pattern->color_at(userX, userY, out)) return;
|
|
46
|
+
} else if (paint.gradient->color_at(userX, userY, out)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
45
49
|
}
|
|
46
50
|
if (paint.color != nullptr) {
|
|
47
51
|
for (int i = 0; i < 4; i++) out[i] = paint.color[i];
|
|
@@ -100,7 +104,7 @@ static void add_span(float *row, int rowOriginX, float xa, float xb, float weigh
|
|
|
100
104
|
|
|
101
105
|
void fill_loops(std::vector<unsigned char> &pixels, int width, int height,
|
|
102
106
|
const std::vector<std::vector<CanvasPoint>> &loops, const CanvasPaint &paint,
|
|
103
|
-
float alpha) {
|
|
107
|
+
float alpha, bool even_odd) {
|
|
104
108
|
if (width <= 0 || height <= 0 || loops.empty()) return;
|
|
105
109
|
bool havePoint = false;
|
|
106
110
|
float minX = 0.0f, maxX = 0.0f, minY = 0.0f, maxY = 0.0f;
|
|
@@ -154,6 +158,14 @@ void fill_loops(std::vector<unsigned char> &pixels, int width, int height,
|
|
|
154
158
|
if (crossings.empty()) continue;
|
|
155
159
|
std::sort(crossings.begin(), crossings.end(),
|
|
156
160
|
[](const Crossing &a, const Crossing &b) { return a.x < b.x; });
|
|
161
|
+
if (even_odd) {
|
|
162
|
+
/* Every second crossing toggles the covered span, regardless of the
|
|
163
|
+
* direction each contour is wound. */
|
|
164
|
+
for (size_t i = 0; i + 1 < crossings.size(); i += 2) {
|
|
165
|
+
add_span(row, x0, crossings[i].x, crossings[i + 1].x, weight, x0, x1);
|
|
166
|
+
}
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
157
169
|
int winding = 0;
|
|
158
170
|
float spanStart = 0.0f;
|
|
159
171
|
for (const Crossing &crossing : crossings) {
|
|
@@ -179,8 +191,10 @@ void fill_loops(std::vector<unsigned char> &pixels, int width, int height,
|
|
|
179
191
|
}
|
|
180
192
|
|
|
181
193
|
void fill_loop(std::vector<unsigned char> &pixels, int width, int height,
|
|
182
|
-
const std::vector<CanvasPoint> &loop, const CanvasPaint &paint, float alpha
|
|
183
|
-
|
|
194
|
+
const std::vector<CanvasPoint> &loop, const CanvasPaint &paint, float alpha,
|
|
195
|
+
bool even_odd) {
|
|
196
|
+
fill_loops(pixels, width, height, std::vector<std::vector<CanvasPoint>>{loop}, paint, alpha,
|
|
197
|
+
even_odd);
|
|
184
198
|
}
|
|
185
199
|
|
|
186
200
|
void fill_rect(std::vector<unsigned char> &pixels, int width, int height, float x0, float y0,
|
|
@@ -331,16 +345,24 @@ void draw_image_affine(std::vector<unsigned char> &pixels, int width, int height
|
|
|
331
345
|
|
|
332
346
|
void blit_glyph(std::vector<unsigned char> &pixels, int width, int height,
|
|
333
347
|
const std::vector<unsigned char> &glyph, int glyphWidth, int glyphHeight, int left,
|
|
334
|
-
int top, float x, float baselineY, const CanvasPaint &paint,
|
|
335
|
-
|
|
348
|
+
int top, float x, float baselineY, float scale_x, const CanvasPaint &paint,
|
|
349
|
+
float alpha) {
|
|
350
|
+
if (glyphWidth <= 0 || glyphHeight <= 0) return;
|
|
351
|
+
if (scale_x <= 0.0f) scale_x = 1.0f;
|
|
352
|
+
bool condense = scale_x < 0.999f;
|
|
353
|
+
int drawWidth = condense ? std::max(1, (int)std::lround((float)glyphWidth * scale_x))
|
|
354
|
+
: glyphWidth;
|
|
355
|
+
int originX = (int)std::lround(x) + (int)std::lround((float)left * scale_x);
|
|
336
356
|
int originY = (int)std::lround(baselineY) - top;
|
|
337
357
|
for (int gy = 0; gy < glyphHeight; gy++) {
|
|
338
358
|
int py = originY + gy;
|
|
339
359
|
if (py < 0 || py >= height) continue;
|
|
340
|
-
for (int gx = 0; gx <
|
|
360
|
+
for (int gx = 0; gx < drawWidth; gx++) {
|
|
341
361
|
int px = originX + gx;
|
|
342
362
|
if (px < 0 || px >= width) continue;
|
|
343
|
-
|
|
363
|
+
/* Nearest sample from the glyph coverage mask when condensing. */
|
|
364
|
+
int sourceX = condense ? std::min(glyphWidth - 1, (int)((float)gx / scale_x)) : gx;
|
|
365
|
+
float coverage = glyph[(size_t)gy * (size_t)glyphWidth + (size_t)sourceX] / 255.0f;
|
|
344
366
|
if (coverage <= 0.0f) continue;
|
|
345
367
|
float color[4];
|
|
346
368
|
paint_color_at(paint, (float)px + 0.5f, (float)py + 0.5f, color);
|
|
@@ -112,11 +112,13 @@ bool positive_finite(float value) { return std::isfinite(value) && value >= 0.0f
|
|
|
112
112
|
void Canvas2D::set_fill_style(const std::string &css) {
|
|
113
113
|
parse_color_into(css, state_.fill_color, &state_.fill_style);
|
|
114
114
|
state_.fill_gradient = 0;
|
|
115
|
+
state_.fill_pattern = 0;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
void Canvas2D::set_stroke_style(const std::string &css) {
|
|
118
119
|
parse_color_into(css, state_.stroke_color, &state_.stroke_style);
|
|
119
120
|
state_.stroke_gradient = 0;
|
|
121
|
+
state_.stroke_pattern = 0;
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
void Canvas2D::set_fill_gradient(uint64_t id) {
|
|
@@ -124,7 +126,7 @@ void Canvas2D::set_fill_gradient(uint64_t id) {
|
|
|
124
126
|
state_.fill_gradient = 0;
|
|
125
127
|
return;
|
|
126
128
|
}
|
|
127
|
-
if (find_gradient(id) != nullptr) state_.fill_gradient = id;
|
|
129
|
+
if (find_gradient(id) != nullptr) { state_.fill_gradient = id; state_.fill_pattern = 0; }
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
void Canvas2D::set_stroke_gradient(uint64_t id) {
|
|
@@ -132,7 +134,7 @@ void Canvas2D::set_stroke_gradient(uint64_t id) {
|
|
|
132
134
|
state_.stroke_gradient = 0;
|
|
133
135
|
return;
|
|
134
136
|
}
|
|
135
|
-
if (find_gradient(id) != nullptr) state_.stroke_gradient = id;
|
|
137
|
+
if (find_gradient(id) != nullptr) { state_.stroke_gradient = id; state_.stroke_pattern = 0; }
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
void Canvas2D::set_global_alpha(float alpha) {
|
package/runtime/ext_gui/dom.h
CHANGED
|
@@ -31,6 +31,10 @@ struct Node {
|
|
|
31
31
|
NodeType type = NodeType::Element;
|
|
32
32
|
std::string tag; // lowercased element name (empty for document/text)
|
|
33
33
|
std::string text; // text content (text nodes only)
|
|
34
|
+
/* `createElementNS` keeps the namespace and the qualified name's prefix.
|
|
35
|
+
* Ordinary HTML elements leave both empty and report the XHTML namespace. */
|
|
36
|
+
std::string ns;
|
|
37
|
+
std::string ns_prefix;
|
|
34
38
|
std::vector<Attr> attrs;
|
|
35
39
|
Node *parent = nullptr;
|
|
36
40
|
std::vector<std::unique_ptr<Node>> children;
|
|
@@ -32,6 +32,7 @@ xt_value g_document_proto = XT_UNDEFINED;
|
|
|
32
32
|
xt_value g_event_proto = XT_UNDEFINED;
|
|
33
33
|
xt_value g_class_list_proto = XT_UNDEFINED;
|
|
34
34
|
xt_value g_style_proto = XT_UNDEFINED;
|
|
35
|
+
xt_value g_computed_style_proto = XT_UNDEFINED;
|
|
35
36
|
xt_value g_canvas_proto = XT_UNDEFINED;
|
|
36
37
|
xt_value g_canvas_gradient_proto = XT_UNDEFINED;
|
|
37
38
|
|