xbintsc 0.5.36 → 0.5.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/index.js +12 -0
- package/dist/src/extensions/gui/index.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/doc/gui/api.md +38 -9
- package/doc/gui/canvas.md +49 -18
- package/doc/gui/dom.md +51 -4
- 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/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/gui.cpp +59 -5
- package/runtime/ext_gui/gui_engine.h +47 -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/window.cpp +14 -65
- package/runtime/ext_gui/window_events.cpp +125 -0
- package/runtime/rt.h +26 -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/src/driver/version.ts +1 -1
- package/src/extensions/gui/index.ts +12 -0
|
@@ -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
|
|
|
@@ -41,10 +41,34 @@ std::string string_arg(int32_t argc, xt_value *argv, int32_t index) {
|
|
|
41
41
|
return to_string_value(xt_arg(argc, argv, index));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/* Defined with the path bindings below; the transform methods accept a matrix
|
|
45
|
+
* argument too. */
|
|
46
|
+
/** `transform(a, b, c, d, e, f)` or `transform(matrix)` where `matrix` is a
|
|
47
|
+
* `DOMMatrix`, `{ a, b, c, d, e, f }` or a six-element array. */
|
|
48
|
+
bool canvas_matrix_arg(int32_t argc, xt_value *argv, int32_t index, float values[6]) {
|
|
49
|
+
if (index >= argc) return false;
|
|
50
|
+
xt_value value = xt_arg(argc, argv, index);
|
|
51
|
+
if (XT_IS_ARRAY(value)) {
|
|
52
|
+
if (xt_to_number(xt_array_length(value)) < 6) return false;
|
|
53
|
+
for (int i = 0; i < 6; i++) values[i] = (float)xt_to_number(xt_array_get(value, xt_number(i)));
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (!XT_IS_OBJECT(value)) return false;
|
|
57
|
+
static const char *const names[6] = {"a", "b", "c", "d", "e", "f"};
|
|
58
|
+
for (int i = 0; i < 6; i++) {
|
|
59
|
+
xt_value member = xt_object_get_cstr(value, names[i]);
|
|
60
|
+
if (!XT_IS_NUMBER(member)) return false;
|
|
61
|
+
values[i] = (float)xt_to_number(member);
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
44
66
|
} // namespace
|
|
45
67
|
|
|
46
68
|
xtgui::Canvas2D *canvas_from_context(xt_value self) { return context_canvas(self); }
|
|
47
69
|
|
|
70
|
+
xt_value canvas_context_attributes(xt_value requested, bool alpha);
|
|
71
|
+
|
|
48
72
|
double canvas_number_arg(int32_t argc, xt_value *argv, int32_t index, double fallback) {
|
|
49
73
|
return number_arg(argc, argv, index, fallback);
|
|
50
74
|
}
|
|
@@ -75,18 +99,70 @@ METHOD(node_get_context) {
|
|
|
75
99
|
if (node == nullptr) return xt_canvas_pending_context(self, win);
|
|
76
100
|
if (!node->isElement() || !node->isTag("canvas")) return XT_NULL;
|
|
77
101
|
if (xt_gui_webgl_find(win, node) != nullptr) return XT_NULL; // already a WebGL canvas
|
|
78
|
-
xtgui::Canvas2D *canvas = xt_gui_canvas_get(win, node);
|
|
79
|
-
if (canvas == nullptr) return XT_NULL;
|
|
80
102
|
xt_value cached = xt_object_get_cstr(self, "__xt_gui_ctx");
|
|
81
103
|
if (XT_IS_OBJECT(cached)) return cached;
|
|
104
|
+
xtgui::Canvas2D *canvas = xt_gui_canvas_get(win, node);
|
|
105
|
+
if (canvas == nullptr) return XT_NULL;
|
|
106
|
+
/* The second argument only takes effect when the context is created; a later
|
|
107
|
+
* `getContext("2d", ...)` returns the existing context unchanged. `alpha` is
|
|
108
|
+
* the one attribute with an observable effect on the bitmap. */
|
|
109
|
+
xt_value attributes = xt_arg(argc, argv, 1);
|
|
110
|
+
bool alpha = true;
|
|
111
|
+
if (XT_IS_OBJECT(attributes)) {
|
|
112
|
+
xt_value value = xt_object_get_cstr(attributes, "alpha");
|
|
113
|
+
if (XT_IS_BOOL(value)) alpha = value_truthy(value);
|
|
114
|
+
}
|
|
115
|
+
canvas->set_alpha(alpha);
|
|
82
116
|
xt_value ctx = xt_object_new_with_proto(canvas_proto());
|
|
83
117
|
xt_object_set(ctx, xt_string_from_cstr("__xt_gui_canvas"), xt_number((double)canvas->id()));
|
|
84
118
|
/* Keep the element handle alive (and reachable) from the context. */
|
|
85
119
|
xt_object_set(ctx, xt_string_from_cstr("__xt_gui_node"), self);
|
|
120
|
+
xt_object_set(ctx, xt_string_from_cstr("__xt_gui_ctx_attributes"),
|
|
121
|
+
canvas_context_attributes(attributes, alpha));
|
|
86
122
|
xt_object_set(self, xt_string_from_cstr("__xt_gui_ctx"), ctx);
|
|
87
123
|
return ctx;
|
|
88
124
|
}
|
|
89
125
|
|
|
126
|
+
/** The attributes object `getContextAttributes()` reports. Options the engine
|
|
127
|
+
* does not implement keep their specification default, so a feature test sees
|
|
128
|
+
* an honest answer instead of `undefined`. */
|
|
129
|
+
xt_value canvas_context_attributes(xt_value requested, bool alpha) {
|
|
130
|
+
xt_value result = xt_object_new();
|
|
131
|
+
struct Attribute {
|
|
132
|
+
const char *name;
|
|
133
|
+
bool fallback;
|
|
134
|
+
};
|
|
135
|
+
const Attribute defaults[] = {
|
|
136
|
+
{"alpha", alpha}, {"antialias", true},
|
|
137
|
+
{"depth", true}, {"stencil", false},
|
|
138
|
+
{"premultipliedAlpha", true}, {"preserveDrawingBuffer", false},
|
|
139
|
+
{"desynchronized", false}, {"willReadFrequently", false},
|
|
140
|
+
{"failIfMajorPerformanceCaveat", false},
|
|
141
|
+
};
|
|
142
|
+
for (const Attribute &attribute : defaults) {
|
|
143
|
+
xt_value value = XT_UNDEFINED;
|
|
144
|
+
if (XT_IS_OBJECT(requested)) value = xt_object_get_cstr(requested, attribute.name);
|
|
145
|
+
if (XT_IS_BOOL(value)) {
|
|
146
|
+
xt_object_set(result, xt_string_from_cstr(attribute.name), value);
|
|
147
|
+
} else {
|
|
148
|
+
xt_object_set(result, xt_string_from_cstr(attribute.name),
|
|
149
|
+
attribute.fallback ? XT_TRUE : XT_FALSE);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
xt_object_set(result, xt_string_from_cstr("powerPreference"),
|
|
153
|
+
xt_string_from_cstr("default"));
|
|
154
|
+
xt_object_set(result, xt_string_from_cstr("colorSpace"), xt_string_from_cstr("srgb"));
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
METHOD(canvas_get_context_attributes) {
|
|
159
|
+
(void)env;
|
|
160
|
+
(void)argc;
|
|
161
|
+
(void)argv;
|
|
162
|
+
xt_value stored = xt_object_get_cstr(self, "__xt_gui_ctx_attributes");
|
|
163
|
+
return XT_IS_OBJECT(stored) ? stored : canvas_context_attributes(XT_UNDEFINED, true);
|
|
164
|
+
}
|
|
165
|
+
|
|
90
166
|
METHOD(canvas_get_canvas) {
|
|
91
167
|
(void)env;
|
|
92
168
|
(void)argc;
|
|
@@ -103,6 +179,9 @@ METHOD(canvas_fill_style_get) {
|
|
|
103
179
|
if (canvas->fill_gradient_id() != 0) {
|
|
104
180
|
return canvas_gradient_handle(self, canvas, canvas->fill_gradient_id());
|
|
105
181
|
}
|
|
182
|
+
if (canvas->fill_pattern_id() != 0) {
|
|
183
|
+
return canvas_pattern_handle(self, canvas, canvas->fill_pattern_id());
|
|
184
|
+
}
|
|
106
185
|
return xt_string_from_cstr(canvas->fill_style().c_str());
|
|
107
186
|
}
|
|
108
187
|
|
|
@@ -116,6 +195,11 @@ METHOD(canvas_fill_style_set) {
|
|
|
116
195
|
canvas->set_fill_gradient(gradient);
|
|
117
196
|
return XT_UNDEFINED;
|
|
118
197
|
}
|
|
198
|
+
uint64_t pattern = canvas_pattern_id(xt_arg(argc, argv, 0), canvas);
|
|
199
|
+
if (pattern != 0) {
|
|
200
|
+
canvas->set_fill_pattern(pattern);
|
|
201
|
+
return XT_UNDEFINED;
|
|
202
|
+
}
|
|
119
203
|
}
|
|
120
204
|
canvas->set_fill_style(string_arg(argc, argv, 0));
|
|
121
205
|
return XT_UNDEFINED;
|
|
@@ -130,6 +214,9 @@ METHOD(canvas_stroke_style_get) {
|
|
|
130
214
|
if (canvas->stroke_gradient_id() != 0) {
|
|
131
215
|
return canvas_gradient_handle(self, canvas, canvas->stroke_gradient_id());
|
|
132
216
|
}
|
|
217
|
+
if (canvas->stroke_pattern_id() != 0) {
|
|
218
|
+
return canvas_pattern_handle(self, canvas, canvas->stroke_pattern_id());
|
|
219
|
+
}
|
|
133
220
|
return xt_string_from_cstr(canvas->stroke_style().c_str());
|
|
134
221
|
}
|
|
135
222
|
|
|
@@ -143,6 +230,11 @@ METHOD(canvas_stroke_style_set) {
|
|
|
143
230
|
canvas->set_stroke_gradient(gradient);
|
|
144
231
|
return XT_UNDEFINED;
|
|
145
232
|
}
|
|
233
|
+
uint64_t pattern = canvas_pattern_id(xt_arg(argc, argv, 0), canvas);
|
|
234
|
+
if (pattern != 0) {
|
|
235
|
+
canvas->set_stroke_pattern(pattern);
|
|
236
|
+
return XT_UNDEFINED;
|
|
237
|
+
}
|
|
146
238
|
}
|
|
147
239
|
canvas->set_stroke_style(string_arg(argc, argv, 0));
|
|
148
240
|
return XT_UNDEFINED;
|
|
@@ -269,23 +361,33 @@ METHOD(canvas_scale) {
|
|
|
269
361
|
METHOD(canvas_transform) {
|
|
270
362
|
(void)env;
|
|
271
363
|
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
272
|
-
if (canvas
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
364
|
+
if (canvas == nullptr) return XT_UNDEFINED;
|
|
365
|
+
float values[6];
|
|
366
|
+
if (canvas_matrix_arg(argc, argv, 0, values)) {
|
|
367
|
+
canvas->transform(values[0], values[1], values[2], values[3], values[4], values[5]);
|
|
368
|
+
return XT_UNDEFINED;
|
|
369
|
+
}
|
|
370
|
+
canvas->transform((float)number_arg(argc, argv, 0, 1.0), (float)number_arg(argc, argv, 1, 0.0),
|
|
371
|
+
(float)number_arg(argc, argv, 2, 0.0), (float)number_arg(argc, argv, 3, 1.0),
|
|
372
|
+
(float)number_arg(argc, argv, 4, 0.0), (float)number_arg(argc, argv, 5, 0.0));
|
|
276
373
|
return XT_UNDEFINED;
|
|
277
374
|
}
|
|
278
375
|
|
|
279
376
|
METHOD(canvas_set_transform) {
|
|
280
377
|
(void)env;
|
|
281
378
|
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
282
|
-
if (canvas
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
379
|
+
if (canvas == nullptr) return XT_UNDEFINED;
|
|
380
|
+
float values[6];
|
|
381
|
+
if (canvas_matrix_arg(argc, argv, 0, values)) {
|
|
382
|
+
canvas->set_transform(values[0], values[1], values[2], values[3], values[4], values[5]);
|
|
383
|
+
return XT_UNDEFINED;
|
|
384
|
+
}
|
|
385
|
+
canvas->set_transform((float)number_arg(argc, argv, 0, 1.0),
|
|
386
|
+
(float)number_arg(argc, argv, 1, 0.0),
|
|
387
|
+
(float)number_arg(argc, argv, 2, 0.0),
|
|
388
|
+
(float)number_arg(argc, argv, 3, 1.0),
|
|
389
|
+
(float)number_arg(argc, argv, 4, 0.0),
|
|
390
|
+
(float)number_arg(argc, argv, 5, 0.0));
|
|
289
391
|
return XT_UNDEFINED;
|
|
290
392
|
}
|
|
291
393
|
|
|
@@ -298,104 +400,6 @@ METHOD(canvas_reset_transform) {
|
|
|
298
400
|
return XT_UNDEFINED;
|
|
299
401
|
}
|
|
300
402
|
|
|
301
|
-
/* -- paths ---------------------------------------------------------------- */
|
|
302
|
-
|
|
303
|
-
METHOD(canvas_begin_path) {
|
|
304
|
-
(void)env;
|
|
305
|
-
(void)argc;
|
|
306
|
-
(void)argv;
|
|
307
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
308
|
-
if (canvas != nullptr) canvas->begin_path();
|
|
309
|
-
return XT_UNDEFINED;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
METHOD(canvas_close_path) {
|
|
313
|
-
(void)env;
|
|
314
|
-
(void)argc;
|
|
315
|
-
(void)argv;
|
|
316
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
317
|
-
if (canvas != nullptr) canvas->close_path();
|
|
318
|
-
return XT_UNDEFINED;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
METHOD(canvas_move_to) {
|
|
322
|
-
(void)env;
|
|
323
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
324
|
-
if (canvas != nullptr)
|
|
325
|
-
canvas->move_to((float)number_arg(argc, argv, 0, 0.0), (float)number_arg(argc, argv, 1, 0.0));
|
|
326
|
-
return XT_UNDEFINED;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
METHOD(canvas_line_to) {
|
|
330
|
-
(void)env;
|
|
331
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
332
|
-
if (canvas != nullptr)
|
|
333
|
-
canvas->line_to((float)number_arg(argc, argv, 0, 0.0), (float)number_arg(argc, argv, 1, 0.0));
|
|
334
|
-
return XT_UNDEFINED;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
METHOD(canvas_rect) {
|
|
338
|
-
(void)env;
|
|
339
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
340
|
-
if (canvas != nullptr)
|
|
341
|
-
canvas->rect((float)number_arg(argc, argv, 0, 0.0), (float)number_arg(argc, argv, 1, 0.0),
|
|
342
|
-
(float)number_arg(argc, argv, 2, 0.0), (float)number_arg(argc, argv, 3, 0.0));
|
|
343
|
-
return XT_UNDEFINED;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
METHOD(canvas_arc) {
|
|
347
|
-
(void)env;
|
|
348
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
349
|
-
if (canvas != nullptr) {
|
|
350
|
-
canvas->arc((float)number_arg(argc, argv, 0, 0.0), (float)number_arg(argc, argv, 1, 0.0),
|
|
351
|
-
(float)number_arg(argc, argv, 2, 0.0), (float)number_arg(argc, argv, 3, 0.0),
|
|
352
|
-
(float)number_arg(argc, argv, 4, 0.0), value_truthy(xt_arg(argc, argv, 5)));
|
|
353
|
-
}
|
|
354
|
-
return XT_UNDEFINED;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
METHOD(canvas_quadratic_curve_to) {
|
|
358
|
-
(void)env;
|
|
359
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
360
|
-
if (canvas != nullptr)
|
|
361
|
-
canvas->quadratic_curve_to((float)number_arg(argc, argv, 0, 0.0),
|
|
362
|
-
(float)number_arg(argc, argv, 1, 0.0),
|
|
363
|
-
(float)number_arg(argc, argv, 2, 0.0),
|
|
364
|
-
(float)number_arg(argc, argv, 3, 0.0));
|
|
365
|
-
return XT_UNDEFINED;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
METHOD(canvas_bezier_curve_to) {
|
|
369
|
-
(void)env;
|
|
370
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
371
|
-
if (canvas != nullptr)
|
|
372
|
-
canvas->bezier_curve_to((float)number_arg(argc, argv, 0, 0.0),
|
|
373
|
-
(float)number_arg(argc, argv, 1, 0.0),
|
|
374
|
-
(float)number_arg(argc, argv, 2, 0.0),
|
|
375
|
-
(float)number_arg(argc, argv, 3, 0.0),
|
|
376
|
-
(float)number_arg(argc, argv, 4, 0.0),
|
|
377
|
-
(float)number_arg(argc, argv, 5, 0.0));
|
|
378
|
-
return XT_UNDEFINED;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
METHOD(canvas_fill) {
|
|
382
|
-
(void)env;
|
|
383
|
-
(void)argc;
|
|
384
|
-
(void)argv;
|
|
385
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
386
|
-
if (canvas != nullptr) canvas->fill();
|
|
387
|
-
return XT_UNDEFINED;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
METHOD(canvas_stroke) {
|
|
391
|
-
(void)env;
|
|
392
|
-
(void)argc;
|
|
393
|
-
(void)argv;
|
|
394
|
-
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
395
|
-
if (canvas != nullptr) canvas->stroke();
|
|
396
|
-
return XT_UNDEFINED;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
403
|
METHOD(canvas_clear_rect) {
|
|
400
404
|
(void)env;
|
|
401
405
|
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
@@ -440,7 +444,8 @@ METHOD(canvas_fill_text) {
|
|
|
440
444
|
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
441
445
|
if (canvas != nullptr)
|
|
442
446
|
canvas->fill_text(string_arg(argc, argv, 0), (float)number_arg(argc, argv, 1, 0.0),
|
|
443
|
-
(float)number_arg(argc, argv, 2, 0.0)
|
|
447
|
+
(float)number_arg(argc, argv, 2, 0.0),
|
|
448
|
+
(float)number_arg(argc, argv, 3, 0.0));
|
|
444
449
|
return XT_UNDEFINED;
|
|
445
450
|
}
|
|
446
451
|
|
|
@@ -449,7 +454,8 @@ METHOD(canvas_stroke_text) {
|
|
|
449
454
|
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
450
455
|
if (canvas != nullptr)
|
|
451
456
|
canvas->stroke_text(string_arg(argc, argv, 0), (float)number_arg(argc, argv, 1, 0.0),
|
|
452
|
-
(float)number_arg(argc, argv, 2, 0.0)
|
|
457
|
+
(float)number_arg(argc, argv, 2, 0.0),
|
|
458
|
+
(float)number_arg(argc, argv, 3, 0.0));
|
|
453
459
|
return XT_UNDEFINED;
|
|
454
460
|
}
|
|
455
461
|
|
|
@@ -516,4 +522,4 @@ METHOD(canvas_get_image_data) {
|
|
|
516
522
|
|
|
517
523
|
} // namespace domapi
|
|
518
524
|
|
|
519
|
-
#undef METHOD
|
|
525
|
+
#undef METHOD
|
|
@@ -100,15 +100,70 @@ METHOD(canvas_ellipse) {
|
|
|
100
100
|
return XT_UNDEFINED;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
namespace {
|
|
104
|
+
|
|
105
|
+
/** One corner radius: a number, or a `{ x, y }` pair (the engine draws circular
|
|
106
|
+
* corners, so such a pair contributes its `x` radius). */
|
|
107
|
+
bool canvas_radius_value(xt_value value, float *out) {
|
|
108
|
+
if (XT_IS_NUMBER(value)) {
|
|
109
|
+
*out = (float)xt_to_number(value);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
if (XT_IS_OBJECT(value)) {
|
|
113
|
+
xt_value x = xt_object_get_cstr(value, "x");
|
|
114
|
+
xt_value y = xt_object_get_cstr(value, "y");
|
|
115
|
+
if (XT_IS_NUMBER(x)) {
|
|
116
|
+
*out = (float)xt_to_number(x);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (XT_IS_NUMBER(y)) {
|
|
120
|
+
*out = (float)xt_to_number(y);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** `roundRect`'s fifth argument: one radius or a list of 1–4 radii in CSS order
|
|
128
|
+
* (top-left, top-right, bottom-right, bottom-left). */
|
|
129
|
+
bool canvas_radii_arg(xt_value value, float radii[4]) {
|
|
130
|
+
radii[0] = radii[1] = radii[2] = radii[3] = 0.0f;
|
|
131
|
+
if (canvas_radius_value(value, &radii[0])) {
|
|
132
|
+
radii[1] = radii[2] = radii[3] = radii[0];
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
if (!XT_IS_ARRAY(value)) return false;
|
|
136
|
+
int32_t count = (int32_t)xt_to_number(xt_array_length(value));
|
|
137
|
+
if (count <= 0) return false;
|
|
138
|
+
if (count > 4) count = 4;
|
|
139
|
+
for (int32_t i = 0; i < count; i++) {
|
|
140
|
+
if (!canvas_radius_value(xt_array_get(value, xt_number((double)i)), &radii[i])) return false;
|
|
141
|
+
}
|
|
142
|
+
/* CSS expansion: [a] [a,b] [a,b,c] to four corners. */
|
|
143
|
+
if (count == 1) {
|
|
144
|
+
radii[1] = radii[2] = radii[3] = radii[0];
|
|
145
|
+
} else if (count == 2) {
|
|
146
|
+
radii[2] = radii[0];
|
|
147
|
+
radii[3] = radii[1];
|
|
148
|
+
} else if (count == 3) {
|
|
149
|
+
radii[3] = radii[1];
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
} // namespace
|
|
155
|
+
|
|
103
156
|
METHOD(canvas_round_rect) {
|
|
104
157
|
(void)env;
|
|
105
158
|
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
106
159
|
if (canvas != nullptr) {
|
|
160
|
+
float radii[4];
|
|
161
|
+
canvas_radii_arg(xt_arg(argc, argv, 4), radii);
|
|
107
162
|
canvas->round_rect((float)canvas_number_arg(argc, argv, 0, 0.0),
|
|
108
163
|
(float)canvas_number_arg(argc, argv, 1, 0.0),
|
|
109
164
|
(float)canvas_number_arg(argc, argv, 2, 0.0),
|
|
110
|
-
(float)canvas_number_arg(argc, argv, 3, 0.0),
|
|
111
|
-
|
|
165
|
+
(float)canvas_number_arg(argc, argv, 3, 0.0), radii[0], radii[1],
|
|
166
|
+
radii[2], radii[3]);
|
|
112
167
|
}
|
|
113
168
|
return XT_UNDEFINED;
|
|
114
169
|
}
|