Skip to content

Commit 154befd

Browse files
committed
Adopt CSS-style drop shadow mask
This commit generates shadow_gaussian lookup table during the build, so the renderer stops depending on runtime math. It replaces the stack-blur border writes with a lookup-table renderer that matches CSS behavior and stays efficient. For performance considerations, it caches the vertical and bottom falloff tables and rely on memset() or pointer writes to trim per-frame work. It exposes a small configurable fade tail and update defaults plus the API, so it aligns with CSS box-shadow semantics.
1 parent c9f462c commit 154befd

File tree

7 files changed

+412
-55
lines changed

7 files changed

+412
-55
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mado-perf
99
font-edit
1010
.font-edit
1111
src/composite-decls.h
12+
src/shadow-gaussian-lut.h
1213

1314
# Swap
1415
[._]*.s[a-v][a-z]

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ src/composite-decls.h: scripts/gen-composite-decls.py
8383
@echo " GEN $@"
8484
@$< > $@
8585

86+
src/shadow-gaussian-lut.h: scripts/gen-shadow-lut.py
87+
@echo " GEN $@"
88+
@./scripts/gen-shadow-lut.py $@
89+
8690
# Optional features
8791

8892
libtwin.a_files-$(CONFIG_LOGGING) += src/log.c
@@ -277,6 +281,9 @@ ifeq ($(filter config defconfig clean,$(MAKECMDGOALS)),)
277281
ifeq ($(wildcard src/composite-decls.h),)
278282
$(shell scripts/gen-composite-decls.py > src/composite-decls.h)
279283
endif
284+
ifeq ($(wildcard src/shadow-gaussian-lut.h),)
285+
$(shell ./scripts/gen-shadow-lut.py src/shadow-gaussian-lut.h)
286+
endif
280287
endif
281288

282289
# Only skip build rules when running ONLY config/defconfig (no other targets)

configs/Kconfig

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,32 +191,45 @@ config DROP_SHADOW
191191

192192
config HORIZONTAL_OFFSET
193193
int "Horizontal offset"
194-
default 1
194+
default 6
195195
range 1 10
196196
depends on DROP_SHADOW
197197
help
198198
Horizontal shadow offset in pixels.
199+
Default matches a CSS-style soft shadow (≈6 px offset).
199200
Larger values push shadow further right.
200201

201202
config VERTICAL_OFFSET
202203
int "Vertical offset"
203-
default 1
204+
default 6
204205
range 1 10
205206
depends on DROP_SHADOW
206207
help
207208
Vertical shadow offset in pixels.
209+
Default matches a CSS-style soft shadow (≈6 px offset).
208210
Larger values push shadow further down.
209211

210212
config SHADOW_BLUR
211213
int "Shadow blur radius"
212-
default 10
214+
default 12
213215
range 1 15
214216
depends on DROP_SHADOW
215217
help
216218
Shadow blur kernel size (radius).
219+
Default 12 approximates common CSS box-shadow softness.
217220
Larger values create softer, more diffuse shadows.
218221
Higher values increase rendering cost.
219222

223+
config SHADOW_FADE_TAIL
224+
int "Shadow fade tail"
225+
default 2
226+
range 0 3
227+
depends on DROP_SHADOW
228+
help
229+
Additional zero-alpha pixels appended to the shadow mask.
230+
Default adds ~2 px of clear space to match CSS feathering.
231+
Higher values slightly enlarge the off-screen buffer usage.
232+
220233
endmenu
221234

222235
menu "Image Loaders"

scripts/gen-shadow-lut.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Generate precomputed Gaussian-style weights for the drop shadow LUT.
4+
5+
The curve approximates (1 - t^2)^2 * 0.92 + 0.08 sampled at 17 points
6+
between 0 and 1 (inclusive). Values are emitted in 16.16 fixed-point so
7+
the renderer can remain purely fixed-point.
8+
"""
9+
10+
import math
11+
import pathlib
12+
import sys
13+
14+
15+
def compute_weights(samples: int = 16) -> list[int]:
16+
weights = []
17+
for idx in range(samples + 1):
18+
t = idx / samples
19+
value = ((1.0 - t * t) ** 2) * 0.92 + 0.08
20+
fixed = int(round(value * 65536)) # 16.16 fixed-point
21+
fixed = max(0, min(fixed, 0xFFFFFFFF))
22+
weights.append(fixed)
23+
return weights
24+
25+
26+
HEADER_TEMPLATE = """\
27+
/*
28+
* This file is auto-generated by scripts/gen-shadow-lut.py.
29+
* Do not edit manually.
30+
*/
31+
#ifndef SHADOW_GAUSSIAN_LUT_H
32+
#define SHADOW_GAUSSIAN_LUT_H
33+
34+
#include "twin.h"
35+
36+
static const twin_fixed_t shadow_gaussian_lut[{count}] = {{
37+
{body}
38+
}};
39+
40+
#endif /* SHADOW_GAUSSIAN_LUT_H */
41+
"""
42+
43+
44+
def emit_header(path: pathlib.Path) -> None:
45+
weights = compute_weights()
46+
lines = []
47+
for idx, value in enumerate(weights):
48+
suffix = "," if idx < len(weights) - 1 else ""
49+
lines.append(f" 0x{value:05x}{suffix}")
50+
header = HEADER_TEMPLATE.format(count=len(weights), body="\n".join(lines))
51+
path.write_text(header, encoding="utf-8")
52+
53+
54+
def main(argv: list[str]) -> int:
55+
if len(argv) != 2:
56+
sys.stderr.write("Usage: gen-shadow-lut.py <output>\n")
57+
return 1
58+
output_path = pathlib.Path(argv[1])
59+
emit_header(output_path)
60+
return 0
61+
62+
63+
if __name__ == "__main__":
64+
raise SystemExit(main(sys.argv))

0 commit comments

Comments
 (0)