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