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,374 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `backdrop-filter` compositor.
|
|
3
|
+
*
|
|
4
|
+
* The backdrop is the scene painted before the element, which only the renderer
|
|
5
|
+
* sees. `drawBackdropPass` samples a copy of that scene (the caller ping-pongs a
|
|
6
|
+
* scratch scene texture), applies the Gaussian and the combined colour matrix in
|
|
7
|
+
* one runtime-compiled shader, and writes the result back through the element's
|
|
8
|
+
* rounded border-box mask. Split from `renderer_layer.cpp` so the blend
|
|
9
|
+
* compositor and this one stay independently readable.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include "renderer_internal.h"
|
|
13
|
+
|
|
14
|
+
#include <algorithm>
|
|
15
|
+
#include <cmath>
|
|
16
|
+
#include <cstdio>
|
|
17
|
+
#include <cstring>
|
|
18
|
+
#include <string>
|
|
19
|
+
#include <vector>
|
|
20
|
+
|
|
21
|
+
#include "gpu_shader_api.h"
|
|
22
|
+
#include "perf.h"
|
|
23
|
+
#include "webgl_glsl.h"
|
|
24
|
+
|
|
25
|
+
namespace xtgui {
|
|
26
|
+
namespace renderer_detail {
|
|
27
|
+
|
|
28
|
+
namespace {
|
|
29
|
+
|
|
30
|
+
/** Compile one GLSL stage into the format this device consumes. Mirrors the
|
|
31
|
+
* helper in `renderer_layer.cpp`; both shaders are compiled at run time because
|
|
32
|
+
* they are not part of the precompiled WebGL blobs. */
|
|
33
|
+
bool prepareStage(const std::string &source, bool fragment, SDL_GPUShaderFormat formats,
|
|
34
|
+
std::vector<uint32_t> *spirv, std::string *text, const Uint8 **code,
|
|
35
|
+
size_t *code_size, std::string *entry, std::string *log) {
|
|
36
|
+
const GpuShaderApi *api = gpu_shader_api();
|
|
37
|
+
if (api == nullptr) {
|
|
38
|
+
*log = "shader translation was not linked (the 'gpu' link feature is inactive)";
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
GlslShaderInfo info;
|
|
42
|
+
api->scan_glsl(source, fragment, &info);
|
|
43
|
+
if (!info.unsupported.empty()) {
|
|
44
|
+
*log = info.unsupported;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
GlslLinkLayout layout;
|
|
48
|
+
layout.varyings["v_uv"] = 0;
|
|
49
|
+
if (fragment) {
|
|
50
|
+
layout.samplers["u_backdrop"] = 0;
|
|
51
|
+
layout.block_members = info.uniforms;
|
|
52
|
+
}
|
|
53
|
+
const std::string rewritten = api->rewrite_glsl(source, fragment, layout, info);
|
|
54
|
+
if (!api->compile_glsl(rewritten, fragment, spirv, log)) return false;
|
|
55
|
+
|
|
56
|
+
if ((formats & SDL_GPU_SHADERFORMAT_MSL) != 0) {
|
|
57
|
+
if (!api->cross_compile_msl(*spirv, fragment, text, entry, log)) return false;
|
|
58
|
+
*code = (const Uint8 *)text->data();
|
|
59
|
+
*code_size = text->size();
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if ((formats & SDL_GPU_SHADERFORMAT_SPIRV) != 0) {
|
|
63
|
+
*code = (const Uint8 *)spirv->data();
|
|
64
|
+
*code_size = spirv->size() * sizeof(uint32_t);
|
|
65
|
+
*entry = "main";
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
*log = "this GPU backend consumes neither Metal nor Vulkan shaders";
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// `v_uv` comes from the shared full-screen vertex shader.
|
|
73
|
+
const char *const kBlendVS = R"GLSL(#version 300 es
|
|
74
|
+
precision highp float;
|
|
75
|
+
|
|
76
|
+
out vec2 v_uv;
|
|
77
|
+
|
|
78
|
+
void main() {
|
|
79
|
+
vec2 corners[6] = vec2[6](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0),
|
|
80
|
+
vec2(1.0, -1.0), vec2(1.0, 1.0), vec2(-1.0, 1.0));
|
|
81
|
+
vec2 p = corners[gl_VertexIndex];
|
|
82
|
+
v_uv = vec2(p.x * 0.5 + 0.5, 0.5 - p.y * 0.5);
|
|
83
|
+
gl_Position = vec4(p, 0.0, 1.0);
|
|
84
|
+
}
|
|
85
|
+
)GLSL";
|
|
86
|
+
|
|
87
|
+
/* `backdrop-filter`: samples the scene painted so far with a separable-looking
|
|
88
|
+
* 13x13 Gaussian (taps spread to cover sigma) and one combined colour matrix,
|
|
89
|
+
* then masks the result to the element's rounded border box. The scene has
|
|
90
|
+
* already been copied to the sampled texture by a copy pass, so this shader
|
|
91
|
+
* never reads the target it writes. */
|
|
92
|
+
const char *const kBackdropFS = R"GLSL(#version 300 es
|
|
93
|
+
precision highp float;
|
|
94
|
+
|
|
95
|
+
in vec2 v_uv;
|
|
96
|
+
uniform sampler2D u_backdrop;
|
|
97
|
+
uniform vec4 u_rect; // x, y, width, height in target pixels
|
|
98
|
+
uniform vec4 u_mask; // corner radius, sigma, target width, target height
|
|
99
|
+
uniform vec4 u_texel; // 1/target width, 1/target height
|
|
100
|
+
uniform vec4 u_row0; // colour matrix row 0 + offset
|
|
101
|
+
uniform vec4 u_row1;
|
|
102
|
+
uniform vec4 u_row2;
|
|
103
|
+
uniform vec4 u_extra; // alpha multiplier
|
|
104
|
+
out vec4 fragColor;
|
|
105
|
+
|
|
106
|
+
float roundedRectDistance(vec2 point, vec4 rect, float radius) {
|
|
107
|
+
vec2 half_size = rect.zw * 0.5;
|
|
108
|
+
vec2 centre = rect.xy + half_size;
|
|
109
|
+
radius = min(radius, min(half_size.x, half_size.y));
|
|
110
|
+
vec2 q = abs(point - centre) - (half_size - vec2(radius));
|
|
111
|
+
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - radius;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
void main() {
|
|
115
|
+
float sigma = u_mask.y;
|
|
116
|
+
vec4 sampled;
|
|
117
|
+
if (sigma > 0.05) {
|
|
118
|
+
float step = max(1.0, sigma * 3.0 / 6.0);
|
|
119
|
+
float total = 0.0;
|
|
120
|
+
vec4 sum = vec4(0.0);
|
|
121
|
+
for (int y = -6; y <= 6; y++) {
|
|
122
|
+
for (int x = -6; x <= 6; x++) {
|
|
123
|
+
float distance2 = float(x * x + y * y) * step * step;
|
|
124
|
+
float weight = exp(-distance2 / (2.0 * sigma * sigma));
|
|
125
|
+
vec4 texel = texture(u_backdrop, v_uv + vec2(float(x), float(y)) * step * u_texel.xy);
|
|
126
|
+
/* Premultiply before averaging: blurring straight alpha would pull
|
|
127
|
+
* transparent black into the edges of a shape. */
|
|
128
|
+
sum += vec4(texel.rgb * texel.a, texel.a) * weight;
|
|
129
|
+
total += weight;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
vec4 blended = sum / total;
|
|
133
|
+
sampled = vec4(blended.a > 0.00001 ? blended.rgb / blended.a : vec3(0.0), blended.a);
|
|
134
|
+
} else {
|
|
135
|
+
sampled = texture(u_backdrop, v_uv);
|
|
136
|
+
}
|
|
137
|
+
vec3 rgb = sampled.rgb;
|
|
138
|
+
vec3 filtered = vec3(dot(u_row0.xyz, rgb) + u_row0.w,
|
|
139
|
+
dot(u_row1.xyz, rgb) + u_row1.w,
|
|
140
|
+
dot(u_row2.xyz, rgb) + u_row2.w);
|
|
141
|
+
filtered = clamp(filtered, 0.0, 1.0);
|
|
142
|
+
vec2 position = vec2(v_uv.x * u_mask.z, v_uv.y * u_mask.w);
|
|
143
|
+
float distance = roundedRectDistance(position, u_rect, u_mask.x);
|
|
144
|
+
float coverage = clamp(0.5 - distance, 0.0, 1.0);
|
|
145
|
+
if (coverage <= 0.0) discard;
|
|
146
|
+
fragColor = vec4(filtered, clamp(sampled.a, 0.0, 1.0) * u_extra.x * coverage);
|
|
147
|
+
}
|
|
148
|
+
)GLSL";
|
|
149
|
+
|
|
150
|
+
} // namespace
|
|
151
|
+
|
|
152
|
+
SDL_GPUGraphicsPipeline *g_backdrop_pipeline = nullptr;
|
|
153
|
+
SDL_GPUSampler *g_backdrop_sampler = nullptr;
|
|
154
|
+
bool g_backdrop_failed = false;
|
|
155
|
+
|
|
156
|
+
bool ensureBackdropPipeline(SDL_GPUDevice *device) {
|
|
157
|
+
if (g_backdrop_pipeline != nullptr) return true;
|
|
158
|
+
if (g_backdrop_failed) return false;
|
|
159
|
+
if (device == nullptr || g_swapchain_format == SDL_GPU_TEXTUREFORMAT_INVALID) return false;
|
|
160
|
+
const SDL_GPUTextureUsageFlags usage =
|
|
161
|
+
SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
|
|
162
|
+
if (!SDL_GPUTextureSupportsFormat(device, g_swapchain_format, SDL_GPU_TEXTURETYPE_2D, usage)) {
|
|
163
|
+
g_backdrop_failed = true;
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
const SDL_GPUShaderFormat formats = SDL_GetGPUShaderFormats(device);
|
|
167
|
+
if ((formats & (SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_SPIRV)) == 0) {
|
|
168
|
+
g_backdrop_failed = true;
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
std::string log;
|
|
173
|
+
std::vector<uint32_t> vs_spirv;
|
|
174
|
+
std::vector<uint32_t> fs_spirv;
|
|
175
|
+
std::string vs_text;
|
|
176
|
+
std::string fs_text;
|
|
177
|
+
const Uint8 *vs_code = nullptr;
|
|
178
|
+
const Uint8 *fs_code = nullptr;
|
|
179
|
+
size_t vs_size = 0;
|
|
180
|
+
size_t fs_size = 0;
|
|
181
|
+
std::string vs_entry;
|
|
182
|
+
std::string fs_entry;
|
|
183
|
+
if (!prepareStage(kBlendVS, false, formats, &vs_spirv, &vs_text, &vs_code, &vs_size, &vs_entry,
|
|
184
|
+
&log) ||
|
|
185
|
+
!prepareStage(kBackdropFS, true, formats, &fs_spirv, &fs_text, &fs_code, &fs_size,
|
|
186
|
+
&fs_entry, &log)) {
|
|
187
|
+
fprintf(stderr, "xt_gui: backdrop-filter shader unavailable: %s\n", log.c_str());
|
|
188
|
+
g_backdrop_failed = true;
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const SDL_GPUShaderFormat shader_format =
|
|
193
|
+
(formats & SDL_GPU_SHADERFORMAT_MSL) != 0 ? SDL_GPU_SHADERFORMAT_MSL
|
|
194
|
+
: SDL_GPU_SHADERFORMAT_SPIRV;
|
|
195
|
+
SDL_GPUShaderCreateInfo vs_info;
|
|
196
|
+
std::memset(&vs_info, 0, sizeof(vs_info));
|
|
197
|
+
vs_info.code_size = vs_size;
|
|
198
|
+
vs_info.code = vs_code;
|
|
199
|
+
vs_info.entrypoint = vs_entry.c_str();
|
|
200
|
+
vs_info.format = shader_format;
|
|
201
|
+
vs_info.stage = SDL_GPU_SHADERSTAGE_VERTEX;
|
|
202
|
+
SDL_GPUShader *vs = SDL_CreateGPUShader(device, &vs_info);
|
|
203
|
+
|
|
204
|
+
SDL_GPUShaderCreateInfo fs_info;
|
|
205
|
+
std::memset(&fs_info, 0, sizeof(fs_info));
|
|
206
|
+
fs_info.code_size = fs_size;
|
|
207
|
+
fs_info.code = fs_code;
|
|
208
|
+
fs_info.entrypoint = fs_entry.c_str();
|
|
209
|
+
fs_info.format = shader_format;
|
|
210
|
+
fs_info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
|
|
211
|
+
fs_info.num_uniform_buffers = 1;
|
|
212
|
+
fs_info.num_samplers = 1;
|
|
213
|
+
SDL_GPUShader *fs = SDL_CreateGPUShader(device, &fs_info);
|
|
214
|
+
|
|
215
|
+
if (vs == nullptr || fs == nullptr) {
|
|
216
|
+
fprintf(stderr, "xt_gui: backdrop-filter shader creation failed: %s\n", SDL_GetError());
|
|
217
|
+
if (vs != nullptr) SDL_ReleaseGPUShader(device, vs);
|
|
218
|
+
if (fs != nullptr) SDL_ReleaseGPUShader(device, fs);
|
|
219
|
+
g_backdrop_failed = true;
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
SDL_GPUColorTargetDescription target;
|
|
224
|
+
std::memset(&target, 0, sizeof(target));
|
|
225
|
+
target.format = g_swapchain_format;
|
|
226
|
+
target.blend_state.enable_blend = true;
|
|
227
|
+
target.blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA;
|
|
228
|
+
target.blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
|
|
229
|
+
target.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD;
|
|
230
|
+
target.blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE;
|
|
231
|
+
target.blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
|
|
232
|
+
target.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD;
|
|
233
|
+
|
|
234
|
+
SDL_GPUGraphicsPipelineCreateInfo info;
|
|
235
|
+
std::memset(&info, 0, sizeof(info));
|
|
236
|
+
info.vertex_shader = vs;
|
|
237
|
+
info.fragment_shader = fs;
|
|
238
|
+
info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
|
|
239
|
+
info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL;
|
|
240
|
+
info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_NONE;
|
|
241
|
+
info.rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE;
|
|
242
|
+
info.multisample_state.sample_count = SDL_GPU_SAMPLECOUNT_1;
|
|
243
|
+
info.target_info.color_target_descriptions = ⌖
|
|
244
|
+
info.target_info.num_color_targets = 1;
|
|
245
|
+
g_backdrop_pipeline = SDL_CreateGPUGraphicsPipeline(device, &info);
|
|
246
|
+
SDL_ReleaseGPUShader(device, vs);
|
|
247
|
+
SDL_ReleaseGPUShader(device, fs);
|
|
248
|
+
if (g_backdrop_pipeline == nullptr) {
|
|
249
|
+
fprintf(stderr, "xt_gui: backdrop-filter pipeline creation failed: %s\n", SDL_GetError());
|
|
250
|
+
g_backdrop_failed = true;
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
SDL_GPUSamplerCreateInfo sampler_info;
|
|
255
|
+
std::memset(&sampler_info, 0, sizeof(sampler_info));
|
|
256
|
+
sampler_info.min_filter = SDL_GPU_FILTER_LINEAR;
|
|
257
|
+
sampler_info.mag_filter = SDL_GPU_FILTER_LINEAR;
|
|
258
|
+
sampler_info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
|
|
259
|
+
sampler_info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
|
260
|
+
sampler_info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
|
261
|
+
sampler_info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
|
262
|
+
g_backdrop_sampler = SDL_CreateGPUSampler(device, &sampler_info);
|
|
263
|
+
if (g_backdrop_sampler == nullptr) {
|
|
264
|
+
fprintf(stderr, "xt_gui: backdrop-filter sampler creation failed: %s\n", SDL_GetError());
|
|
265
|
+
SDL_ReleaseGPUGraphicsPipeline(device, g_backdrop_pipeline);
|
|
266
|
+
g_backdrop_pipeline = nullptr;
|
|
267
|
+
g_backdrop_failed = true;
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
struct BackdropParams {
|
|
274
|
+
float rect[4];
|
|
275
|
+
float mask[4];
|
|
276
|
+
float texel[4];
|
|
277
|
+
float row0[4];
|
|
278
|
+
float row1[4];
|
|
279
|
+
float row2[4];
|
|
280
|
+
float extra[4];
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/** Filter `source` (the scene painted so far) into the active target only
|
|
284
|
+
* inside the element's rounded rect. The target already holds the same scene,
|
|
285
|
+
* so the fragment's source-over blend replaces the region with the filtered
|
|
286
|
+
* version while leaving rounded-corner coverage to antialias the edge. */
|
|
287
|
+
void drawBackdropPass(SDL_GPUCommandBuffer *cmd, SDL_GPURenderPass *pass,
|
|
288
|
+
SDL_GPUTexture *source, const DisplayList &list,
|
|
289
|
+
const XtGuiDrawRun &run, int target_width, int target_height,
|
|
290
|
+
float pixel_density) {
|
|
291
|
+
if (cmd == nullptr || pass == nullptr || source == nullptr ||
|
|
292
|
+
g_backdrop_pipeline == nullptr || g_backdrop_sampler == nullptr)
|
|
293
|
+
return;
|
|
294
|
+
if (run.backdrop >= list.backdrops.size()) return;
|
|
295
|
+
const xtgui::PaintBackdrop &backdrop = list.backdrops[run.backdrop];
|
|
296
|
+
if (backdrop.width <= 0.0f || backdrop.height <= 0.0f) return;
|
|
297
|
+
|
|
298
|
+
const float density = pixel_density;
|
|
299
|
+
float left = backdrop.x * density;
|
|
300
|
+
float top = backdrop.y * density;
|
|
301
|
+
float right = (backdrop.x + backdrop.width) * density;
|
|
302
|
+
float bottom = (backdrop.y + backdrop.height) * density;
|
|
303
|
+
if (run.clip_width > 0.0f && run.clip_height > 0.0f) {
|
|
304
|
+
left = std::max(left, run.clip_x * density);
|
|
305
|
+
top = std::max(top, run.clip_y * density);
|
|
306
|
+
right = std::min(right, (run.clip_x + run.clip_width) * density);
|
|
307
|
+
bottom = std::min(bottom, (run.clip_y + run.clip_height) * density);
|
|
308
|
+
}
|
|
309
|
+
int x0 = (int)std::floor(left);
|
|
310
|
+
int y0 = (int)std::floor(top);
|
|
311
|
+
int x1 = (int)std::ceil(right);
|
|
312
|
+
int y1 = (int)std::ceil(bottom);
|
|
313
|
+
if (x0 < 0) x0 = 0;
|
|
314
|
+
if (y0 < 0) y0 = 0;
|
|
315
|
+
if (x1 > target_width) x1 = target_width;
|
|
316
|
+
if (y1 > target_height) y1 = target_height;
|
|
317
|
+
if (x1 <= x0 || y1 <= y0) return;
|
|
318
|
+
|
|
319
|
+
SDL_BindGPUGraphicsPipeline(pass, g_backdrop_pipeline);
|
|
320
|
+
SDL_GPUTextureSamplerBinding binding;
|
|
321
|
+
binding.texture = source;
|
|
322
|
+
binding.sampler = g_backdrop_sampler;
|
|
323
|
+
SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1);
|
|
324
|
+
|
|
325
|
+
BackdropParams params;
|
|
326
|
+
std::memset(¶ms, 0, sizeof(params));
|
|
327
|
+
params.rect[0] = backdrop.x * density;
|
|
328
|
+
params.rect[1] = backdrop.y * density;
|
|
329
|
+
params.rect[2] = backdrop.width * density;
|
|
330
|
+
params.rect[3] = backdrop.height * density;
|
|
331
|
+
params.mask[0] = backdrop.radius * density;
|
|
332
|
+
params.mask[1] = backdrop.blur * density;
|
|
333
|
+
params.mask[2] = (float)target_width;
|
|
334
|
+
params.mask[3] = (float)target_height;
|
|
335
|
+
params.texel[0] = 1.0f / (float)target_width;
|
|
336
|
+
params.texel[1] = 1.0f / (float)target_height;
|
|
337
|
+
for (int i = 0; i < 3; i++) {
|
|
338
|
+
params.row0[i] = backdrop.color[i];
|
|
339
|
+
params.row1[i] = backdrop.color[3 + i];
|
|
340
|
+
params.row2[i] = backdrop.color[6 + i];
|
|
341
|
+
}
|
|
342
|
+
params.row0[3] = backdrop.offset[0];
|
|
343
|
+
params.row1[3] = backdrop.offset[1];
|
|
344
|
+
params.row2[3] = backdrop.offset[2];
|
|
345
|
+
params.extra[0] = backdrop.alpha;
|
|
346
|
+
SDL_PushGPUFragmentUniformData(cmd, 0, ¶ms, sizeof(params));
|
|
347
|
+
|
|
348
|
+
SDL_Rect scissor;
|
|
349
|
+
scissor.x = x0;
|
|
350
|
+
scissor.y = y0;
|
|
351
|
+
scissor.w = x1 - x0;
|
|
352
|
+
scissor.h = y1 - y0;
|
|
353
|
+
SDL_SetGPUScissor(pass, &scissor);
|
|
354
|
+
SDL_DrawGPUPrimitives(pass, 6, 1, 0, 0);
|
|
355
|
+
xtgui::g_perf_frame.drawCalls += 1;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
bool backdropRendererReady(SDL_GPUDevice *device) { return ensureBackdropPipeline(device); }
|
|
360
|
+
|
|
361
|
+
void backdropRendererDestroy(SDL_GPUDevice *device) {
|
|
362
|
+
if (g_backdrop_pipeline != nullptr) {
|
|
363
|
+
SDL_ReleaseGPUGraphicsPipeline(device, g_backdrop_pipeline);
|
|
364
|
+
g_backdrop_pipeline = nullptr;
|
|
365
|
+
}
|
|
366
|
+
if (g_backdrop_sampler != nullptr) {
|
|
367
|
+
SDL_ReleaseGPUSampler(device, g_backdrop_sampler);
|
|
368
|
+
g_backdrop_sampler = nullptr;
|
|
369
|
+
}
|
|
370
|
+
g_backdrop_failed = false;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
} // namespace renderer_detail
|
|
374
|
+
} // namespace xtgui
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — renderer textures for CPU-rasterised shadows.
|
|
3
|
+
*
|
|
4
|
+
* `paint_shadow.cpp` produces the blurred RGBA mask; this file uploads it once
|
|
5
|
+
* and caches the GPU texture by the mask's paint key. A shadow whose colour
|
|
6
|
+
* changes (the common animation) reuses the same mask and only the draw's tint
|
|
7
|
+
* differs, so the expensive rasterisation and upload happen on the first frame.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "renderer_internal.h"
|
|
11
|
+
|
|
12
|
+
#include <cstdio>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <unordered_map>
|
|
15
|
+
|
|
16
|
+
namespace xtgui {
|
|
17
|
+
namespace renderer_detail {
|
|
18
|
+
|
|
19
|
+
namespace {
|
|
20
|
+
|
|
21
|
+
struct ShadowTexture {
|
|
22
|
+
SDL_GPUTexture *texture = nullptr;
|
|
23
|
+
int width = 0;
|
|
24
|
+
int height = 0;
|
|
25
|
+
bool used = false;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
std::unordered_map<std::string, ShadowTexture> g_shadow_textures;
|
|
29
|
+
|
|
30
|
+
/** Keep a few stale masks for animated shadows that change shape every frame;
|
|
31
|
+
* the rest are released as soon as a paint pass stops drawing them. */
|
|
32
|
+
constexpr size_t kMaxStaleShadowTextures = 16;
|
|
33
|
+
|
|
34
|
+
} // namespace
|
|
35
|
+
|
|
36
|
+
void beginBitmapUpload() {
|
|
37
|
+
for (auto &entry : g_shadow_textures) entry.second.used = false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void finishBitmapUpload(SDL_GPUDevice *device) {
|
|
41
|
+
if (g_shadow_textures.size() <= kMaxStaleShadowTextures) return;
|
|
42
|
+
for (auto it = g_shadow_textures.begin(); it != g_shadow_textures.end();) {
|
|
43
|
+
if (it->second.used) {
|
|
44
|
+
++it;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (it->second.texture != nullptr) SDL_ReleaseGPUTexture(device, it->second.texture);
|
|
48
|
+
it = g_shadow_textures.erase(it);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
SDL_GPUTexture *ensureBitmapTexture(SDL_GPUDevice *device, const xtgui::PaintBitmap &bitmap,
|
|
53
|
+
XtGuiUploadBatch *batch) {
|
|
54
|
+
if (bitmap.width <= 0 || bitmap.height <= 0 || bitmap.pixels.empty()) return nullptr;
|
|
55
|
+
std::string key = bitmap.key + "#" + std::to_string(bitmap.width) + "x" +
|
|
56
|
+
std::to_string(bitmap.height);
|
|
57
|
+
ShadowTexture &entry = g_shadow_textures[key];
|
|
58
|
+
entry.used = true;
|
|
59
|
+
if (entry.texture != nullptr && entry.width == bitmap.width && entry.height == bitmap.height) {
|
|
60
|
+
return entry.texture;
|
|
61
|
+
}
|
|
62
|
+
xtgui::Image image;
|
|
63
|
+
image.width = bitmap.width;
|
|
64
|
+
image.height = bitmap.height;
|
|
65
|
+
image.pixels = bitmap.pixels;
|
|
66
|
+
SDL_GPUTexture *texture = createImageTexture(device, image, batch);
|
|
67
|
+
if (entry.texture != nullptr) SDL_ReleaseGPUTexture(device, entry.texture);
|
|
68
|
+
entry.texture = texture;
|
|
69
|
+
entry.width = texture != nullptr ? bitmap.width : 0;
|
|
70
|
+
entry.height = texture != nullptr ? bitmap.height : 0;
|
|
71
|
+
return texture;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
void releaseBitmapTextures(SDL_GPUDevice *device) {
|
|
75
|
+
for (auto &entry : g_shadow_textures) {
|
|
76
|
+
if (entry.second.texture != nullptr) SDL_ReleaseGPUTexture(device, entry.second.texture);
|
|
77
|
+
}
|
|
78
|
+
g_shadow_textures.clear();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
} // namespace renderer_detail
|
|
82
|
+
} // namespace xtgui
|
|
@@ -40,21 +40,28 @@ std::unordered_map<std::string, GradientTexture> g_gradient_textures;
|
|
|
40
40
|
* fresh texture every frame and never free the old ones. */
|
|
41
41
|
constexpr size_t kMaxStaleGradients = 8;
|
|
42
42
|
|
|
43
|
-
/** Largest texture edge a gradient is rasterised at. A
|
|
44
|
-
*
|
|
43
|
+
/** Largest texture edge a gradient is rasterised at. A ramp is smooth, so a
|
|
44
|
+
* larger box is rasterised smaller and stretched: the sampled colours are
|
|
45
45
|
* unchanged (the stretch is uniform), only the resolution of the ramp drops. */
|
|
46
46
|
constexpr int kMaxGradientEdge = 1024;
|
|
47
47
|
|
|
48
|
+
/** The same cap for a linear ramp, which is one texel thick across its line and
|
|
49
|
+
* therefore cheap at any length. A hairline stop (`calc(100% - 1px)`) wants the
|
|
50
|
+
* full device resolution of a wide window, so the 2D cap would soften it. */
|
|
51
|
+
constexpr int kMaxLinearGradientEdge = 4096;
|
|
52
|
+
|
|
48
53
|
unsigned char byteOf(float value) {
|
|
49
54
|
if (!(value > 0.0f)) return 0; // NaN and negatives
|
|
50
55
|
if (value > 1.0f) value = 1.0f;
|
|
51
56
|
return (unsigned char)(value * 255.0f + 0.5f);
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
/** Rasterise `gradient` into `out`: the box maps 1:1 onto the texture
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* are sampled with linear
|
|
59
|
+
/** Rasterise `gradient` into `out`: the box maps 1:1 onto the texture (the
|
|
60
|
+
* caller may have shrunk one axis, see `rasterSize`). The ramp covers the whole
|
|
61
|
+
* box — past the last stop CSS holds that colour rather than fading out — so
|
|
62
|
+
* there is no shape edge to antialias; the texels are sampled with linear
|
|
63
|
+
* filtering. A linear ramp is constant across the gradient line, so a walk
|
|
64
|
+
* along it is one row (or one column) of texels rather than a full box. */
|
|
58
65
|
void rasterizeGradient(const PaintGradient &gradient, float logical_width, float logical_height,
|
|
59
66
|
int pixel_width, int pixel_height, Image *out) {
|
|
60
67
|
out->width = pixel_width;
|
|
@@ -62,16 +69,29 @@ void rasterizeGradient(const PaintGradient &gradient, float logical_width, float
|
|
|
62
69
|
out->pixels.assign((size_t)pixel_width * (size_t)pixel_height * 4u, 0);
|
|
63
70
|
const float pixel_x = logical_width / (float)pixel_width; // logical px per texel
|
|
64
71
|
const float pixel_y = logical_height / (float)pixel_height;
|
|
65
|
-
const
|
|
66
|
-
const float
|
|
72
|
+
const bool linear = gradient.kind == PaintGradient::Kind::Linear;
|
|
73
|
+
const float inv_rx = linear || gradient.radius_x <= 0.0f ? 1.0f : 1.0f / gradient.radius_x;
|
|
74
|
+
const float inv_ry = linear || gradient.radius_y <= 0.0f ? 1.0f : 1.0f / gradient.radius_y;
|
|
75
|
+
const float inv_length =
|
|
76
|
+
linear && gradient.length > 0.00001f ? 1.0f / gradient.length : 0.0f;
|
|
67
77
|
for (int y = 0; y < pixel_height; y++) {
|
|
68
78
|
const float local_y = ((float)y + 0.5f) * pixel_y;
|
|
69
79
|
unsigned char *row = out->pixels.data() + (size_t)y * (size_t)pixel_width * 4u;
|
|
70
80
|
for (int x = 0; x < pixel_width; x++) {
|
|
71
81
|
const float local_x = ((float)x + 0.5f) * pixel_x;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
float t = 0.0f;
|
|
83
|
+
if (linear) {
|
|
84
|
+
/* A linear ramp is the position projected onto the gradient line. */
|
|
85
|
+
t = ((local_x - gradient.start_x) * gradient.dir_x +
|
|
86
|
+
(local_y - gradient.start_y) * gradient.dir_y) *
|
|
87
|
+
inv_length;
|
|
88
|
+
} else {
|
|
89
|
+
/* A radial ramp is the distance from the centre, normalised by the
|
|
90
|
+
* ending ellipse. */
|
|
91
|
+
const float u = (local_x - gradient.center_x) * inv_rx;
|
|
92
|
+
const float v = (local_y - gradient.center_y) * inv_ry;
|
|
93
|
+
t = std::sqrt(u * u + v * v);
|
|
94
|
+
}
|
|
75
95
|
Color color =
|
|
76
96
|
paint_detail::gradientColorAt(gradient.stops, gradient.repeating, gradient.period, t);
|
|
77
97
|
if (color.a <= 0.0f) continue;
|
|
@@ -85,12 +105,12 @@ void rasterizeGradient(const PaintGradient &gradient, float logical_width, float
|
|
|
85
105
|
}
|
|
86
106
|
|
|
87
107
|
/** The device size a gradient of `pixel_width` x `pixel_height` is rasterised at,
|
|
88
|
-
* preserving its aspect ratio under `
|
|
89
|
-
void rasterSize(int pixel_width, int pixel_height, int *width, int *height) {
|
|
108
|
+
* preserving its aspect ratio under `max_edge`. */
|
|
109
|
+
void rasterSize(int pixel_width, int pixel_height, int max_edge, int *width, int *height) {
|
|
90
110
|
*width = std::max(1, pixel_width);
|
|
91
111
|
*height = std::max(1, pixel_height);
|
|
92
|
-
if (*width <=
|
|
93
|
-
float shrink = (float)
|
|
112
|
+
if (*width <= max_edge && *height <= max_edge) return;
|
|
113
|
+
float shrink = (float)max_edge / (float)std::max(*width, *height);
|
|
94
114
|
*width = std::max(1, (int)((float)*width * shrink + 0.5f));
|
|
95
115
|
*height = std::max(1, (int)((float)*height * shrink + 0.5f));
|
|
96
116
|
}
|
|
@@ -119,12 +139,26 @@ SDL_GPUTexture *ensureGradientTexture(SDL_GPUDevice *device, const xtgui::PaintI
|
|
|
119
139
|
int pixel_width, int pixel_height, XtGuiUploadBatch *batch) {
|
|
120
140
|
if (image.gradient == nullptr || image.width <= 0.0f || image.height <= 0.0f) return nullptr;
|
|
121
141
|
const xtgui::PaintGradient &gradient = *image.gradient;
|
|
122
|
-
if (gradient.
|
|
142
|
+
if (gradient.stops.size() < 2) return nullptr;
|
|
143
|
+
const bool linear = gradient.kind == xtgui::PaintGradient::Kind::Linear;
|
|
144
|
+
if (linear) {
|
|
145
|
+
if (gradient.length <= 0.00001f) return nullptr;
|
|
146
|
+
} else if (gradient.radius_x <= 0.0f || gradient.radius_y <= 0.0f) {
|
|
123
147
|
return nullptr;
|
|
124
148
|
}
|
|
149
|
+
int across = pixel_width;
|
|
150
|
+
int down = pixel_height;
|
|
151
|
+
if (linear) {
|
|
152
|
+
/* A linear ramp does not change along the box's other axis, so one texel of
|
|
153
|
+
* it is enough: only the axis the ramp runs along needs resolution. */
|
|
154
|
+
if (std::fabs(gradient.dir_y) < 0.0001f) down = 1;
|
|
155
|
+
else if (std::fabs(gradient.dir_x) < 0.0001f) across = 1;
|
|
156
|
+
}
|
|
157
|
+
const bool one_dimensional = linear && (across == 1 || down == 1);
|
|
125
158
|
int width = 0;
|
|
126
159
|
int height = 0;
|
|
127
|
-
rasterSize(
|
|
160
|
+
rasterSize(across, down, one_dimensional ? kMaxLinearGradientEdge : kMaxGradientEdge, &width,
|
|
161
|
+
&height);
|
|
128
162
|
/* The key carries the ramp, its geometry and the logical box size: two boxes
|
|
129
163
|
* of different sizes can resolve the same px radius but not the same picture. */
|
|
130
164
|
char box[64];
|
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
#include "canvas.h"
|
|
13
13
|
#include "image.h"
|
|
14
|
+
#include "image_api.h"
|
|
14
15
|
#include "media.h"
|
|
15
16
|
#include "perf.h"
|
|
16
17
|
#include "svg.h"
|
|
17
|
-
#include "
|
|
18
|
+
#include "webgl_api.h"
|
|
18
19
|
|
|
19
20
|
#include "renderer_shaders.h"
|
|
20
21
|
|
|
@@ -157,8 +158,10 @@ static SDL_GPUTexture *ensureMediaTexture(SDL_GPUDevice *device, uint64_t media_
|
|
|
157
158
|
* directly and nothing is copied through the CPU. */
|
|
158
159
|
static SDL_GPUTexture *ensureCanvasTexture(SDL_GPUDevice *device, uint64_t canvas_id,
|
|
159
160
|
XtGuiUploadBatch *batch) {
|
|
160
|
-
if (
|
|
161
|
-
|
|
161
|
+
if (const XtWebglApi *webgl = xt_webgl_api()) {
|
|
162
|
+
if (void *texture = webgl->color_texture(canvas_id)) {
|
|
163
|
+
return static_cast<SDL_GPUTexture *>(texture);
|
|
164
|
+
}
|
|
162
165
|
}
|
|
163
166
|
xtgui::Canvas2D *canvas = xtgui::xt_canvas_by_id(canvas_id);
|
|
164
167
|
if (canvas == nullptr) return nullptr;
|
|
@@ -316,14 +319,16 @@ SDL_GPUTexture *createImageTexture(SDL_GPUDevice *device, const xtgui::Image &im
|
|
|
316
319
|
* their intrinsic size and are cached by path. */
|
|
317
320
|
SDL_GPUTexture *ensureImageTexture(SDL_GPUDevice *device, const std::string &src, int pixel_width,
|
|
318
321
|
int pixel_height, XtGuiUploadBatch *batch) {
|
|
319
|
-
|
|
322
|
+
const XtImageApi *images = xt_image_api();
|
|
323
|
+
if (images == nullptr) return nullptr;
|
|
324
|
+
bool vector_source = images->is_vector(src);
|
|
320
325
|
std::string key =
|
|
321
326
|
vector_source ? src + "@" + std::to_string(pixel_width) + "x" + std::to_string(pixel_height)
|
|
322
327
|
: src;
|
|
323
328
|
auto found = g_image_textures.find(key);
|
|
324
329
|
if (found != g_image_textures.end()) return found->second;
|
|
325
330
|
|
|
326
|
-
const xtgui::Image *image =
|
|
331
|
+
const xtgui::Image *image = images->load(src, pixel_width, pixel_height);
|
|
327
332
|
if (image == nullptr || image->width <= 0 || image->height <= 0) {
|
|
328
333
|
fprintf(stderr, "xt_gui: could not decode image '%s'\n", src.c_str());
|
|
329
334
|
g_image_textures.emplace(key, nullptr);
|
|
@@ -346,8 +351,10 @@ SDL_GPUTexture *ensureInlineSvgTexture(SDL_GPUDevice *device, const std::string
|
|
|
346
351
|
auto found = g_image_textures.find(key);
|
|
347
352
|
if (found != g_image_textures.end()) return found->second;
|
|
348
353
|
|
|
354
|
+
const XtImageApi *images = xt_image_api();
|
|
355
|
+
if (images == nullptr) return nullptr;
|
|
349
356
|
xtgui::Image image;
|
|
350
|
-
if (!
|
|
357
|
+
if (!images->svg_markup_render(markup, pixel_width, pixel_height, &image)) {
|
|
351
358
|
fprintf(stderr, "xt_gui: could not rasterise inline SVG\n");
|
|
352
359
|
g_image_textures.emplace(key, nullptr);
|
|
353
360
|
return nullptr;
|
|
@@ -380,6 +387,7 @@ void xt_gui_image_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
|
|
|
380
387
|
full.u1 = 1.0f;
|
|
381
388
|
full.v1 = 1.0f;
|
|
382
389
|
beginGradientUpload();
|
|
390
|
+
beginBitmapUpload();
|
|
383
391
|
for (const xtgui::PaintOp &op : list.ops) {
|
|
384
392
|
if (op.kind != xtgui::PaintKind::Image || op.index >= list.images.size()) continue;
|
|
385
393
|
const xtgui::PaintImage &image = list.images[op.index];
|
|
@@ -391,7 +399,9 @@ void xt_gui_image_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
|
|
|
391
399
|
if (pixel_width < 1) pixel_width = 1;
|
|
392
400
|
if (pixel_height < 1) pixel_height = 1;
|
|
393
401
|
SDL_GPUTexture *texture = nullptr;
|
|
394
|
-
if (image.
|
|
402
|
+
if (image.bitmap != nullptr) {
|
|
403
|
+
texture = ensureBitmapTexture(device, *image.bitmap, batch);
|
|
404
|
+
} else if (image.gradient != nullptr) {
|
|
395
405
|
texture = ensureGradientTexture(device, image, pixel_width, pixel_height, batch);
|
|
396
406
|
} else if (image.canvas_id != 0) {
|
|
397
407
|
texture = ensureCanvasTexture(device, image.canvas_id, batch);
|
|
@@ -409,12 +419,14 @@ void xt_gui_image_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
|
|
|
409
419
|
geometry->op_textures[op.index] = texture;
|
|
410
420
|
}
|
|
411
421
|
finishGradientUpload(device);
|
|
422
|
+
finishBitmapUpload(device);
|
|
412
423
|
uploadVertices(device, geometry, verts.data(), (uint32_t)verts.size(), sizeof(TextVertex), batch);
|
|
413
424
|
}
|
|
414
425
|
|
|
415
426
|
void xt_gui_image_textures_destroy(SDL_GPUDevice *device) {
|
|
416
427
|
using namespace xtgui::renderer_detail;
|
|
417
428
|
releaseGradientTextures(device);
|
|
429
|
+
releaseBitmapTextures(device);
|
|
418
430
|
for (auto &entry : g_image_textures) {
|
|
419
431
|
if (entry.second != nullptr) SDL_ReleaseGPUTexture(device, entry.second);
|
|
420
432
|
}
|