xbintsc 0.5.37 → 0.5.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/codegen/generator/module.js +23 -7
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +1 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.js +1 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/deferred.d.ts +63 -0
- package/dist/src/driver/bundler/deferred.js +279 -0
- package/dist/src/driver/bundler/deferred.js.map +1 -0
- package/dist/src/driver/bundler/merge.js +214 -2
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/runtime-objects.d.ts +1 -1
- package/dist/src/driver/runtime-objects.js +1 -0
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/html.js +98 -15
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/gui/index.js +1 -0
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/gui/three.d.ts +4 -3
- package/dist/src/extensions/gui/three.js +4 -3
- package/dist/src/extensions/gui/three.js.map +1 -1
- package/dist/tests/codegen/closures.test.js +7 -0
- package/dist/tests/codegen/closures.test.js.map +1 -1
- package/dist/tests/e2e/compile-objects-classes.test.js +31 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas-state.test.d.ts +9 -0
- package/dist/tests/e2e/gui-canvas-state.test.js +297 -0
- package/dist/tests/e2e/gui-canvas-state.test.js.map +1 -0
- package/dist/tests/e2e/gui-draw-perf.test.d.ts +22 -0
- package/dist/tests/e2e/gui-draw-perf.test.js +405 -0
- package/dist/tests/e2e/gui-draw-perf.test.js.map +1 -0
- package/dist/tests/e2e/gui-perf.test.d.ts +23 -0
- package/dist/tests/e2e/gui-perf.test.js +283 -0
- package/dist/tests/e2e/gui-perf.test.js.map +1 -0
- package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
- package/dist/tests/e2e/gui-script-import-order.test.js +169 -0
- package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl.test.js +4 -4
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.d.ts +8 -0
- package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js +336 -0
- package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
- package/dist/tests/e2e/url.test.d.ts +6 -0
- package/dist/tests/e2e/url.test.js +114 -0
- package/dist/tests/e2e/url.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.js +45 -10
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/doc/gui/api.md +28 -2
- package/doc/gui/canvas.md +110 -27
- package/doc/gui/known-issues.md +32 -10
- package/doc/gui/rendering.md +77 -0
- package/doc/gui/runtime.md +18 -3
- package/doc/gui/webgl.md +58 -29
- package/doc/gui-scripts.md +19 -19
- package/doc/gui.md +43 -9
- package/doc/implemented/fetch.md +5 -0
- package/doc/implemented/modules.md +13 -0
- package/doc/implemented/stdlib.md +17 -0
- package/doc/unimplemented/deviations.md +1 -0
- package/doc/zh-CN/gui/api.md +25 -2
- package/doc/zh-CN/gui/canvas.md +95 -26
- package/doc/zh-CN/gui/known-issues.md +21 -4
- package/doc/zh-CN/gui/rendering.md +59 -0
- package/doc/zh-CN/gui/runtime.md +13 -3
- package/doc/zh-CN/gui/webgl.md +30 -8
- package/doc/zh-CN/gui-scripts.md +14 -13
- package/doc/zh-CN/gui.md +32 -8
- package/doc/zh-CN/implemented/fetch.md +4 -0
- package/doc/zh-CN/implemented/modules.md +10 -0
- package/doc/zh-CN/implemented/stdlib.md +15 -0
- package/doc/zh-CN/unimplemented/deviations.md +1 -0
- package/package.json +7 -7
- package/runtime/ext_gui/browser.cpp +3 -1
- package/runtime/ext_gui/canvas.cpp +135 -89
- package/runtime/ext_gui/canvas.h +116 -0
- package/runtime/ext_gui/canvas_clip.cpp +143 -0
- package/runtime/ext_gui/canvas_composite.cpp +241 -0
- package/runtime/ext_gui/canvas_coverage.cpp +241 -0
- package/runtime/ext_gui/canvas_gradient.cpp +41 -4
- package/runtime/ext_gui/canvas_image_data.cpp +4 -0
- package/runtime/ext_gui/canvas_internal.h +34 -9
- package/runtime/ext_gui/canvas_raster.cpp +207 -178
- package/runtime/ext_gui/canvas_shadow.cpp +161 -0
- package/runtime/ext_gui/canvas_stroke.cpp +37 -9
- package/runtime/ext_gui/canvas_style.cpp +59 -0
- package/runtime/ext_gui/canvas_text.cpp +140 -0
- package/runtime/ext_gui/css.h +7 -0
- package/runtime/ext_gui/css_sheet.cpp +22 -0
- package/runtime/ext_gui/document.cpp +25 -1
- package/runtime/ext_gui/document.h +8 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +16 -1
- package/runtime/ext_gui/dom_api_canvas_image.cpp +9 -0
- package/runtime/ext_gui/dom_api_canvas_state.cpp +217 -0
- package/runtime/ext_gui/dom_api_internal.h +36 -37
- package/runtime/ext_gui/dom_api_offscreen.cpp +213 -0
- package/runtime/ext_gui/dom_api_proto.cpp +18 -0
- package/runtime/ext_gui/dom_api_webgl.cpp +29 -9
- package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
- package/runtime/ext_gui/dom_api_webgl_internal.h +58 -0
- package/runtime/ext_gui/dom_api_webgl_source.cpp +18 -0
- package/runtime/ext_gui/gradient_values.cpp +12 -1
- package/runtime/ext_gui/gui.cpp +5 -0
- package/runtime/ext_gui/gui_canvas.cpp +4 -0
- package/runtime/ext_gui/gui_engine.h +32 -0
- package/runtime/ext_gui/gui_events.cpp +182 -58
- package/runtime/ext_gui/gui_media.cpp +3 -0
- package/runtime/ext_gui/image.cpp +69 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +11 -0
- package/runtime/ext_gui/layout.cpp +4 -0
- package/runtime/ext_gui/paint.cpp +19 -0
- package/runtime/ext_gui/paint_background.cpp +18 -1
- package/runtime/ext_gui/perf.cpp +171 -0
- package/runtime/ext_gui/perf.h +127 -0
- package/runtime/ext_gui/renderer.cpp +109 -41
- package/runtime/ext_gui/renderer.h +33 -9
- package/runtime/ext_gui/renderer_image.cpp +135 -65
- package/runtime/ext_gui/renderer_internal.h +15 -3
- package/runtime/ext_gui/renderer_text.cpp +24 -12
- package/runtime/ext_gui/script.cpp +31 -0
- package/runtime/ext_gui/style.cpp +101 -30
- package/runtime/ext_gui/style.h +6 -3
- package/runtime/ext_gui/style_print.cpp +7 -1
- package/runtime/ext_gui/text.cpp +23 -4
- package/runtime/ext_gui/text.h +29 -0
- package/runtime/ext_gui/text_cache.cpp +118 -0
- package/runtime/ext_gui/text_glyph_cache.cpp +100 -0
- package/runtime/ext_gui/webgl.h +48 -146
- package/runtime/ext_gui/webgl_blit.cpp +12 -1
- package/runtime/ext_gui/webgl_blit_depth.cpp +270 -0
- package/runtime/ext_gui/webgl_buffer.cpp +59 -1
- package/runtime/ext_gui/webgl_buffer_objects.cpp +106 -9
- package/runtime/ext_gui/webgl_clear.cpp +3 -0
- package/runtime/ext_gui/webgl_context.cpp +29 -3
- package/runtime/ext_gui/webgl_draw.cpp +75 -270
- package/runtime/ext_gui/webgl_draw_buffers.cpp +1 -0
- package/runtime/ext_gui/webgl_draw_setup.cpp +321 -0
- package/runtime/ext_gui/webgl_draw_state.h +130 -0
- package/runtime/ext_gui/webgl_glsl.h +3 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +3 -8
- package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
- package/runtime/ext_gui/webgl_msl.cpp +4 -0
- package/runtime/ext_gui/webgl_objects.h +148 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +8 -8
- package/runtime/ext_gui/webgl_program_internal.h +35 -0
- package/runtime/ext_gui/webgl_shader.cpp +34 -36
- package/runtime/ext_gui/webgl_webgl2.cpp +36 -0
- package/runtime/ext_gui/window.cpp +56 -1
- package/runtime/rt.h +3 -0
- package/runtime/xt_fetch/async.inc +60 -0
- package/runtime/xt_url_object.c +463 -0
- package/src/codegen/generator/module.ts +22 -7
- package/src/codegen/generator/state.ts +1 -0
- package/src/codegen/generator/tables.ts +1 -0
- package/src/driver/bundler/deferred.ts +351 -0
- package/src/driver/bundler/merge.ts +219 -2
- package/src/driver/runtime-objects.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/html.ts +100 -17
- package/src/extensions/gui/index.ts +1 -0
- package/src/extensions/gui/three.ts +4 -3
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#include "style_internal.h"
|
|
10
10
|
|
|
11
11
|
#include "css_internal.h"
|
|
12
|
+
#include "perf.h"
|
|
12
13
|
|
|
13
14
|
#include <algorithm>
|
|
14
15
|
#include <cctype>
|
|
@@ -45,13 +46,17 @@ struct Candidate {
|
|
|
45
46
|
int important = 0;
|
|
46
47
|
int specificity = 0;
|
|
47
48
|
int order = 0;
|
|
49
|
+
/** Push order, so two candidates with the same `order` (the inline
|
|
50
|
+
* declarations all use `INT_MAX`) keep their declaration order. */
|
|
51
|
+
int sequence = 0;
|
|
48
52
|
const Declaration *declaration = nullptr;
|
|
49
53
|
};
|
|
50
54
|
|
|
51
55
|
bool candidateBefore(const Candidate &a, const Candidate &b) {
|
|
52
56
|
if (a.important != b.important) return a.important < b.important;
|
|
53
57
|
if (a.specificity != b.specificity) return a.specificity < b.specificity;
|
|
54
|
-
return a.order < b.order;
|
|
58
|
+
if (a.order != b.order) return a.order < b.order;
|
|
59
|
+
return a.sequence < b.sequence;
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
|
|
@@ -156,7 +161,9 @@ void inheritFrom(XtStyle *style, const XtStyle *parent) {
|
|
|
156
161
|
style->color = parent->color;
|
|
157
162
|
style->visible = parent->visible;
|
|
158
163
|
style->pointer_events = parent->pointer_events;
|
|
159
|
-
|
|
164
|
+
/* Most documents define no custom properties; copying the (empty) map for
|
|
165
|
+
* every element is pure overhead. */
|
|
166
|
+
if (!parent->variables.empty()) style->variables = parent->variables;
|
|
160
167
|
style->font_size = parent->font_size;
|
|
161
168
|
style->font_weight = parent->font_weight;
|
|
162
169
|
style->font_family = parent->font_family;
|
|
@@ -167,14 +174,12 @@ void inheritFrom(XtStyle *style, const XtStyle *parent) {
|
|
|
167
174
|
style->text_shadow = parent->text_shadow;
|
|
168
175
|
}
|
|
169
176
|
|
|
170
|
-
bool ruleApplies(const Rule &rule, const ApplyContext &context) {
|
|
171
|
-
return rule.media.empty() ||
|
|
172
|
-
xt_css_media_matches(rule.media, context.viewportWidth, context.viewportHeight);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
177
|
void applyCandidates(XtStyle *style, std::vector<Candidate> *candidates,
|
|
176
178
|
const ApplyContext &nodeContext) {
|
|
177
|
-
|
|
179
|
+
/* `candidateBefore` is a total order now (the sequence breaks ties), so an
|
|
180
|
+
* unstable `std::sort` is equivalent to the old `stable_sort` without its
|
|
181
|
+
* temporary buffer. */
|
|
182
|
+
std::sort(candidates->begin(), candidates->end(), candidateBefore);
|
|
178
183
|
/* Custom properties participate in the cascade first; their final values are
|
|
179
184
|
* then available to every normal declaration on this element. */
|
|
180
185
|
for (const Candidate &candidate : *candidates) {
|
|
@@ -332,11 +337,35 @@ void resetPseudoPointers(const Node *node) {
|
|
|
332
337
|
for (const std::unique_ptr<Node> &child : node->children) resetPseudoPointers(child.get());
|
|
333
338
|
}
|
|
334
339
|
|
|
340
|
+
/** Per-computation scratch buffers. The cascade runs depth-first, so one
|
|
341
|
+
* candidate list (and one inline-declaration list) can be reused for every
|
|
342
|
+
* node — the old code allocated both per element. */
|
|
343
|
+
struct ComputeScratch {
|
|
344
|
+
std::vector<Candidate> candidates;
|
|
345
|
+
std::vector<Declaration> inline_declarations;
|
|
346
|
+
int sequence = 0;
|
|
347
|
+
/** Set when any computed style declares a `transition` (including an inline
|
|
348
|
+
* `style="transition: …"`), so the document knows whether it needs the
|
|
349
|
+
* transition snapshot path at all. */
|
|
350
|
+
bool has_transitions = false;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
void pushCandidates(std::vector<Candidate> *candidates, ComputeScratch *scratch,
|
|
354
|
+
const Declaration &declaration, int important, int specificity, int order) {
|
|
355
|
+
Candidate candidate;
|
|
356
|
+
candidate.important = important;
|
|
357
|
+
candidate.specificity = specificity;
|
|
358
|
+
candidate.order = order;
|
|
359
|
+
candidate.sequence = scratch->sequence++;
|
|
360
|
+
candidate.declaration = &declaration;
|
|
361
|
+
candidates->push_back(candidate);
|
|
362
|
+
}
|
|
363
|
+
|
|
335
364
|
/** Compute one `::before`/`::after` style and attach its generated content. */
|
|
336
365
|
void computePseudo(const Node *origin, PseudoElement side, const XtStyle &originStyle,
|
|
337
|
-
const StyleSheet &sheet, std::
|
|
338
|
-
const
|
|
339
|
-
PseudoNodeSet *pseudos) {
|
|
366
|
+
const StyleSheet &sheet, const std::vector<char> &applies,
|
|
367
|
+
std::unordered_map<const Node *, XtStyle> &out, const ApplyContext &context,
|
|
368
|
+
const MatchState &state, PseudoNodeSet *pseudos, ComputeScratch *scratch) {
|
|
340
369
|
if (pseudos == nullptr || originStyle.display == Display::None) return;
|
|
341
370
|
Node *pseudo = pseudos->create(const_cast<Node *>(origin), side);
|
|
342
371
|
if (side == PseudoElement::Before) const_cast<Node *>(origin)->pseudo_before = pseudo;
|
|
@@ -350,9 +379,11 @@ void computePseudo(const Node *origin, PseudoElement side, const XtStyle &origin
|
|
|
350
379
|
nodeContext.parentFontSize = originStyle.font_size;
|
|
351
380
|
|
|
352
381
|
std::string pseudoName = side == PseudoElement::Before ? "before" : "after";
|
|
353
|
-
std::vector<Candidate> candidates;
|
|
354
|
-
|
|
355
|
-
|
|
382
|
+
std::vector<Candidate> &candidates = scratch->candidates;
|
|
383
|
+
candidates.clear();
|
|
384
|
+
for (size_t ruleIndex = 0; ruleIndex < sheet.rules.size(); ruleIndex++) {
|
|
385
|
+
if (applies[ruleIndex] == 0) continue;
|
|
386
|
+
const Rule &rule = sheet.rules[ruleIndex];
|
|
356
387
|
for (const ComplexSelector &selector : rule.selectors) {
|
|
357
388
|
if (selector.compounds.empty() || selector.compounds.back().pseudo_element != pseudoName)
|
|
358
389
|
continue;
|
|
@@ -360,15 +391,17 @@ void computePseudo(const Node *origin, PseudoElement side, const XtStyle &origin
|
|
|
360
391
|
* pseudo-element suffix removed: `.card.info::before` is `.card.info`. */
|
|
361
392
|
ComplexSelector copy = selector;
|
|
362
393
|
copy.compounds.back().pseudo_element.clear();
|
|
394
|
+
g_perf_frame.rulesEvaluated += 1;
|
|
363
395
|
if (!xt_css_match(origin, copy, state)) continue;
|
|
364
396
|
for (const Declaration &declaration : rule.declarations) {
|
|
365
|
-
candidates
|
|
366
|
-
|
|
397
|
+
pushCandidates(&candidates, scratch, declaration, declaration.important ? 1 : 0,
|
|
398
|
+
selector.specificity, rule.order);
|
|
367
399
|
}
|
|
368
400
|
}
|
|
369
401
|
}
|
|
370
402
|
applyCandidates(&style, &candidates, nodeContext);
|
|
371
403
|
finalizeStyle(&style, nodeContext);
|
|
404
|
+
if (!style.transitions.empty()) scratch->has_transitions = true;
|
|
372
405
|
|
|
373
406
|
bool generated = false;
|
|
374
407
|
std::string text;
|
|
@@ -380,8 +413,9 @@ void computePseudo(const Node *origin, PseudoElement side, const XtStyle &origin
|
|
|
380
413
|
}
|
|
381
414
|
|
|
382
415
|
void computeNode(const Node *node, const XtStyle *parent, const StyleSheet &sheet,
|
|
383
|
-
std::unordered_map<const Node *, XtStyle> &out,
|
|
384
|
-
const MatchState &state, PseudoNodeSet *pseudos
|
|
416
|
+
const std::vector<char> &applies, std::unordered_map<const Node *, XtStyle> &out,
|
|
417
|
+
const ApplyContext &context, const MatchState &state, PseudoNodeSet *pseudos,
|
|
418
|
+
ComputeScratch *scratch) {
|
|
385
419
|
const XtStyle *childParent = parent;
|
|
386
420
|
if (node->isElement() && !node->is_pseudo) {
|
|
387
421
|
XtStyle style = xt_style_initial();
|
|
@@ -395,35 +429,48 @@ void computeNode(const Node *node, const XtStyle *parent, const StyleSheet &shee
|
|
|
395
429
|
ApplyContext nodeContext = context;
|
|
396
430
|
nodeContext.parentFontSize = (parent != nullptr) ? parent->font_size : context.rootFontSize;
|
|
397
431
|
|
|
398
|
-
std::vector<Candidate> candidates;
|
|
399
|
-
|
|
400
|
-
|
|
432
|
+
std::vector<Candidate> &candidates = scratch->candidates;
|
|
433
|
+
candidates.clear();
|
|
434
|
+
for (size_t ruleIndex = 0; ruleIndex < sheet.rules.size(); ruleIndex++) {
|
|
435
|
+
if (applies[ruleIndex] == 0) continue;
|
|
436
|
+
const Rule &rule = sheet.rules[ruleIndex];
|
|
401
437
|
for (const ComplexSelector &selector : rule.selectors) {
|
|
438
|
+
g_perf_frame.rulesEvaluated += 1;
|
|
402
439
|
if (!xt_css_match(node, selector, state)) continue;
|
|
403
440
|
for (const Declaration &declaration : rule.declarations) {
|
|
404
|
-
candidates
|
|
441
|
+
pushCandidates(&candidates, scratch, declaration, declaration.important ? 1 : 0,
|
|
442
|
+
selector.specificity, rule.order);
|
|
405
443
|
}
|
|
406
444
|
}
|
|
407
445
|
}
|
|
408
446
|
const std::string *inlineStyle = node->attr("style");
|
|
409
|
-
std::vector<Declaration> inlineDeclarations;
|
|
410
447
|
if (inlineStyle != nullptr) {
|
|
448
|
+
std::vector<Declaration> &inlineDeclarations = scratch->inline_declarations;
|
|
411
449
|
inlineDeclarations = xt_css_parse_declarations(*inlineStyle);
|
|
412
450
|
for (const Declaration &declaration : inlineDeclarations) {
|
|
413
|
-
candidates
|
|
451
|
+
pushCandidates(&candidates, scratch, declaration, declaration.important ? 1 : 0, 1 << 28,
|
|
452
|
+
INT_MAX);
|
|
414
453
|
}
|
|
415
454
|
}
|
|
416
455
|
applyCandidates(&style, &candidates, nodeContext);
|
|
417
456
|
finalizeStyle(&style, nodeContext);
|
|
457
|
+
if (!style.transitions.empty()) scratch->has_transitions = true;
|
|
418
458
|
|
|
459
|
+
/* `emplace` keeps the address of an existing entry stable, which is what
|
|
460
|
+
* `box->style` pointers rely on. */
|
|
419
461
|
out[node] = style;
|
|
420
462
|
childParent = &out[node];
|
|
421
463
|
const XtStyle &computed = out[node];
|
|
422
|
-
|
|
423
|
-
|
|
464
|
+
if (pseudos != nullptr && sheet.has_pseudo_elements) {
|
|
465
|
+
computePseudo(node, PseudoElement::Before, computed, sheet, applies, out, context, state,
|
|
466
|
+
pseudos, scratch);
|
|
467
|
+
computePseudo(node, PseudoElement::After, computed, sheet, applies, out, context, state,
|
|
468
|
+
pseudos, scratch);
|
|
469
|
+
}
|
|
470
|
+
g_perf_frame.restyleNodes += 1;
|
|
424
471
|
}
|
|
425
472
|
for (const std::unique_ptr<Node> &child : node->children) {
|
|
426
|
-
computeNode(child.get(), childParent, sheet, out, context, state, pseudos);
|
|
473
|
+
computeNode(child.get(), childParent, sheet, applies, out, context, state, pseudos, scratch);
|
|
427
474
|
}
|
|
428
475
|
}
|
|
429
476
|
|
|
@@ -431,16 +478,40 @@ void computeNode(const Node *node, const XtStyle *parent, const StyleSheet &shee
|
|
|
431
478
|
|
|
432
479
|
void xt_style_compute(const Node *root, const StyleSheet &sheet,
|
|
433
480
|
std::unordered_map<const Node *, XtStyle> &out, float viewportWidth,
|
|
434
|
-
float viewportHeight, const MatchState &state, PseudoNodeSet *pseudos
|
|
481
|
+
float viewportHeight, const MatchState &state, PseudoNodeSet *pseudos,
|
|
482
|
+
bool *hasTransitions) {
|
|
435
483
|
out.clear();
|
|
436
|
-
|
|
484
|
+
bool wantsPseudo = pseudos != nullptr && sheet.has_pseudo_elements;
|
|
485
|
+
if (wantsPseudo && root != nullptr) {
|
|
437
486
|
resetPseudoPointers(root);
|
|
438
487
|
pseudos->clear();
|
|
439
488
|
}
|
|
440
489
|
ApplyContext context;
|
|
441
490
|
context.viewportWidth = viewportWidth;
|
|
442
491
|
context.viewportHeight = viewportHeight;
|
|
443
|
-
|
|
492
|
+
/* An `@media` prelude is evaluated once per rule (not once per rule *and*
|
|
493
|
+
* node): the query never depends on the element, so this was the engine's
|
|
494
|
+
* largest per-element cost on a large document. */
|
|
495
|
+
std::vector<char> applies(sheet.rules.size(), 0);
|
|
496
|
+
for (size_t i = 0; i < sheet.rules.size(); i++) {
|
|
497
|
+
const std::string &media = sheet.rules[i].media;
|
|
498
|
+
if (media.empty()) {
|
|
499
|
+
applies[i] = 1;
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
g_perf_frame.mediaEvaluated += 1;
|
|
503
|
+
applies[i] =
|
|
504
|
+
xt_css_media_matches(media, viewportWidth, viewportHeight) ? (char)1 : (char)0;
|
|
505
|
+
}
|
|
506
|
+
ComputeScratch scratch;
|
|
507
|
+
scratch.candidates.reserve(32);
|
|
508
|
+
if (root != nullptr) {
|
|
509
|
+
computeNode(root, nullptr, sheet, applies, out, context, state, wantsPseudo ? pseudos : nullptr,
|
|
510
|
+
&scratch);
|
|
511
|
+
}
|
|
512
|
+
if (hasTransitions != nullptr) {
|
|
513
|
+
*hasTransitions = *hasTransitions || scratch.has_transitions;
|
|
514
|
+
}
|
|
444
515
|
}
|
|
445
516
|
|
|
446
517
|
} // namespace xtgui
|
package/runtime/ext_gui/style.h
CHANGED
|
@@ -60,7 +60,7 @@ struct BackgroundGradientStop {
|
|
|
60
60
|
Color color;
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
enum class BackgroundKind { Linear, Radial };
|
|
63
|
+
enum class BackgroundKind { Linear, Radial, Image };
|
|
64
64
|
|
|
65
65
|
/** How a radial layer's radii are chosen when it has no explicit lengths. */
|
|
66
66
|
enum class RadialSize { ClosestSide, FarthestSide, ClosestCorner, FarthestCorner, Explicit };
|
|
@@ -80,6 +80,8 @@ struct BackgroundLayer {
|
|
|
80
80
|
Length center_y{50.0f, Unit::Percent};
|
|
81
81
|
Length radius_x{0.0f, Unit::Auto}; // Explicit radii only
|
|
82
82
|
Length radius_y{0.0f, Unit::Auto};
|
|
83
|
+
/** `BackgroundKind::Image`: the `url(...)` source (a path or data URL). */
|
|
84
|
+
std::string image_src;
|
|
83
85
|
std::vector<BackgroundGradientStop> stops;
|
|
84
86
|
};
|
|
85
87
|
|
|
@@ -262,11 +264,12 @@ class PseudoNodeSet {
|
|
|
262
264
|
* `state` supplies `:hover`/`:focus` for dynamic selector matching. When
|
|
263
265
|
* `pseudos` is non-null, `::before`/`::after` styles are computed too and each
|
|
264
266
|
* element's transient `pseudo_before`/`pseudo_after` pointers are published for
|
|
265
|
-
* layout.
|
|
267
|
+
* layout. When `hasTransitions` is non-null it is OR-ed with whether any
|
|
268
|
+
* computed style declares a `transition` (inline `style="…"` included). */
|
|
266
269
|
void xt_style_compute(const Node *root, const StyleSheet &sheet,
|
|
267
270
|
std::unordered_map<const Node *, XtStyle> &out, float viewportWidth,
|
|
268
271
|
float viewportHeight, const MatchState &state = MatchState(),
|
|
269
|
-
PseudoNodeSet *pseudos = nullptr);
|
|
272
|
+
PseudoNodeSet *pseudos = nullptr, bool *hasTransitions = nullptr);
|
|
270
273
|
|
|
271
274
|
/** Format a computed property for diagnostics/tests (`computedStyle(sel, prop)`). */
|
|
272
275
|
std::string xt_style_property_to_string(const XtStyle &style, const std::string &property);
|
|
@@ -247,7 +247,13 @@ std::string xt_style_property_to_string(const XtStyle &style, const std::string
|
|
|
247
247
|
std::string out;
|
|
248
248
|
for (const BackgroundLayer &layer : style.background_layers) {
|
|
249
249
|
if (!out.empty()) out += ", ";
|
|
250
|
-
|
|
250
|
+
if (layer.kind == BackgroundKind::Linear) {
|
|
251
|
+
out += "linear-gradient()";
|
|
252
|
+
} else if (layer.kind == BackgroundKind::Radial) {
|
|
253
|
+
out += "radial-gradient()";
|
|
254
|
+
} else {
|
|
255
|
+
out += "url(" + layer.image_src + ")";
|
|
256
|
+
}
|
|
251
257
|
}
|
|
252
258
|
return out;
|
|
253
259
|
}
|
package/runtime/ext_gui/text.cpp
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
#include "text.h"
|
|
19
19
|
|
|
20
|
+
#include "perf.h"
|
|
21
|
+
|
|
20
22
|
#include <ft2build.h>
|
|
21
23
|
#include FT_FREETYPE_H
|
|
22
24
|
|
|
@@ -25,6 +27,7 @@
|
|
|
25
27
|
|
|
26
28
|
#include <cmath>
|
|
27
29
|
#include <cstddef>
|
|
30
|
+
#include <cstdint>
|
|
28
31
|
#include <cstdio>
|
|
29
32
|
#include <cstdlib>
|
|
30
33
|
#include <cstring>
|
|
@@ -125,6 +128,10 @@ struct Engine {
|
|
|
125
128
|
std::unordered_map<std::string, std::unique_ptr<Font>> cache; // key -> face (may be null)
|
|
126
129
|
std::unordered_map<std::string, std::vector<Font *>> fallbacks; // class+size -> faces
|
|
127
130
|
std::unordered_map<std::string, hb_face_t *> hb_faces; // path -> face
|
|
131
|
+
/** Primary face by (family class, pixel size): `xt_text_resolve` and
|
|
132
|
+
* `xt_text_metrics` are called per text box per layout, and building the
|
|
133
|
+
* 512-byte path key each time showed up in the profile. */
|
|
134
|
+
std::unordered_map<std::string, Font *> primary;
|
|
128
135
|
};
|
|
129
136
|
|
|
130
137
|
Engine &engine() {
|
|
@@ -416,9 +423,15 @@ Font *xt_text_resolve(const FontSpec &spec) {
|
|
|
416
423
|
if (!e.ft_ok) return nullptr;
|
|
417
424
|
|
|
418
425
|
bool mono = isMonospaceFamily(spec.family);
|
|
426
|
+
char key[48];
|
|
427
|
+
std::snprintf(key, sizeof(key), "%c|%.4f", mono ? 'm' : 's', (double)spec.pixel_size);
|
|
428
|
+
auto cached = e.primary.find(key);
|
|
429
|
+
if (cached != e.primary.end()) return cached->second;
|
|
430
|
+
|
|
419
431
|
const std::string &path = mono ? e.mono_path : e.sans_path;
|
|
420
|
-
|
|
421
|
-
|
|
432
|
+
Font *font = path.empty() ? nullptr : resolveFacePath(path, spec.pixel_size, mono);
|
|
433
|
+
e.primary.emplace(key, font);
|
|
434
|
+
return font;
|
|
422
435
|
}
|
|
423
436
|
|
|
424
437
|
Font *xt_text_scale(Font *font, float pixel_size) {
|
|
@@ -429,12 +442,14 @@ Font *xt_text_scale(Font *font, float pixel_size) {
|
|
|
429
442
|
float xt_text_measure_width(const std::string &utf8, const FontSpec &spec) {
|
|
430
443
|
Font *font = xt_text_resolve(spec);
|
|
431
444
|
if (font == nullptr) return approximateWidth(utf8, spec.pixel_size, spec.family);
|
|
432
|
-
|
|
433
|
-
|
|
445
|
+
if (utf8.empty()) return 0.0f;
|
|
446
|
+
const ShapedRun *run = xt_text_shape_cached(utf8, font);
|
|
447
|
+
return run != nullptr ? run->width : 0.0f;
|
|
434
448
|
}
|
|
435
449
|
|
|
436
450
|
float xt_text_shape_run(const std::string &utf8, Font *font, std::vector<ShapedGlyph> &out) {
|
|
437
451
|
if (font == nullptr) return 0;
|
|
452
|
+
g_perf_frame.textShapeCalls += 1;
|
|
438
453
|
float total = 0;
|
|
439
454
|
size_t index = 0;
|
|
440
455
|
size_t segment_start = 0;
|
|
@@ -502,6 +517,10 @@ bool xt_text_ready() {
|
|
|
502
517
|
|
|
503
518
|
void xt_text_shutdown() {
|
|
504
519
|
Engine &e = engine();
|
|
520
|
+
/* Cached shaped runs point at faces, so drop them before the faces go. */
|
|
521
|
+
xt_text_cache_clear();
|
|
522
|
+
xt_text_glyph_cache_clear();
|
|
523
|
+
e.primary.clear();
|
|
505
524
|
e.fallbacks.clear();
|
|
506
525
|
e.cache.clear();
|
|
507
526
|
for (auto &entry : e.hb_faces) {
|
package/runtime/ext_gui/text.h
CHANGED
|
@@ -40,6 +40,12 @@ struct ShapedGlyph {
|
|
|
40
40
|
Font *font = nullptr;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
/** One shaped run: its glyphs and total advance. */
|
|
44
|
+
struct ShapedRun {
|
|
45
|
+
float width = 0;
|
|
46
|
+
std::vector<ShapedGlyph> glyphs;
|
|
47
|
+
};
|
|
48
|
+
|
|
43
49
|
/** An 8-bit grayscale glyph bitmap, as produced by FreeType. */
|
|
44
50
|
struct GlyphImage {
|
|
45
51
|
int width = 0;
|
|
@@ -59,6 +65,19 @@ Font *xt_text_resolve(const FontSpec &spec);
|
|
|
59
65
|
* glyph's `font` says which face it belongs to. */
|
|
60
66
|
float xt_text_shape_run(const std::string &utf8, Font *font, std::vector<ShapedGlyph> &out);
|
|
61
67
|
|
|
68
|
+
/** Shape `utf8` through the process-wide bounded cache (`text_cache.cpp`).
|
|
69
|
+
* Returns a stable pointer for the lifetime of the cache entry (valid until the
|
|
70
|
+
* next cache generation is dropped); callers must not hold it across frames.
|
|
71
|
+
* Layout and paint both go through this, so a word is shaped once instead of
|
|
72
|
+
* once per layout pass and again per repaint. `nullptr` for a null face. */
|
|
73
|
+
const ShapedRun *xt_text_shape_cached(const std::string &utf8, Font *font);
|
|
74
|
+
|
|
75
|
+
/** Current cache occupancy (bytes of keys + glyphs, number of entries). */
|
|
76
|
+
uint64_t xt_text_cache_bytes();
|
|
77
|
+
uint64_t xt_text_cache_entries();
|
|
78
|
+
/** Drop every entry (called by `xt_text_shutdown`, and by tests). */
|
|
79
|
+
void xt_text_cache_clear();
|
|
80
|
+
|
|
62
81
|
/** The same face as `font` at a different pixel size (used to rasterise glyphs
|
|
63
82
|
* at the display's scale factor), or null when it cannot be loaded. */
|
|
64
83
|
Font *xt_text_scale(Font *font, float pixel_size);
|
|
@@ -67,6 +86,16 @@ Font *xt_text_scale(Font *font, float pixel_size);
|
|
|
67
86
|
* the glyph has no outline (e.g. a space) or cannot be rendered. */
|
|
68
87
|
bool xt_text_rasterize(Font *font, uint32_t glyph, GlyphImage &out);
|
|
69
88
|
|
|
89
|
+
/** `xt_text_rasterize` through a bounded, two-generation cache. A canvas that
|
|
90
|
+
* redraws the same text every frame otherwise runs FreeType for every glyph of
|
|
91
|
+
* every `fillText`. The pointer stays valid for the rest of the frame (until a
|
|
92
|
+
* later call fills a generation and drops the spare one); callers must not hold
|
|
93
|
+
* it across frames. Returns null for a glyph without a bitmap. */
|
|
94
|
+
const GlyphImage *xt_text_glyph_cached(Font *font, uint32_t glyph);
|
|
95
|
+
|
|
96
|
+
/** Drop every cached glyph bitmap (called by `xt_text_shutdown`). */
|
|
97
|
+
void xt_text_glyph_cache_clear(void);
|
|
98
|
+
|
|
70
99
|
/** Shaped horizontal advance of `utf8` in pixels. Falls back to a deterministic
|
|
71
100
|
* approximation when no font file can be loaded. */
|
|
72
101
|
float xt_text_measure_width(const std::string &utf8, const FontSpec &spec);
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — bounded shaping cache.
|
|
3
|
+
*
|
|
4
|
+
* Layout measures every word of the document on every layout pass, and the
|
|
5
|
+
* renderer shapes every text run again to place its glyphs. Without a cache a
|
|
6
|
+
* document with a few thousand words runs thousands of HarfBuzz shapes per
|
|
7
|
+
* frame, which is what made a large page unusable. This module memoises
|
|
8
|
+
* `(face, text) -> glyphs + advance` behind a two-generation cache: when the
|
|
9
|
+
* budget is exceeded the *spare* generation is dropped and becomes the current
|
|
10
|
+
* one, so no entry-by-entry LRU bookkeeping is needed on the hot path (a
|
|
11
|
+
* document that cycles through many strings keeps at most two budgets).
|
|
12
|
+
*
|
|
13
|
+
* The key is the resolved face pointer plus the text: fallback faces, sizes and
|
|
14
|
+
* `XT_GUI_FONT` overrides each resolve to their own face, and
|
|
15
|
+
* `xt_text_shutdown()` clears the cache before the faces are freed.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#include "text.h"
|
|
19
|
+
|
|
20
|
+
#include "perf.h"
|
|
21
|
+
|
|
22
|
+
#include <string>
|
|
23
|
+
#include <unordered_map>
|
|
24
|
+
#include <utility>
|
|
25
|
+
#include <vector>
|
|
26
|
+
|
|
27
|
+
namespace xtgui {
|
|
28
|
+
namespace {
|
|
29
|
+
|
|
30
|
+
/** Glyph + key budget per generation. A shaped glyph is 24 bytes, so 2 MiB is
|
|
31
|
+
* roughly 80k glyphs (more than any single document draws). */
|
|
32
|
+
constexpr uint64_t kMaxBytes = 2ull * 1024 * 1024;
|
|
33
|
+
constexpr uint64_t kMaxEntries = 8192;
|
|
34
|
+
|
|
35
|
+
struct Key {
|
|
36
|
+
Font *font = nullptr;
|
|
37
|
+
std::string text;
|
|
38
|
+
|
|
39
|
+
bool operator==(const Key &other) const {
|
|
40
|
+
return font == other.font && text == other.text;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
struct KeyHash {
|
|
45
|
+
size_t operator()(const Key &key) const {
|
|
46
|
+
size_t hash = std::hash<const void *>()(key.font);
|
|
47
|
+
// FNV-ish mix of the text, then combine — cheap and stable.
|
|
48
|
+
size_t textHash = std::hash<std::string>()(key.text);
|
|
49
|
+
hash ^= textHash + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2);
|
|
50
|
+
return hash;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
struct Generation {
|
|
55
|
+
std::unordered_map<Key, ShapedRun, KeyHash> runs;
|
|
56
|
+
uint64_t bytes = 0;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
Generation g_generations[2];
|
|
60
|
+
int g_current = 0;
|
|
61
|
+
|
|
62
|
+
uint64_t estimateBytes(const Key &key, const ShapedRun &run) {
|
|
63
|
+
/* Key/Run objects plus the map node's own overhead (a rough, conservative
|
|
64
|
+
* constant: the budget is a guard, not an allocator). */
|
|
65
|
+
return (uint64_t)sizeof(Key) + (uint64_t)key.text.size() + (uint64_t)sizeof(ShapedRun) +
|
|
66
|
+
(uint64_t)run.glyphs.size() * sizeof(ShapedGlyph) + 64;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Rotate to the spare generation when the current one is full. */
|
|
70
|
+
void respectBudget() {
|
|
71
|
+
Generation ¤t = g_generations[g_current];
|
|
72
|
+
if (current.runs.size() <= kMaxEntries && current.bytes <= kMaxBytes) return;
|
|
73
|
+
int spare = 1 - g_current;
|
|
74
|
+
g_generations[spare].runs.clear();
|
|
75
|
+
g_generations[spare].bytes = 0;
|
|
76
|
+
g_current = spare;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
} // namespace
|
|
80
|
+
|
|
81
|
+
const ShapedRun *xt_text_shape_cached(const std::string &utf8, Font *font) {
|
|
82
|
+
if (font == nullptr) return nullptr;
|
|
83
|
+
if (utf8.empty()) return nullptr;
|
|
84
|
+
Key key;
|
|
85
|
+
key.font = font;
|
|
86
|
+
key.text = utf8;
|
|
87
|
+
Generation &generation = g_generations[g_current];
|
|
88
|
+
auto found = generation.runs.find(key);
|
|
89
|
+
if (found != generation.runs.end()) {
|
|
90
|
+
g_perf_frame.textShapeHits += 1;
|
|
91
|
+
return &found->second;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
auto inserted = generation.runs.emplace(std::move(key), ShapedRun());
|
|
95
|
+
ShapedRun &run = inserted.first->second;
|
|
96
|
+
run.width = xt_text_shape_run(utf8, font, run.glyphs);
|
|
97
|
+
generation.bytes += estimateBytes(inserted.first->first, run);
|
|
98
|
+
respectBudget();
|
|
99
|
+
return &run;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
uint64_t xt_text_cache_bytes() {
|
|
103
|
+
return g_generations[0].bytes + g_generations[1].bytes;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
uint64_t xt_text_cache_entries() {
|
|
107
|
+
return (uint64_t)(g_generations[0].runs.size() + g_generations[1].runs.size());
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void xt_text_cache_clear() {
|
|
111
|
+
g_generations[0].runs.clear();
|
|
112
|
+
g_generations[0].bytes = 0;
|
|
113
|
+
g_generations[1].runs.clear();
|
|
114
|
+
g_generations[1].bytes = 0;
|
|
115
|
+
g_current = 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
} // namespace xtgui
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — cached glyph bitmaps.
|
|
3
|
+
*
|
|
4
|
+
* `xt_text_rasterize` runs FreeType for one glyph. Canvas `fillText` used to
|
|
5
|
+
* call it for every glyph of every draw, so a redrawn label paid the whole
|
|
6
|
+
* rasterisation cost again on every frame. This unit keeps the bitmaps in two
|
|
7
|
+
* bounded generations, the same policy as the shaped-run cache in
|
|
8
|
+
* `text_cache.cpp`: a hot generation survives, and a page that walks a huge
|
|
9
|
+
* alphabet cannot grow the cache without bound.
|
|
10
|
+
*
|
|
11
|
+
* The cache is process-wide (fonts are), keyed by the face pointer and the
|
|
12
|
+
* glyph id. It is deliberately leaked at shutdown like the other engine
|
|
13
|
+
* caches; `xt_text_glyph_cache_clear` drops its contents when the fonts go.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
#include "text.h"
|
|
17
|
+
|
|
18
|
+
#include <cstddef>
|
|
19
|
+
#include <cstdint>
|
|
20
|
+
#include <unordered_map>
|
|
21
|
+
#include <utility>
|
|
22
|
+
|
|
23
|
+
namespace xtgui {
|
|
24
|
+
|
|
25
|
+
namespace {
|
|
26
|
+
|
|
27
|
+
/** Per-generation bitmap budget and entry cap. */
|
|
28
|
+
constexpr size_t kGlyphCacheBytes = 2u * 1024u * 1024u;
|
|
29
|
+
constexpr size_t kGlyphCacheEntries = 8192;
|
|
30
|
+
|
|
31
|
+
struct GlyphCache {
|
|
32
|
+
std::unordered_map<uint64_t, GlyphImage> current;
|
|
33
|
+
std::unordered_map<uint64_t, GlyphImage> spare;
|
|
34
|
+
size_t current_bytes = 0;
|
|
35
|
+
size_t spare_bytes = 0;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
GlyphCache &glyph_cache() {
|
|
39
|
+
/* Deliberately leaked: `Font` objects are destroyed at shutdown after this
|
|
40
|
+
* cache would be, and the key holds their addresses (never dereferenced). */
|
|
41
|
+
static GlyphCache *cache = new GlyphCache();
|
|
42
|
+
return *cache;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
uint64_t glyph_key(Font *font, uint32_t glyph) {
|
|
46
|
+
return (uint64_t)(uintptr_t)font << 32 | (uint64_t)glyph;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Drop the old generation and make the current one the spare. */
|
|
50
|
+
void rotate(GlyphCache &cache) {
|
|
51
|
+
cache.current.swap(cache.spare);
|
|
52
|
+
std::swap(cache.current_bytes, cache.spare_bytes);
|
|
53
|
+
cache.spare.clear();
|
|
54
|
+
cache.spare_bytes = 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
} // namespace
|
|
58
|
+
|
|
59
|
+
const GlyphImage *xt_text_glyph_cached(Font *font, uint32_t glyph) {
|
|
60
|
+
if (font == nullptr) return nullptr;
|
|
61
|
+
GlyphCache &cache = glyph_cache();
|
|
62
|
+
const uint64_t key = glyph_key(font, glyph);
|
|
63
|
+
auto found = cache.current.find(key);
|
|
64
|
+
if (found != cache.current.end()) {
|
|
65
|
+
return found->second.pixels.empty() ? nullptr : &found->second;
|
|
66
|
+
}
|
|
67
|
+
GlyphImage image;
|
|
68
|
+
auto spare = cache.spare.find(key);
|
|
69
|
+
if (spare != cache.spare.end()) {
|
|
70
|
+
image = std::move(spare->second);
|
|
71
|
+
cache.spare_bytes -= image.pixels.size();
|
|
72
|
+
cache.spare.erase(spare);
|
|
73
|
+
} else if (!xt_text_rasterize(font, glyph, image)) {
|
|
74
|
+
/* Cache the miss too: a space (or .notdef) would otherwise retry FreeType
|
|
75
|
+
* on every `fillText`. */
|
|
76
|
+
image.width = 0;
|
|
77
|
+
image.height = 0;
|
|
78
|
+
image.left = 0;
|
|
79
|
+
image.top = 0;
|
|
80
|
+
}
|
|
81
|
+
const size_t bytes = image.pixels.size();
|
|
82
|
+
if ((cache.current_bytes + bytes > kGlyphCacheBytes ||
|
|
83
|
+
cache.current.size() >= kGlyphCacheEntries) &&
|
|
84
|
+
!cache.current.empty()) {
|
|
85
|
+
rotate(cache);
|
|
86
|
+
}
|
|
87
|
+
cache.current_bytes += bytes;
|
|
88
|
+
auto inserted = cache.current.emplace(key, std::move(image));
|
|
89
|
+
return inserted.first->second.pixels.empty() ? nullptr : &inserted.first->second;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void xt_text_glyph_cache_clear(void) {
|
|
93
|
+
GlyphCache &cache = glyph_cache();
|
|
94
|
+
cache.current.clear();
|
|
95
|
+
cache.spare.clear();
|
|
96
|
+
cache.current_bytes = 0;
|
|
97
|
+
cache.spare_bytes = 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
} // namespace xtgui
|