Skip to content

Commit

Permalink
Implement the drop shadow
Browse files Browse the repository at this point in the history
Add the `twin_stack_blur()` function to implement Mario's Stack Blur
algorithm, which blurs the target pixel map. Additionally, create a test
window to evaluate the effect of `twin_stack_blur()`. Implement a shadow
pixel map beneath the test window with a shadow effect, enabling the
shadow pixel map to blur the background and create a frosted glass
effect. The shadow effect of the test window only becomes visible when
the test window is on the top layer.

Ref: https://melatonin.dev/blog/implementing-marios-stack-blur-15-times-in-cpp/
See #34

Signed-off-by: Wei-Hsin Yeh <[email protected]>
  • Loading branch information
weihsinyeh committed Jan 3, 2025
1 parent a2becfd commit e403354
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 16 deletions.
56 changes: 49 additions & 7 deletions apps/multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
#include "apps_multi.h"

#define D(x) twin_double_to_fixed(x)
#define ASSET_PATH "assets/"

static void apps_line_start(twin_screen_t *screen, int x, int y, int w, int h)
{
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
twin_pixmap_t *pixmap = window->pixmap;
twin_path_t *stroke = twin_path_create();
twin_fixed_t fy;
Expand All @@ -38,7 +39,7 @@ static void apps_circletext_start(twin_screen_t *screen,
int h)
{
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
int wid = window->client.right - window->client.left;
int hei = window->client.bottom - window->client.top;
twin_pixmap_t *pixmap = window->pixmap;
Expand Down Expand Up @@ -83,7 +84,7 @@ static void apps_quickbrown_start(twin_screen_t *screen,
int h)
{
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
int wid = window->client.right - window->client.left;
int hei = window->client.bottom - window->client.top;
twin_pixmap_t *pixmap = window->pixmap;
Expand Down Expand Up @@ -126,7 +127,7 @@ static void apps_quickbrown_start(twin_screen_t *screen,
static void apps_ascii_start(twin_screen_t *screen, int x, int y, int w, int h)
{
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
int wid = window->client.right - window->client.left;
int hei = window->client.bottom - window->client.top;
twin_pixmap_t *pixmap = window->pixmap;
Expand Down Expand Up @@ -174,7 +175,7 @@ static void apps_ascii_start(twin_screen_t *screen, int x, int y, int w, int h)
static void apps_jelly_start(twin_screen_t *screen, int x, int y, int w, int h)
{
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
int wid = window->client.right - window->client.left;
int hei = window->client.bottom - window->client.top;
twin_pixmap_t *pixmap = window->pixmap;
Expand Down Expand Up @@ -250,7 +251,7 @@ static void draw_flower(twin_path_t *path,
static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h)
{
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
twin_pixmap_t *pixmap = window->pixmap;
twin_path_t *stroke = twin_path_create();
twin_path_t *path = twin_path_create();
Expand All @@ -272,6 +273,47 @@ static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h)
twin_window_show(window);
}

static void apps_test(twin_screen_t *screen, int x, int y, int w, int h)
{
twin_pixmap_t *raw_background =
twin_pixmap_from_file(ASSET_PATH "tux.png", TWIN_ARGB32);
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, true);
twin_window_set_name(window, "Test");
twin_pixmap_t *scaled_background = twin_pixmap_create(
TWIN_ARGB32, window->pixmap->width, window->pixmap->height);
twin_fixed_t sx, sy;
sx = twin_fixed_div(
twin_int_to_fixed(raw_background->width),
twin_int_to_fixed(window->client.right - window->client.left));
sy = twin_fixed_div(
twin_int_to_fixed(raw_background->height),
twin_int_to_fixed(window->client.bottom - window->client.top));

twin_matrix_scale(&raw_background->transform, sx, sy);
twin_operand_t srcop = {
.source_kind = TWIN_PIXMAP,
.u.pixmap = raw_background,
};

twin_composite(scaled_background, 0, 0, &srcop, 0, 0, 0, 0, 0, TWIN_SOURCE,
screen->width, screen->height);

twin_pointer_t src, dst;
for (int y = window->client.top; y < window->client.bottom; y++)
for (int x = window->client.left; x < window->client.right; x++) {
src =
twin_pixmap_pointer(scaled_background, x - window->client.left,
y - window->client.top);
dst = twin_pixmap_pointer(window->pixmap, x, y);
*dst.argb32 = *src.argb32 | 0xff000000;
}

twin_pixmap_destroy(scaled_background);
twin_pixmap_destroy(raw_background);
twin_window_show(window);
}

void apps_multi_start(twin_screen_t *screen,
const char *name,
int x,
Expand All @@ -285,5 +327,5 @@ void apps_multi_start(twin_screen_t *screen,
apps_quickbrown_start(screen, x += 20, y += 20, w, h);
apps_ascii_start(screen, x += 20, y += 20, w, h);
apps_jelly_start(screen, x += 20, y += 20, w / 2, h);
apps_flower_start(screen, x += 20, y += 20, w, h);
apps_test(screen, x += 20, y += 20, w, h);
}
15 changes: 14 additions & 1 deletion include/twin.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ typedef struct _twin_pixmap {
* Pixels
*/
twin_animation_t *animation;
bool shadow;
twin_pointer_t p;
/*
* When representing a window, this point
Expand Down Expand Up @@ -422,6 +423,10 @@ typedef void (*twin_destroy_func_t)(twin_window_t *window);
struct _twin_window {
twin_screen_t *screen;
twin_pixmap_t *pixmap;
bool shadow;
twin_pixmap_t *shadow_pixmap;
twin_coord_t shadow_offset_x;
twin_coord_t shadow_offset_y;
twin_window_style_t style;
twin_rect_t client;
twin_rect_t damage;
Expand Down Expand Up @@ -648,6 +653,13 @@ void twin_fill(twin_pixmap_t *dst,

void twin_premultiply_alpha(twin_pixmap_t *px);

void twin_stack_blur(twin_pixmap_t *px,
int radius,
twin_coord_t left,
twin_coord_t right,
twin_coord_t top,
twin_coord_t bottom);

/*
* event.c
*/
Expand Down Expand Up @@ -1142,7 +1154,8 @@ twin_window_t *twin_window_create(twin_screen_t *screen,
twin_coord_t x,
twin_coord_t y,
twin_coord_t width,
twin_coord_t height);
twin_coord_t height,
bool shadow);

void twin_window_destroy(twin_window_t *window);

Expand Down
147 changes: 147 additions & 0 deletions src/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,153 @@ static const twin_src_msk_op comp3[2][4][4][3] = {
#define operand_index(o) \
((o)->source_kind == TWIN_SOLID ? 3 : o->u.pixmap->format)

#define _twin_add_ARGB(s, d, i, t) (((t) = (s) + twin_get_8(d, i)))
#define _twin_add(s, d, t) (((t) = (s) + (d)))
#define _twin_div(d, den, i, t) \
(((t) = (d) / (den)), (t) = twin_get_8((t), 0), \
(twin_argb32_t) twin_sat(t) << (i))
#define _twin_sub_ARGB(s, d, i, t) (((t) = (s) - twin_get_8(d, i)))
#define _twin_sub(s, d, t) (((t) = (s) - (d)))
#define twin_put_8(d, i, t) (((t) = (d) << (i)))

#define min(x, y) \
({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; \
})
#define max(x, y) \
({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x > _y ? _x : _y; \
})

void twin_stack(twin_pixmap_t *trg_px,
twin_pixmap_t *src_px,
int radius,
int first_str,
int first_end,
int second_str,
int second_end,
bool horiz_span)
{
int den = (radius + 1) * (radius + 1);
twin_pointer_t src_ptr, trg_ptr, old_ptr, new_ptr;
twin_argb32_t sumInR, sumOutR, sumR, sumInG, sumOutG, sumG, sumInB, sumOutB,
sumB, _cur, _old, _new, _src;
uint16_t t1, t2, t3;
for (int first = first_str; first < first_end; first++) {
sumInR = sumOutR = sumR = sumInG = sumOutG = sumG = sumInB = sumOutB =
sumB = 0x00000000;

/* Initialize SumOut by padding */
if (horiz_span)
src_ptr = twin_pixmap_pointer(src_px, second_str, first);
else
src_ptr = twin_pixmap_pointer(src_px, first, second_str);
_src = *src_ptr.argb32;

for (int i = second_str; i < second_str + radius; i++) {
sumOutR = _twin_add_ARGB(sumOutR, _src, 0, t1);
sumOutG = _twin_add_ARGB(sumOutG, _src, 8, t2);
sumOutB = _twin_add_ARGB(sumOutB, _src, 16, t3);
for (int j = 0; j < (i - second_str) + 1; j++) {
sumR = _twin_add_ARGB(sumR, _src, 0, t1);
sumG = _twin_add_ARGB(sumG, _src, 8, t2);
sumB = _twin_add_ARGB(sumB, _src, 16, t3);
}
}

/* Initialize SumIn */
for (int i = second_str; i < second_str + radius; i++) {
if (horiz_span)
src_ptr = twin_pixmap_pointer(src_px, i, first);
else
src_ptr = twin_pixmap_pointer(src_px, first, i);
_src = *src_ptr.argb32;
sumInR = _twin_add_ARGB(sumInR, _src, 0, t1);
sumInG = _twin_add_ARGB(sumInG, _src, 8, t2);
sumInB = _twin_add_ARGB(sumInB, _src, 16, t3);
for (int j = 0; j < radius - (i - second_str); j++) {
sumR = _twin_add_ARGB(sumR, _src, 0, t1);
sumG = _twin_add_ARGB(sumG, _src, 8, t2);
sumB = _twin_add_ARGB(sumB, _src, 16, t3);
}
}

for (int cur = second_str; cur < second_end; cur++) {
if (horiz_span) {
src_ptr = twin_pixmap_pointer(src_px, cur, first);
trg_ptr = twin_pixmap_pointer(trg_px, cur, first);
old_ptr = twin_pixmap_pointer(
src_px, max(cur - radius, second_str), first);
new_ptr = twin_pixmap_pointer(
src_px, min(cur + radius, second_end - 1), first);
} else {
src_ptr = twin_pixmap_pointer(src_px, first, cur);
trg_ptr = twin_pixmap_pointer(trg_px, first, cur);
old_ptr = twin_pixmap_pointer(src_px, first,
max(cur - radius, second_str));
new_ptr = twin_pixmap_pointer(
src_px, first, min(cur + radius, second_end - 1));
}
_cur = *src_ptr.argb32;
_old = *old_ptr.argb32;
_new = *new_ptr.argb32;
/* STEP 1 : sum_out + current */
sumOutR = _twin_add_ARGB(sumOutR, _cur, 0, t1);
sumOutG = _twin_add_ARGB(sumOutG, _cur, 8, t2);
sumOutB = _twin_add_ARGB(sumOutB, _cur, 16, t3);
/* STEP 2 : sum_in + new */
sumInR = _twin_add_ARGB(sumInR, _new, 0, t1);
sumInG = _twin_add_ARGB(sumInG, _new, 8, t2);
sumInB = _twin_add_ARGB(sumInB, _new, 16, t3);
/* STEP 3 : sum + sum_in */
sumR = _twin_add(sumR, sumInR, t1);
sumG = _twin_add(sumG, sumInG, t2);
sumB = _twin_add(sumB, sumInB, t3);
/* STEP 4 : sum / denominator */
*trg_ptr.argb32 =
(_twin_div(sumR, den, 0, t1) | _twin_div(sumG, den, 8, t2) |
_twin_div(sumB, den, 16, t3) | (*src_ptr.argb32 & 0xff000000));
/* STEP 5 : sum - sum_out */
sumR = _twin_sub(sumR, sumOutR, t1);
sumG = _twin_sub(sumG, sumOutG, t2);
sumB = _twin_sub(sumB, sumOutB, t3);
/* STEP 6 : sum_out - old */
sumOutR = _twin_sub_ARGB(sumOutR, _old, 0, t1);
sumOutG = _twin_sub_ARGB(sumOutG, _old, 8, t2);
sumOutB = _twin_sub_ARGB(sumOutB, _old, 16, t3);
/* STEP 7 : sum_in - current */
sumInR = _twin_sub_ARGB(sumInR, _cur, 0, t1);
sumInG = _twin_sub_ARGB(sumInG, _cur, 8, t2);
sumInB = _twin_sub_ARGB(sumInB, _cur, 16, t3);
}
}
}

void twin_stack_blur(twin_pixmap_t *px,
int radius,
twin_coord_t left,
twin_coord_t right,
twin_coord_t top,
twin_coord_t bottom)
{
if (px->format != TWIN_ARGB32)
return;
twin_pixmap_t *tmp_px =
twin_pixmap_create(px->format, px->width, px->height);
memcpy(tmp_px->p.v, px->p.v,
px->width * px->height * twin_bytes_per_pixel(px->format));
twin_stack(tmp_px, px, radius, top, bottom, left, right, true);
twin_stack(px, tmp_px, radius, left, right, top, bottom, false);
twin_pixmap_destroy(tmp_px);
return;
}

/* FIXME: source clipping is busted */
static void _twin_composite_simple(twin_pixmap_t *dst,
twin_coord_t dst_x,
Expand Down
1 change: 1 addition & 0 deletions src/pixmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ twin_pixmap_t *twin_pixmap_create(twin_format_t format,
pixmap->stride = stride;
pixmap->disable = 0;
pixmap->animation = NULL;
pixmap->shadow = false;
pixmap->p.v = pixmap + 1;
memset(pixmap->p.v, '\0', space);
return pixmap;
Expand Down
Loading

0 comments on commit e403354

Please sign in to comment.