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,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — sfnt (TrueType/OpenType) metadata reader.
|
|
3
|
+
*
|
|
4
|
+
* `text_font_sfnt.cpp` reads only the tables needed to match a CSS family and
|
|
5
|
+
* weight: `name` (family/style names), `OS/2` (usWeightClass/fsSelection),
|
|
6
|
+
* `head` (macStyle) and `post` (isFixedPitch). It does not decode outlines or
|
|
7
|
+
* shape text, and it accepts both single faces and collections (`.ttc`/`.otc`).
|
|
8
|
+
*
|
|
9
|
+
* The reader is deliberately dependency-free: the default text backend links
|
|
10
|
+
* stb_truetype and the optional `shaping` backend links FreeType/HarfBuzz, but
|
|
11
|
+
* font discovery must work with either, so neither library is used here.
|
|
12
|
+
*/
|
|
13
|
+
#ifndef XT_GUI_TEXT_FONT_SFNT_H
|
|
14
|
+
#define XT_GUI_TEXT_FONT_SFNT_H
|
|
15
|
+
|
|
16
|
+
#include <string>
|
|
17
|
+
#include <vector>
|
|
18
|
+
|
|
19
|
+
namespace xtgui {
|
|
20
|
+
|
|
21
|
+
/** One face in a font file. `families` holds every name a family lookup may
|
|
22
|
+
* match (legacy name ID 1 and typographic name ID 16), with `primary_family`
|
|
23
|
+
* being the name CSS reports and child faces inherit. */
|
|
24
|
+
struct SfntFace {
|
|
25
|
+
int index = 0;
|
|
26
|
+
std::string primary_family;
|
|
27
|
+
std::vector<std::string> families;
|
|
28
|
+
std::string style;
|
|
29
|
+
int weight = 400;
|
|
30
|
+
bool italic = false;
|
|
31
|
+
bool monospace = false;
|
|
32
|
+
/** False for CFF/OTTO fonts, which the default stb_truetype backend cannot
|
|
33
|
+
* outline; the `shaping` backend can load them. */
|
|
34
|
+
bool truetype = true;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Read every face in `path`, appending them to `out` in collection order.
|
|
38
|
+
* Returns false when the file cannot be opened or is not an sfnt container;
|
|
39
|
+
* `out` may still contain the faces read before an error. */
|
|
40
|
+
bool xt_sfnt_read_faces(const std::string &path, std::vector<SfntFace> &out);
|
|
41
|
+
|
|
42
|
+
} // namespace xtgui
|
|
43
|
+
|
|
44
|
+
#endif /* XT_GUI_TEXT_FONT_SFNT_H */
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — CSS family/weight/style matching.
|
|
3
|
+
*
|
|
4
|
+
* The process-wide face database lives in `text_font_db.cpp`; this unit parses
|
|
5
|
+
* a CSS `font-family` list, ranks the faces of a family by the CSS Fonts
|
|
6
|
+
* weight/style rules, and appends generic-family and fallback candidates. Both
|
|
7
|
+
* text backends (`text_stb.cpp` and `text_hb.cpp`) consume the same result.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "text_fonts_internal.h"
|
|
11
|
+
|
|
12
|
+
#include "text_font_paths.h"
|
|
13
|
+
|
|
14
|
+
#include <algorithm>
|
|
15
|
+
#include <cstdlib>
|
|
16
|
+
#include <string>
|
|
17
|
+
#include <unordered_set>
|
|
18
|
+
#include <utility>
|
|
19
|
+
#include <vector>
|
|
20
|
+
|
|
21
|
+
namespace xtgui {
|
|
22
|
+
namespace {
|
|
23
|
+
|
|
24
|
+
std::string lowerAscii(const std::string &text) {
|
|
25
|
+
std::string out = text;
|
|
26
|
+
for (char &c : out) {
|
|
27
|
+
if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
std::vector<std::string> parseFamilyList(const std::string &text) {
|
|
33
|
+
std::vector<std::string> families;
|
|
34
|
+
std::string current;
|
|
35
|
+
char quote = 0;
|
|
36
|
+
bool escaped = false;
|
|
37
|
+
auto push = [&families](const std::string &value) {
|
|
38
|
+
const std::string trimmed = normalizeFamily(value);
|
|
39
|
+
if (!trimmed.empty()) families.push_back(trimmed);
|
|
40
|
+
};
|
|
41
|
+
for (char c : text) {
|
|
42
|
+
if (escaped) {
|
|
43
|
+
current.push_back(c);
|
|
44
|
+
escaped = false;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (c == '\\') {
|
|
48
|
+
escaped = true;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (quote != 0) {
|
|
52
|
+
if (c == quote) {
|
|
53
|
+
quote = 0;
|
|
54
|
+
} else {
|
|
55
|
+
current.push_back(c);
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (c == '\'' || c == '"') {
|
|
60
|
+
quote = c;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (c == ',') {
|
|
64
|
+
push(current);
|
|
65
|
+
current.clear();
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
current.push_back(c);
|
|
69
|
+
}
|
|
70
|
+
push(current);
|
|
71
|
+
return families;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
bool genericKind(const std::string &normalized, FontGeneric *kind) {
|
|
75
|
+
if (normalized == "serif" || normalized == "ui-serif" || normalized == "math" ||
|
|
76
|
+
normalized == "fangsong") {
|
|
77
|
+
*kind = FontGeneric::Serif;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (normalized == "monospace" || normalized == "ui-monospace") {
|
|
81
|
+
*kind = FontGeneric::Monospace;
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (normalized == "cursive") {
|
|
85
|
+
*kind = FontGeneric::Cursive;
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
if (normalized == "fantasy" || normalized == "ui-rounded") {
|
|
89
|
+
*kind = FontGeneric::Fantasy;
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
if (normalized == "sans-serif" || normalized == "sans serif" ||
|
|
93
|
+
normalized == "system-ui" || normalized == "ui-sans-serif" ||
|
|
94
|
+
normalized == "-apple-system" || normalized == "blinkmacsystemfont" ||
|
|
95
|
+
normalized == "emoji") {
|
|
96
|
+
*kind = FontGeneric::Sans;
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Weight preference ranks per CSS Fonts 4 §5.2.1. */
|
|
103
|
+
void weightRanks(int desired, int ranks[1001]) {
|
|
104
|
+
for (int i = 0; i <= 1000; i++) ranks[i] = 1000000;
|
|
105
|
+
int next = 0;
|
|
106
|
+
auto push = [ranks, &next](int weight) {
|
|
107
|
+
if (weight < 1 || weight > 1000) return;
|
|
108
|
+
if (ranks[weight] != 1000000) return;
|
|
109
|
+
ranks[weight] = next++;
|
|
110
|
+
};
|
|
111
|
+
if (desired >= 400 && desired <= 500) {
|
|
112
|
+
for (int weight = desired; weight <= 500; weight++) push(weight);
|
|
113
|
+
for (int weight = desired - 1; weight >= 1; weight--) push(weight);
|
|
114
|
+
for (int weight = 501; weight <= 1000; weight++) push(weight);
|
|
115
|
+
} else if (desired < 400) {
|
|
116
|
+
for (int weight = desired; weight >= 1; weight--) push(weight);
|
|
117
|
+
for (int weight = desired + 1; weight <= 1000; weight++) push(weight);
|
|
118
|
+
} else {
|
|
119
|
+
for (int weight = desired; weight <= 1000; weight++) push(weight);
|
|
120
|
+
for (int weight = desired - 1; weight >= 1; weight--) push(weight);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void appendOrdered(const std::vector<size_t> &indices, int weight, bool italic, bool truetype_only,
|
|
125
|
+
std::vector<FontFaceRef> &out) {
|
|
126
|
+
Database &db = fontDatabase();
|
|
127
|
+
std::vector<const FaceRecord *> matching;
|
|
128
|
+
std::vector<const FaceRecord *> opposite;
|
|
129
|
+
for (size_t index : indices) {
|
|
130
|
+
const FaceRecord &face = db.faces[index];
|
|
131
|
+
if (truetype_only && !face.truetype) continue;
|
|
132
|
+
if (face.italic == italic) {
|
|
133
|
+
matching.push_back(&face);
|
|
134
|
+
} else {
|
|
135
|
+
opposite.push_back(&face);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/* CSS checks the requested style first; the other style is only a fallback
|
|
139
|
+
* when the family has no face in the requested style at all. */
|
|
140
|
+
if (matching.empty()) matching = std::move(opposite);
|
|
141
|
+
|
|
142
|
+
int ranks[1001];
|
|
143
|
+
const int wanted = clampedWeight(weight);
|
|
144
|
+
weightRanks(wanted, ranks);
|
|
145
|
+
std::stable_sort(matching.begin(), matching.end(),
|
|
146
|
+
[&ranks, wanted](const FaceRecord *left, const FaceRecord *right) {
|
|
147
|
+
return ranks[effectiveWeight(*left, wanted)] <
|
|
148
|
+
ranks[effectiveWeight(*right, wanted)];
|
|
149
|
+
});
|
|
150
|
+
for (const FaceRecord *face : matching) out.push_back(face->ref);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Best weight preference inside one family, or a negative value when the
|
|
154
|
+
* family has no face this backend can load. `style_exact` says whether the
|
|
155
|
+
* requested style (italic vs upright) exists at all, which ranks a family that
|
|
156
|
+
* can satisfy it above one that cannot. */
|
|
157
|
+
int bestWeightRank(const std::vector<size_t> &indices, int weight, bool italic, bool truetype_only,
|
|
158
|
+
bool *style_exact) {
|
|
159
|
+
Database &db = fontDatabase();
|
|
160
|
+
int ranks[1001];
|
|
161
|
+
const int wanted = clampedWeight(weight);
|
|
162
|
+
weightRanks(wanted, ranks);
|
|
163
|
+
bool has_style = false;
|
|
164
|
+
bool has_other = false;
|
|
165
|
+
int best_style = 1000000;
|
|
166
|
+
int best_other = 1000000;
|
|
167
|
+
for (size_t index : indices) {
|
|
168
|
+
const FaceRecord &face = db.faces[index];
|
|
169
|
+
if (truetype_only && !face.truetype) continue;
|
|
170
|
+
const int rank = ranks[effectiveWeight(face, wanted)];
|
|
171
|
+
if (face.italic == italic) {
|
|
172
|
+
has_style = true;
|
|
173
|
+
best_style = std::min(best_style, rank);
|
|
174
|
+
} else {
|
|
175
|
+
has_other = true;
|
|
176
|
+
best_other = std::min(best_other, rank);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
*style_exact = has_style;
|
|
180
|
+
if (has_style) return best_style;
|
|
181
|
+
return has_other ? best_other : -1;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** The file explicitly selected through `XT_GUI_FONT`/`XT_GUI_FONT_MONO` for a
|
|
185
|
+
* generic family, if any. An override must win over platform ranking for every
|
|
186
|
+
* weight/style, not only when its faces happen to match better. */
|
|
187
|
+
std::string forcedDefaultPath(FontGeneric kind) {
|
|
188
|
+
const char *sans_env = std::getenv("XT_GUI_FONT");
|
|
189
|
+
if (kind == FontGeneric::Sans && sans_env != nullptr && fileExists(sans_env)) return sans_env;
|
|
190
|
+
if (kind == FontGeneric::Monospace) {
|
|
191
|
+
const char *mono_env = std::getenv("XT_GUI_FONT_MONO");
|
|
192
|
+
if (mono_env != nullptr && fileExists(mono_env)) return mono_env;
|
|
193
|
+
if (sans_env != nullptr && fileExists(sans_env) &&
|
|
194
|
+
lowerAscii(sans_env).find("mono") != std::string::npos) {
|
|
195
|
+
return sans_env;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
void genericCandidates(FontGeneric kind, int weight, bool italic, bool truetype_only,
|
|
202
|
+
std::vector<FontFaceRef> &out) {
|
|
203
|
+
Database &db = fontDatabase();
|
|
204
|
+
ensureDefaults();
|
|
205
|
+
const std::string forced_path = forcedDefaultPath(kind);
|
|
206
|
+
|
|
207
|
+
struct FamilyChoice {
|
|
208
|
+
std::string family;
|
|
209
|
+
size_t path_order = 0;
|
|
210
|
+
bool style_exact = false;
|
|
211
|
+
bool forced = false;
|
|
212
|
+
int weight_rank = 0;
|
|
213
|
+
};
|
|
214
|
+
std::vector<FamilyChoice> choices;
|
|
215
|
+
std::vector<FontFaceRef> raw;
|
|
216
|
+
std::unordered_set<std::string> visited_families;
|
|
217
|
+
size_t path_order = 0;
|
|
218
|
+
for (const std::string &path : db.defaults[kindIndex(kind)]) {
|
|
219
|
+
addFontFile(path);
|
|
220
|
+
const auto path_faces = db.by_path.find(path);
|
|
221
|
+
if (path_faces == db.by_path.end() || path_faces->second.empty()) {
|
|
222
|
+
raw.push_back(FontFaceRef{path, 0});
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const FaceRecord &first = db.faces[path_faces->second.front()];
|
|
226
|
+
if (first.primary_family.empty()) {
|
|
227
|
+
raw.push_back(first.ref);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (!visited_families.insert(first.primary_family).second) continue;
|
|
231
|
+
const auto family = db.by_family.find(first.primary_family);
|
|
232
|
+
if (family == db.by_family.end()) continue;
|
|
233
|
+
bool style_exact = false;
|
|
234
|
+
const int rank = bestWeightRank(family->second, weight, italic, truetype_only, &style_exact);
|
|
235
|
+
if (rank < 0) continue;
|
|
236
|
+
choices.push_back(
|
|
237
|
+
FamilyChoice{first.primary_family, path_order++, style_exact, path == forced_path, rank});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* A generic family is UA-defined, so prefer a family that has the requested
|
|
241
|
+
* style and the closest weight (e.g. bold `sans-serif` picks a family with a
|
|
242
|
+
* real bold face) before falling back to the CJK-first path order. An explicit
|
|
243
|
+
* `XT_GUI_FONT`/`XT_GUI_FONT_MONO` always comes first. */
|
|
244
|
+
std::stable_sort(choices.begin(), choices.end(),
|
|
245
|
+
[](const FamilyChoice &left, const FamilyChoice &right) {
|
|
246
|
+
if (left.forced != right.forced) return left.forced;
|
|
247
|
+
if (left.style_exact != right.style_exact) return left.style_exact;
|
|
248
|
+
if (left.weight_rank != right.weight_rank) {
|
|
249
|
+
return left.weight_rank < right.weight_rank;
|
|
250
|
+
}
|
|
251
|
+
return left.path_order < right.path_order;
|
|
252
|
+
});
|
|
253
|
+
for (const FamilyChoice &choice : choices) {
|
|
254
|
+
const auto family = db.by_family.find(choice.family);
|
|
255
|
+
if (family != db.by_family.end()) {
|
|
256
|
+
appendOrdered(family->second, weight, italic, truetype_only, out);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
for (const FontFaceRef &ref : raw) out.push_back(ref);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
void appendFirstGeneric(FontGeneric kind, bool truetype_only, std::vector<FontFaceRef> &out) {
|
|
263
|
+
std::vector<FontFaceRef> faces;
|
|
264
|
+
genericCandidates(kind, 400, false, truetype_only, faces);
|
|
265
|
+
if (!faces.empty()) out.push_back(faces.front());
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
} // namespace
|
|
269
|
+
|
|
270
|
+
void xt_font_face_candidates(const FontSpec &spec, bool truetype_only,
|
|
271
|
+
std::vector<FontFaceRef> &out) {
|
|
272
|
+
out.clear();
|
|
273
|
+
std::vector<std::string> families = parseFamilyList(spec.family);
|
|
274
|
+
if (families.empty()) families.push_back("sans-serif");
|
|
275
|
+
|
|
276
|
+
bool accepted = false;
|
|
277
|
+
for (const std::string &family : families) {
|
|
278
|
+
FontGeneric kind = FontGeneric::Sans;
|
|
279
|
+
if (genericKind(family, &kind)) {
|
|
280
|
+
genericCandidates(kind, spec.weight, spec.italic, truetype_only, out);
|
|
281
|
+
accepted = true;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
/* An @font-face family resolves without scanning the system directories;
|
|
285
|
+
* only fall through to the scan when the author family is absent (or has no
|
|
286
|
+
* face this backend can load). */
|
|
287
|
+
std::vector<size_t> matches = lookupFamily(family);
|
|
288
|
+
size_t before = out.size();
|
|
289
|
+
if (!matches.empty()) appendOrdered(matches, spec.weight, spec.italic, truetype_only, out);
|
|
290
|
+
if (out.size() > before) {
|
|
291
|
+
accepted = true;
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
ensureScanned();
|
|
295
|
+
matches = lookupFamily(family);
|
|
296
|
+
if (matches.empty()) continue;
|
|
297
|
+
before = out.size();
|
|
298
|
+
appendOrdered(matches, spec.weight, spec.italic, truetype_only, out);
|
|
299
|
+
if (out.size() > before) {
|
|
300
|
+
accepted = true;
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (!accepted) genericCandidates(FontGeneric::Sans, spec.weight, spec.italic, truetype_only, out);
|
|
305
|
+
|
|
306
|
+
std::unordered_set<std::string> seen;
|
|
307
|
+
std::vector<FontFaceRef> unique;
|
|
308
|
+
unique.reserve(out.size());
|
|
309
|
+
for (const FontFaceRef &ref : out) {
|
|
310
|
+
if (seen.insert(faceKey(ref)).second) unique.push_back(ref);
|
|
311
|
+
}
|
|
312
|
+
out.swap(unique);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
void xt_font_fallback_faces(const FontFaceRef &primary, bool truetype_only,
|
|
316
|
+
std::vector<FontFaceRef> &out) {
|
|
317
|
+
out.clear();
|
|
318
|
+
ensureDefaults();
|
|
319
|
+
Database &db = fontDatabase();
|
|
320
|
+
std::vector<FontFaceRef> candidates;
|
|
321
|
+
for (const std::string &path : xt_font_symbol_paths()) {
|
|
322
|
+
if (!fileExists(path)) continue;
|
|
323
|
+
addFontFile(path);
|
|
324
|
+
const auto faces = db.by_path.find(path);
|
|
325
|
+
if (faces == db.by_path.end()) continue;
|
|
326
|
+
bool added = false;
|
|
327
|
+
for (size_t index : faces->second) {
|
|
328
|
+
const FaceRecord &face = db.faces[index];
|
|
329
|
+
if (truetype_only && !face.truetype) continue;
|
|
330
|
+
candidates.push_back(face.ref);
|
|
331
|
+
added = true;
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
if (!added && !truetype_only) candidates.push_back(FontFaceRef{path, 0});
|
|
335
|
+
}
|
|
336
|
+
appendFirstGeneric(FontGeneric::Sans, truetype_only, candidates);
|
|
337
|
+
appendFirstGeneric(FontGeneric::Serif, truetype_only, candidates);
|
|
338
|
+
appendFirstGeneric(FontGeneric::Monospace, truetype_only, candidates);
|
|
339
|
+
|
|
340
|
+
std::unordered_set<std::string> seen;
|
|
341
|
+
seen.insert(faceKey(primary));
|
|
342
|
+
for (const FontFaceRef &ref : candidates) {
|
|
343
|
+
if (out.size() >= 16) break;
|
|
344
|
+
if (seen.insert(faceKey(ref)).second) out.push_back(ref);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
} // namespace xtgui
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — CSS font matching.
|
|
3
|
+
*
|
|
4
|
+
* `text_fonts.cpp` owns the process-wide font database: it discovers installed
|
|
5
|
+
* faces, resolves a CSS `font-family` list to concrete `(file, face index)`
|
|
6
|
+
* pairs, and orders the faces of a family by the CSS Fonts weight/style rules.
|
|
7
|
+
* Both text backends (`text_stb.cpp` and `text_hb.cpp`) consume the same
|
|
8
|
+
* result, so `font-family: "Times New Roman", serif` and `font-weight: 700`
|
|
9
|
+
* select the same real face whichever shaper is linked.
|
|
10
|
+
*
|
|
11
|
+
* Discovery is lazy: a generic family parses only the curated default files
|
|
12
|
+
* (which contain their own weight variants); an arbitrary family name scans the
|
|
13
|
+
* platform font directories once. Metadata comes from `text_font_sfnt.cpp`, not
|
|
14
|
+
* from stb_truetype/FreeType, so the default backend does not pull the shaping
|
|
15
|
+
* libraries in.
|
|
16
|
+
*/
|
|
17
|
+
#ifndef XT_GUI_TEXT_FONTS_H
|
|
18
|
+
#define XT_GUI_TEXT_FONTS_H
|
|
19
|
+
|
|
20
|
+
#include <string>
|
|
21
|
+
#include <vector>
|
|
22
|
+
|
|
23
|
+
#include "text.h"
|
|
24
|
+
|
|
25
|
+
namespace xtgui {
|
|
26
|
+
|
|
27
|
+
/** One concrete face in a font file (a collection holds several). */
|
|
28
|
+
struct FontFaceRef {
|
|
29
|
+
std::string path;
|
|
30
|
+
int index = 0;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** One source URL from an author `@font-face` rule. Relative paths are read as
|
|
34
|
+
* local files (the `file:` protocol) from the process working directory; remote
|
|
35
|
+
* URLs are ignored. */
|
|
36
|
+
struct UserFontSource {
|
|
37
|
+
std::string url;
|
|
38
|
+
std::string format;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** One resolved-author `@font-face` request. The declared `family` is matched
|
|
42
|
+
* by CSS even when it differs from the font file's internal family name. A
|
|
43
|
+
* missing weight/style descriptor (`has_* == false`) takes the value from the
|
|
44
|
+
* font file's own metadata. */
|
|
45
|
+
struct UserFontFace {
|
|
46
|
+
std::string family;
|
|
47
|
+
std::vector<UserFontSource> sources;
|
|
48
|
+
bool has_weight = false;
|
|
49
|
+
int weight_min = 400;
|
|
50
|
+
int weight_max = 400;
|
|
51
|
+
bool has_italic = false;
|
|
52
|
+
bool italic = false;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Replace the document's author font faces. Every call clears the previous
|
|
56
|
+
* set, so `loadHTML` starts from the new document's `@font-face` rules. */
|
|
57
|
+
void xt_font_set_user_faces(const std::vector<UserFontFace> &faces);
|
|
58
|
+
|
|
59
|
+
/** Append the faces for `spec`, best match first. Named families are looked up
|
|
60
|
+
* case-insensitively; generic families resolve to the platform defaults. Every
|
|
61
|
+
* family in the CSS list is tried in order and later families are kept as
|
|
62
|
+
* load-failure fallbacks. `truetype_only` excludes CFF/OTTO faces for the
|
|
63
|
+
* stb_truetype backend. `out` is cleared first. */
|
|
64
|
+
void xt_font_face_candidates(const FontSpec &spec, bool truetype_only,
|
|
65
|
+
std::vector<FontFaceRef> &out);
|
|
66
|
+
|
|
67
|
+
/** Append the fallback faces for codepoints the primary does not cover: symbol
|
|
68
|
+
* faces first, then the default sans, serif and monospace faces. The primary is
|
|
69
|
+
* excluded. `out` is cleared first. */
|
|
70
|
+
void xt_font_fallback_faces(const FontFaceRef &primary, bool truetype_only,
|
|
71
|
+
std::vector<FontFaceRef> &out);
|
|
72
|
+
|
|
73
|
+
/** Drop the database and every parsed font file (called at text shutdown). */
|
|
74
|
+
void xt_font_db_shutdown();
|
|
75
|
+
|
|
76
|
+
} // namespace xtgui
|
|
77
|
+
|
|
78
|
+
#endif /* XT_GUI_TEXT_FONTS_H */
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — shared font database state.
|
|
3
|
+
*
|
|
4
|
+
* `text_font_db.cpp` owns the process-wide discovery/indexing; `text_fonts.cpp`
|
|
5
|
+
* owns CSS family/weight matching on top of it. The two halves share these
|
|
6
|
+
* internal types and the small helpers that cross the boundary; this header is
|
|
7
|
+
* not part of the public text API.
|
|
8
|
+
*/
|
|
9
|
+
#ifndef XT_GUI_TEXT_FONTS_INTERNAL_H
|
|
10
|
+
#define XT_GUI_TEXT_FONTS_INTERNAL_H
|
|
11
|
+
|
|
12
|
+
#include <array>
|
|
13
|
+
#include <cstddef>
|
|
14
|
+
#include <string>
|
|
15
|
+
#include <unordered_map>
|
|
16
|
+
#include <unordered_set>
|
|
17
|
+
#include <vector>
|
|
18
|
+
|
|
19
|
+
#include "text_font_paths.h"
|
|
20
|
+
#include "text_fonts.h"
|
|
21
|
+
|
|
22
|
+
namespace xtgui {
|
|
23
|
+
|
|
24
|
+
constexpr size_t kGenericCount = 5;
|
|
25
|
+
|
|
26
|
+
/** One concrete face and the CSS metadata the matcher needs. */
|
|
27
|
+
struct FaceRecord {
|
|
28
|
+
FontFaceRef ref;
|
|
29
|
+
std::string primary_family; // normalized
|
|
30
|
+
std::vector<std::string> families; // normalized (legacy + typographic)
|
|
31
|
+
int weight = 400;
|
|
32
|
+
/** Authored `@font-face` ranges can cover several weights; system faces have
|
|
33
|
+
* `weight_min == weight_max == weight`. */
|
|
34
|
+
int weight_min = 400;
|
|
35
|
+
int weight_max = 400;
|
|
36
|
+
bool italic = false;
|
|
37
|
+
bool monospace = false;
|
|
38
|
+
bool truetype = true;
|
|
39
|
+
/** True for a face registered by the document's `@font-face` rules. */
|
|
40
|
+
bool user = false;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
struct Database {
|
|
44
|
+
bool defaults_loaded = false;
|
|
45
|
+
bool scanned = false;
|
|
46
|
+
std::vector<FaceRecord> faces;
|
|
47
|
+
std::unordered_map<std::string, std::vector<size_t>> by_family;
|
|
48
|
+
std::unordered_map<std::string, std::vector<size_t>> by_compact;
|
|
49
|
+
std::unordered_map<std::string, std::vector<size_t>> by_path;
|
|
50
|
+
std::unordered_set<std::string> parsed_paths;
|
|
51
|
+
std::array<std::vector<std::string>, kGenericCount> defaults;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
Database &fontDatabase();
|
|
55
|
+
size_t kindIndex(FontGeneric kind);
|
|
56
|
+
|
|
57
|
+
inline int clampedWeight(int weight) { return weight < 1 ? 1 : weight > 1000 ? 1000 : weight; }
|
|
58
|
+
|
|
59
|
+
/** The weight a face actually offers for a requested CSS weight. A descriptor
|
|
60
|
+
* range can satisfy any weight inside it; system faces have a single weight. */
|
|
61
|
+
inline int effectiveWeight(const FaceRecord &face, int desired) {
|
|
62
|
+
return desired < face.weight_min ? face.weight_min
|
|
63
|
+
: (desired > face.weight_max ? face.weight_max : desired);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
bool fileExists(const std::string &path);
|
|
67
|
+
std::string normalizeFamily(const std::string &name);
|
|
68
|
+
std::string faceKey(const FontFaceRef &ref);
|
|
69
|
+
|
|
70
|
+
void addFontFile(const std::string &path);
|
|
71
|
+
void ensureDefaults();
|
|
72
|
+
void ensureScanned();
|
|
73
|
+
std::vector<size_t> lookupFamily(const std::string &name);
|
|
74
|
+
|
|
75
|
+
} // namespace xtgui
|
|
76
|
+
|
|
77
|
+
#endif /* XT_GUI_TEXT_FONTS_INTERNAL_H */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* xbintsc GUI engine — cached glyph bitmaps.
|
|
3
3
|
*
|
|
4
|
-
* `xt_text_rasterize` runs
|
|
4
|
+
* `xt_text_rasterize` runs stb_truetype for one glyph. Canvas `fillText` used to
|
|
5
5
|
* call it for every glyph of every draw, so a redrawn label paid the whole
|
|
6
6
|
* rasterisation cost again on every frame. This unit keeps the bitmaps in two
|
|
7
7
|
* bounded generations, the same policy as the shaped-run cache in
|
|
@@ -71,7 +71,7 @@ const GlyphImage *xt_text_glyph_cached(Font *font, uint32_t glyph) {
|
|
|
71
71
|
cache.spare_bytes -= image.pixels.size();
|
|
72
72
|
cache.spare.erase(spare);
|
|
73
73
|
} else if (!xt_text_rasterize(font, glyph, image)) {
|
|
74
|
-
/* Cache the miss too: a space (or .notdef) would otherwise retry
|
|
74
|
+
/* Cache the miss too: a space (or .notdef) would otherwise retry stb_truetype
|
|
75
75
|
* on every `fillText`. */
|
|
76
76
|
image.width = 0;
|
|
77
77
|
image.height = 0;
|