xbintsc 0.5.51 → 0.6.8
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/README.md +8 -2
- package/README.zh-CN.md +9 -3
- package/dist/scripts/fetch-tarball.d.ts +28 -0
- package/dist/scripts/fetch-tarball.js +57 -0
- package/dist/scripts/fetch-tarball.js.map +1 -0
- package/dist/scripts/gui-build-options.d.ts +77 -0
- package/dist/scripts/gui-build-options.js +134 -0
- package/dist/scripts/gui-build-options.js.map +1 -0
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/hints.d.ts +6 -2
- package/dist/src/cli/hints.js +16 -2
- package/dist/src/cli/hints.js.map +1 -1
- package/dist/src/cli/main.js +9 -0
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/driver/app-artifacts.d.ts +2 -0
- package/dist/src/driver/app-artifacts.js +3 -3
- package/dist/src/driver/app-artifacts.js.map +1 -1
- package/dist/src/driver/compiler.js +147 -52
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/icon.d.ts +1 -1
- package/dist/src/driver/icon.js +3 -3
- package/dist/src/driver/icon.js.map +1 -1
- package/dist/src/driver/macos-sdk.d.ts +32 -0
- package/dist/src/driver/macos-sdk.js +50 -0
- package/dist/src/driver/macos-sdk.js.map +1 -0
- package/dist/src/driver/options.d.ts +6 -0
- package/dist/src/driver/platform-support.d.ts +45 -0
- package/dist/src/driver/platform-support.js +66 -0
- package/dist/src/driver/platform-support.js.map +1 -0
- package/dist/src/driver/prebuilt.d.ts +26 -0
- package/dist/src/driver/prebuilt.js +58 -0
- package/dist/src/driver/prebuilt.js.map +1 -0
- package/dist/src/driver/runtime-objects.d.ts +11 -0
- package/dist/src/driver/runtime-objects.js +14 -5
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/toolchain-provider.d.ts +8 -0
- package/dist/src/driver/toolchain-provider.js +8 -0
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.d.ts +20 -1
- package/dist/src/driver/toolchain.js +19 -0
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/driver/version.js.map +1 -1
- package/dist/src/driver/win-entry.d.ts +1 -1
- package/dist/src/driver/win-entry.js +4 -4
- package/dist/src/driver/win-entry.js.map +1 -1
- package/dist/src/extensions/gui/features.d.ts +9 -6
- package/dist/src/extensions/gui/features.js +14 -9
- package/dist/src/extensions/gui/features.js.map +1 -1
- package/dist/src/extensions/gui/html.d.ts +10 -7
- package/dist/src/extensions/gui/html.js +53 -29
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/node/child_process/index.d.ts +4 -3
- package/dist/src/extensions/node/child_process/index.js +4 -3
- package/dist/src/extensions/node/child_process/index.js.map +1 -1
- package/dist/tests/cli/hints.test.js +9 -1
- package/dist/tests/cli/hints.test.js.map +1 -1
- package/dist/tests/cli/main.test.js +9 -0
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/compiler.test.js +107 -0
- package/dist/tests/driver/compiler.test.js.map +1 -1
- package/dist/tests/driver/cxx-compile-flags.test.d.ts +1 -0
- package/dist/tests/driver/cxx-compile-flags.test.js +24 -0
- package/dist/tests/driver/cxx-compile-flags.test.js.map +1 -0
- package/dist/tests/driver/macos-sdk.test.d.ts +1 -0
- package/dist/tests/driver/macos-sdk.test.js +83 -0
- package/dist/tests/driver/macos-sdk.test.js.map +1 -0
- package/dist/tests/driver/platform-support.test.d.ts +1 -0
- package/dist/tests/driver/platform-support.test.js +41 -0
- package/dist/tests/driver/platform-support.test.js.map +1 -0
- package/dist/tests/driver/prebuilt.test.d.ts +1 -0
- package/dist/tests/driver/prebuilt.test.js +44 -0
- package/dist/tests/driver/prebuilt.test.js.map +1 -0
- package/dist/tests/driver/toolchain-provider.test.js +24 -0
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +23 -0
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/async-await.test.js +4 -2
- package/dist/tests/e2e/async-await.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +21 -0
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/fetch.test.js +34 -6
- package/dist/tests/e2e/fetch.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-example-pelican3d.test.d.ts +10 -0
- package/dist/tests/e2e/gui-example-pelican3d.test.js +94 -0
- package/dist/tests/e2e/gui-example-pelican3d.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 +5 -0
- package/dist/tests/e2e/gui-helpers.js +4 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-layout.test.js +3 -2
- package/dist/tests/e2e/gui-layout.test.js.map +1 -1
- package/dist/tests/e2e/gui-script.test.js +48 -0
- package/dist/tests/e2e/gui-script.test.js.map +1 -1
- 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/unhandled-rejection.test.d.ts +14 -0
- package/dist/tests/e2e/unhandled-rejection.test.js +107 -0
- package/dist/tests/e2e/unhandled-rejection.test.js.map +1 -0
- package/dist/tests/extensions/gui-features.test.js +8 -1
- package/dist/tests/extensions/gui-features.test.js.map +1 -1
- package/dist/tests/extensions/gui.test.js +44 -0
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/dist/tests/scripts/fetch-tarball.test.d.ts +1 -0
- package/dist/tests/scripts/fetch-tarball.test.js +70 -0
- package/dist/tests/scripts/fetch-tarball.test.js.map +1 -0
- package/doc/ai/cli.md +6 -0
- package/doc/ai/extensions.md +10 -5
- package/doc/ai/language-support.md +1 -0
- package/doc/ai/troubleshooting.md +32 -2
- package/doc/ai/zh-CN/cli.md +5 -0
- package/doc/ai/zh-CN/extensions.md +9 -4
- package/doc/ai/zh-CN/language-support.md +1 -0
- package/doc/ai/zh-CN/troubleshooting.md +27 -2
- package/doc/gui/animation.md +39 -9
- package/doc/gui/build.md +13 -8
- package/doc/gui/dom.md +23 -5
- package/doc/gui/known-issues.md +31 -23
- package/doc/gui/layout.md +30 -7
- package/doc/gui/rendering.md +84 -26
- package/doc/gui/webgl.md +4 -0
- package/doc/gui-scripts.md +33 -17
- package/doc/gui.md +79 -15
- package/doc/implemented/async.md +1 -0
- package/doc/implemented/modules.md +9 -6
- package/doc/implemented/node/child-process.md +12 -4
- package/doc/implemented/runtime-core.md +1 -1
- package/doc/npm-package.md +23 -2
- package/doc/post/make-js-great-again.md +30 -0
- package/doc/requirements.md +31 -0
- package/doc/unimplemented/deviations.md +1 -0
- package/doc/zh-CN/gui/animation.md +33 -10
- package/doc/zh-CN/gui/build.md +9 -7
- package/doc/zh-CN/gui/dom.md +19 -4
- package/doc/zh-CN/gui/known-issues.md +21 -12
- package/doc/zh-CN/gui/layout.md +22 -5
- package/doc/zh-CN/gui/rendering.md +61 -20
- package/doc/zh-CN/gui/webgl.md +1 -0
- package/doc/zh-CN/gui-scripts.md +23 -11
- package/doc/zh-CN/gui.md +64 -16
- package/doc/zh-CN/implemented/async.md +1 -0
- package/doc/zh-CN/implemented/modules.md +8 -4
- package/doc/zh-CN/implemented/node/child-process.md +10 -3
- package/doc/zh-CN/implemented/runtime-core.md +1 -1
- package/doc/zh-CN/npm-package.md +18 -1
- package/doc/zh-CN/post/make-js-great-again.md +32 -0
- package/doc/zh-CN/requirements.md +25 -0
- package/doc/zh-CN/unimplemented/deviations.md +1 -0
- 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/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/effect_values.cpp +245 -21
- package/runtime/ext_gui/gradient_values.cpp +24 -0
- package/runtime/ext_gui/grid_values.cpp +23 -0
- package/runtime/ext_gui/gui_cursor.cpp +50 -0
- package/runtime/ext_gui/gui_events.cpp +16 -4
- package/runtime/ext_gui/gui_internal.h +3 -0
- package/runtime/ext_gui/image_api.h +3 -1
- package/runtime/ext_gui/image_feature.cpp +4 -2
- package/runtime/ext_gui/inline_svg.cpp +95 -8
- package/runtime/ext_gui/inline_svg.h +5 -1
- package/runtime/ext_gui/layout.cpp +3 -2
- package/runtime/ext_gui/layout.h +6 -0
- 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/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 +10 -1
- 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 +6 -1
- package/runtime/ext_gui/renderer_internal.h +21 -0
- package/runtime/ext_gui/renderer_layer.cpp +27 -1
- package/runtime/ext_gui/renderer_text.cpp +2 -1
- 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 +4 -2
- package/runtime/ext_gui/text.h +11 -4
- package/runtime/ext_gui/text_cache.cpp +11 -2
- 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_hb.cpp +63 -179
- package/runtime/ext_gui/text_stb.cpp +49 -193
- package/runtime/ext_gui/transform.cpp +107 -9
- package/runtime/ext_gui/transform.h +5 -0
- package/runtime/ext_gui/window.cpp +1 -1
- package/runtime/ext_node/child_process/child_process.c +149 -7
- package/runtime/rt.h +4 -0
- package/runtime/xt_async.c +4 -0
- package/runtime/xt_generator.c +2 -0
- package/runtime/xt_loop.c +8 -4
- package/runtime/xt_promise.c +103 -0
- package/scripts/build-gui.ts +17 -5
- package/scripts/build-media.ts +89 -10
- package/scripts/build-runtime.ts +7 -2
- package/scripts/fetch-tarball.ts +71 -0
- package/scripts/gui-build-cache.ts +28 -0
- package/scripts/gui-build-options.ts +26 -2
- package/scripts/package-release.ts +8 -0
- package/src/cli/hints.ts +19 -2
- package/src/cli/main.ts +11 -0
- package/src/driver/app-artifacts.ts +5 -3
- package/src/driver/compiler.ts +167 -56
- package/src/driver/icon.ts +3 -2
- package/src/driver/macos-sdk.ts +52 -0
- package/src/driver/options.ts +6 -0
- package/src/driver/platform-support.ts +69 -0
- package/src/driver/prebuilt.ts +61 -0
- package/src/driver/runtime-objects.ts +26 -5
- package/src/driver/toolchain-provider.ts +11 -0
- package/src/driver/toolchain.ts +26 -1
- package/src/driver/version.ts +1 -1
- package/src/driver/win-entry.ts +4 -3
- package/src/extensions/gui/features.ts +13 -9
- package/src/extensions/gui/html.ts +61 -31
- package/src/extensions/node/child_process/index.ts +4 -3
|
@@ -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];
|
|
@@ -387,6 +387,7 @@ void xt_gui_image_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
|
|
|
387
387
|
full.u1 = 1.0f;
|
|
388
388
|
full.v1 = 1.0f;
|
|
389
389
|
beginGradientUpload();
|
|
390
|
+
beginBitmapUpload();
|
|
390
391
|
for (const xtgui::PaintOp &op : list.ops) {
|
|
391
392
|
if (op.kind != xtgui::PaintKind::Image || op.index >= list.images.size()) continue;
|
|
392
393
|
const xtgui::PaintImage &image = list.images[op.index];
|
|
@@ -398,7 +399,9 @@ void xt_gui_image_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
|
|
|
398
399
|
if (pixel_width < 1) pixel_width = 1;
|
|
399
400
|
if (pixel_height < 1) pixel_height = 1;
|
|
400
401
|
SDL_GPUTexture *texture = nullptr;
|
|
401
|
-
if (image.
|
|
402
|
+
if (image.bitmap != nullptr) {
|
|
403
|
+
texture = ensureBitmapTexture(device, *image.bitmap, batch);
|
|
404
|
+
} else if (image.gradient != nullptr) {
|
|
402
405
|
texture = ensureGradientTexture(device, image, pixel_width, pixel_height, batch);
|
|
403
406
|
} else if (image.canvas_id != 0) {
|
|
404
407
|
texture = ensureCanvasTexture(device, image.canvas_id, batch);
|
|
@@ -416,12 +419,14 @@ void xt_gui_image_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
|
|
|
416
419
|
geometry->op_textures[op.index] = texture;
|
|
417
420
|
}
|
|
418
421
|
finishGradientUpload(device);
|
|
422
|
+
finishBitmapUpload(device);
|
|
419
423
|
uploadVertices(device, geometry, verts.data(), (uint32_t)verts.size(), sizeof(TextVertex), batch);
|
|
420
424
|
}
|
|
421
425
|
|
|
422
426
|
void xt_gui_image_textures_destroy(SDL_GPUDevice *device) {
|
|
423
427
|
using namespace xtgui::renderer_detail;
|
|
424
428
|
releaseGradientTextures(device);
|
|
429
|
+
releaseBitmapTextures(device);
|
|
425
430
|
for (auto &entry : g_image_textures) {
|
|
426
431
|
if (entry.second != nullptr) SDL_ReleaseGPUTexture(device, entry.second);
|
|
427
432
|
}
|
|
@@ -135,6 +135,16 @@ void drawRuns(SDL_GPURenderPass *pass, const std::vector<XtGuiDrawRun> &runs, si
|
|
|
135
135
|
* SPIR-V or MSL). D3D12/DXIL has no shader compiler here, so it falls back to
|
|
136
136
|
* the ordinary single-pass painter. */
|
|
137
137
|
bool layerRendererReady(SDL_GPUDevice *device);
|
|
138
|
+
/** True when the `backdrop-filter` snapshot/filter pipeline was created. */
|
|
139
|
+
bool backdropRendererReady(SDL_GPUDevice *device);
|
|
140
|
+
/** Filter `source` into the active scene pass inside the element's rounded
|
|
141
|
+
* border box. No-op when the pipeline is unavailable. */
|
|
142
|
+
void drawBackdropPass(SDL_GPUCommandBuffer *cmd, SDL_GPURenderPass *pass,
|
|
143
|
+
SDL_GPUTexture *source, const xtgui::DisplayList &list,
|
|
144
|
+
const XtGuiDrawRun &run, int target_width, int target_height,
|
|
145
|
+
float pixel_density);
|
|
146
|
+
/** Release the backdrop pipeline and sampler. */
|
|
147
|
+
void backdropRendererDestroy(SDL_GPUDevice *device);
|
|
138
148
|
/** Render `list` through the offscreen layer compositor into `swapchain`.
|
|
139
149
|
* Returns false before recording any pass when the scene textures cannot be
|
|
140
150
|
* created, so the caller can fall back to the ordinary painter. */
|
|
@@ -159,6 +169,17 @@ SDL_GPUTexture *createImageTexture(SDL_GPUDevice *device, const xtgui::Image &im
|
|
|
159
169
|
SDL_GPUTexture *ensureImageTexture(SDL_GPUDevice *device, const std::string &src, int pixel_width,
|
|
160
170
|
int pixel_height, XtGuiUploadBatch *batch);
|
|
161
171
|
|
|
172
|
+
/* -- CPU-rasterised shadows ---------------------------------------------- */
|
|
173
|
+
|
|
174
|
+
/** Upload (once) the blurred mask of a `box-shadow`/`text-shadow` layer and
|
|
175
|
+
* return its cached texture. Bracket the frame's image upload with
|
|
176
|
+
* `beginBitmapUpload`/`finishBitmapUpload` so stale masks are reclaimed. */
|
|
177
|
+
SDL_GPUTexture *ensureBitmapTexture(SDL_GPUDevice *device, const xtgui::PaintBitmap &bitmap,
|
|
178
|
+
XtGuiUploadBatch *batch);
|
|
179
|
+
void beginBitmapUpload();
|
|
180
|
+
void finishBitmapUpload(SDL_GPUDevice *device);
|
|
181
|
+
void releaseBitmapTextures(SDL_GPUDevice *device);
|
|
182
|
+
|
|
162
183
|
/* -- background gradients ------------------------------------------------- */
|
|
163
184
|
|
|
164
185
|
/** Rasterise a `PaintImage` radial gradient into an RGBA texture and cache it by
|