Skip to content

Conversation

@jserv
Copy link
Contributor

@jserv jserv commented Oct 26, 2025

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.


Summary by cubic

Switches drop shadow rendering to a CSS-style box-shadow mask using a precomputed Gaussian LUT. Improves visual match and speeds up rendering while simplifying the shadow API.

  • New Features

    • Generate a fixed-point Gaussian weight LUT at build time (scripts/gen-shadow-lut.py → src/shadow-gaussian-lut.h), removing runtime math.
    • Replace stack-blur edge writes with a LUT-based renderer for right, bottom, and corner falloffs to match CSS behavior.
    • Cache vertical and bottom falloff tables and use fast clears to cut per-frame work.
    • Add SHADOW_FADE_TAIL (default 2) and update defaults: HORIZONTAL_OFFSET 6, VERTICAL_OFFSET 6, SHADOW_BLUR 12; set default shadow color to 50% black (0x80000000).
  • Migration

    • Update calls to twin_shadow_border(shadow, color); remove shift_x and shift_y as offsets now come from Kconfig.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 7 files


/* Clear stale shadow pixels before repainting the mask. */
if (shadow->window->shadow_x > 0) {
for (twin_coord_t y = 0; y < shadow->height; y++) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line can be changed to for (twin_coord_t y = 0; y < win_height; y++) {
This is because the next part, if (shadow->window->shadow_y > 0), handles the area between win_height and shadow->height.

twin_pointer_t clear = twin_pixmap_pointer(shadow, 0, y);
if (is_argb32) {
memset(clear.argb32, 0,
(size_t) win_width * sizeof(*clear.argb32));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(size_t) shadow->width * sizeof(*clear.argb32));
This change allows it to handle the bottom-right corner. However, I don’t observe any difference after applying it.

const twin_coord_t fade_tail = CONFIG_SHADOW_FADE_TAIL;
const twin_coord_t lut_x_len = x_offset + fade_tail;
const twin_coord_t lut_y_len = y_offset + fade_tail;
twin_coord_t right_extent = shadow->width - win_width;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twin_coord_t win_width = shadow->width - shadow->window->shadow_x;
Therefore, right_extent is equal to shadow->window->shadow_x.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twin_coord_t right_extent = shadow->window->shadow_x;

const twin_coord_t lut_x_len = x_offset + fade_tail;
const twin_coord_t lut_y_len = y_offset + fade_tail;
twin_coord_t right_extent = shadow->width - win_width;
twin_coord_t bottom_extent = shadow->height - win_height;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twin_coord_t win_height = shadow->height - shadow->window->shadow_y;
Therefore, bottom_extent is equal to shadow->window->shadow_y.

#define SHADOW_LUT_Y_LEN (CONFIG_VERTICAL_OFFSET + CONFIG_SHADOW_FADE_TAIL)

/* Fast lookup: 17-entry approximation of (1 - t^2)^2 * 0.92 + 0.08. */
static inline twin_fixed_t shadow_gaussian_weight(twin_fixed_t t)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that the shadow_gaussian_weight() function has the same effect as twin_stack_blur(). Removing the call to twin_stack_blur() still produces a gradient color effect.


if (alpha_bottom) {
for (twin_coord_t x = 0; x < bottom_width; x++) {
twin_a8_t alpha = (alpha_y * alpha_bottom[x]) >> 8;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be twin_a8_t alpha = (alpha_y * alpha_bottom[0]) >> 8;.
image

I’m not sure why it’s written as twin_a8_t alpha = (alpha_y * alpha_bottom[x]) >> 8;. Was this done on purpose?
image

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.
const twin_coord_t fade_tail = CONFIG_SHADOW_FADE_TAIL;
const twin_coord_t lut_x_len = x_offset + fade_tail;
const twin_coord_t lut_y_len = y_offset + fade_tail;
twin_coord_t right_extent = shadow->width - win_width;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twin_coord_t right_extent = shadow->window->shadow_x;

twin_coord_t bottom_start = win_height;
if (bottom_start < 0)
bottom_start = 0;
twin_coord_t bottom_end = win_height + bottom_extent;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twin_coord_t win_height = shadow->height - shadow->window->shadow_y;
twin_coord_t bottom_extent = shadow->height - win_height;
Therefore, bottom_end == shadow->height

if (bottom_start < 0)
bottom_start = 0;
twin_coord_t bottom_end = win_height + bottom_extent;
if (bottom_end > shadow->height)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to compare.

if (bottom_width <= 0)
continue;

twin_coord_t dist_y = y - win_height;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twin_coord_t dist_y = y - bottom_start; this is clearer.

@weihsinyeh
Copy link
Collaborator

Only need to keep one variable for the same function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants