Skip to content

Commit 397f8e9

Browse files
committed
Implement the drop shadow
Add the `twin_stack_blur()` function to implement Mario's Stack Blur algorithm, which blurs the target pixel map. Create a test window to evaluate the effect of `twin_stack_blur()`. Additionally, 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]>
1 parent a2becfd commit 397f8e9

File tree

7 files changed

+351
-16
lines changed

7 files changed

+351
-16
lines changed

apps/multi.c

+52-7
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
#include "apps_multi.h"
88

99
#define D(x) twin_double_to_fixed(x)
10+
#define ASSET_PATH "assets/"
1011

1112
static void apps_line_start(twin_screen_t *screen, int x, int y, int w, int h)
1213
{
1314
twin_window_t *window = twin_window_create(
14-
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
15+
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
1516
twin_pixmap_t *pixmap = window->pixmap;
1617
twin_path_t *stroke = twin_path_create();
1718
twin_fixed_t fy;
@@ -38,7 +39,7 @@ static void apps_circletext_start(twin_screen_t *screen,
3839
int h)
3940
{
4041
twin_window_t *window = twin_window_create(
41-
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
42+
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
4243
int wid = window->client.right - window->client.left;
4344
int hei = window->client.bottom - window->client.top;
4445
twin_pixmap_t *pixmap = window->pixmap;
@@ -83,7 +84,7 @@ static void apps_quickbrown_start(twin_screen_t *screen,
8384
int h)
8485
{
8586
twin_window_t *window = twin_window_create(
86-
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
87+
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
8788
int wid = window->client.right - window->client.left;
8889
int hei = window->client.bottom - window->client.top;
8990
twin_pixmap_t *pixmap = window->pixmap;
@@ -126,7 +127,7 @@ static void apps_quickbrown_start(twin_screen_t *screen,
126127
static void apps_ascii_start(twin_screen_t *screen, int x, int y, int w, int h)
127128
{
128129
twin_window_t *window = twin_window_create(
129-
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
130+
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
130131
int wid = window->client.right - window->client.left;
131132
int hei = window->client.bottom - window->client.top;
132133
twin_pixmap_t *pixmap = window->pixmap;
@@ -174,7 +175,7 @@ static void apps_ascii_start(twin_screen_t *screen, int x, int y, int w, int h)
174175
static void apps_jelly_start(twin_screen_t *screen, int x, int y, int w, int h)
175176
{
176177
twin_window_t *window = twin_window_create(
177-
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
178+
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
178179
int wid = window->client.right - window->client.left;
179180
int hei = window->client.bottom - window->client.top;
180181
twin_pixmap_t *pixmap = window->pixmap;
@@ -250,7 +251,7 @@ static void draw_flower(twin_path_t *path,
250251
static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h)
251252
{
252253
twin_window_t *window = twin_window_create(
253-
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
254+
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, false);
254255
twin_pixmap_t *pixmap = window->pixmap;
255256
twin_path_t *stroke = twin_path_create();
256257
twin_path_t *path = twin_path_create();
@@ -272,6 +273,50 @@ static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h)
272273
twin_window_show(window);
273274
}
274275

276+
static void apps_test(twin_screen_t *screen, int x, int y, int w, int h)
277+
{
278+
twin_pixmap_t *raw_background =
279+
twin_pixmap_from_file(ASSET_PATH "tux.png", TWIN_ARGB32);
280+
twin_window_t *window = twin_window_create(
281+
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, true);
282+
twin_window_set_name(window, "Test");
283+
twin_pixmap_t *scaled_background = twin_pixmap_create(
284+
TWIN_ARGB32, window->pixmap->width, window->pixmap->height);
285+
twin_fixed_t sx, sy;
286+
sx = twin_fixed_div(
287+
twin_int_to_fixed(raw_background->width),
288+
twin_int_to_fixed(window->client.right - window->client.left));
289+
sy = twin_fixed_div(
290+
twin_int_to_fixed(raw_background->height),
291+
twin_int_to_fixed(window->client.bottom - window->client.top));
292+
293+
twin_matrix_scale(&raw_background->transform, sx, sy);
294+
twin_operand_t srcop = {
295+
.source_kind = TWIN_PIXMAP,
296+
.u.pixmap = raw_background,
297+
};
298+
299+
twin_composite(scaled_background, 0, 0, &srcop, 0, 0, 0, 0, 0, TWIN_SOURCE,
300+
screen->width, screen->height);
301+
302+
twin_pointer_t src, dst;
303+
for (int y = window->client.top; y < window->client.bottom; y++)
304+
for (int x = window->client.left; x < window->client.right; x++) {
305+
src =
306+
twin_pixmap_pointer(scaled_background, x - window->client.left,
307+
y - window->client.top);
308+
dst = twin_pixmap_pointer(window->pixmap, x, y);
309+
*dst.argb32 = *src.argb32 | 0xff000000;
310+
}
311+
312+
twin_stack_blur(window->pixmap, 5, window->client.left,
313+
window->client.right, window->client.top,
314+
window->client.bottom);
315+
twin_pixmap_destroy(scaled_background);
316+
twin_pixmap_destroy(raw_background);
317+
twin_window_show(window);
318+
}
319+
275320
void apps_multi_start(twin_screen_t *screen,
276321
const char *name,
277322
int x,
@@ -285,5 +330,5 @@ void apps_multi_start(twin_screen_t *screen,
285330
apps_quickbrown_start(screen, x += 20, y += 20, w, h);
286331
apps_ascii_start(screen, x += 20, y += 20, w, h);
287332
apps_jelly_start(screen, x += 20, y += 20, w / 2, h);
288-
apps_flower_start(screen, x += 20, y += 20, w, h);
333+
apps_test(screen, x += 20, y += 20, w, h);
289334
}

include/twin.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ typedef struct _twin_pixmap {
194194
* Pixels
195195
*/
196196
twin_animation_t *animation;
197+
bool shadow;
197198
twin_pointer_t p;
198199
/*
199200
* When representing a window, this point
@@ -422,6 +423,10 @@ typedef void (*twin_destroy_func_t)(twin_window_t *window);
422423
struct _twin_window {
423424
twin_screen_t *screen;
424425
twin_pixmap_t *pixmap;
426+
bool shadow;
427+
twin_pixmap_t *shadow_pixmap;
428+
twin_coord_t shadow_offset_x;
429+
twin_coord_t shadow_offset_y;
425430
twin_window_style_t style;
426431
twin_rect_t client;
427432
twin_rect_t damage;
@@ -648,6 +653,13 @@ void twin_fill(twin_pixmap_t *dst,
648653

649654
void twin_premultiply_alpha(twin_pixmap_t *px);
650655

656+
void twin_stack_blur(twin_pixmap_t *px,
657+
int radius,
658+
twin_coord_t left,
659+
twin_coord_t right,
660+
twin_coord_t top,
661+
twin_coord_t bottom);
662+
651663
/*
652664
* event.c
653665
*/
@@ -1142,7 +1154,8 @@ twin_window_t *twin_window_create(twin_screen_t *screen,
11421154
twin_coord_t x,
11431155
twin_coord_t y,
11441156
twin_coord_t width,
1145-
twin_coord_t height);
1157+
twin_coord_t height,
1158+
bool shadow);
11461159

11471160
void twin_window_destroy(twin_window_t *window);
11481161

src/draw.c

+147
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,153 @@ static const twin_src_msk_op comp3[2][4][4][3] = {
302302
#define operand_index(o) \
303303
((o)->source_kind == TWIN_SOLID ? 3 : o->u.pixmap->format)
304304

305+
#define _twin_add_ARGB(s, d, i, t) (((t) = (s) + twin_get_8(d, i)))
306+
#define _twin_add(s, d, t) (((t) = (s) + (d)))
307+
#define _twin_div(d, den, i, t) \
308+
(((t) = (d) / (den)), (t) = twin_get_8((t), 0), \
309+
(twin_argb32_t) twin_sat(t) << (i))
310+
#define _twin_sub_ARGB(s, d, i, t) (((t) = (s) - twin_get_8(d, i)))
311+
#define _twin_sub(s, d, t) (((t) = (s) - (d)))
312+
#define twin_put_8(d, i, t) (((t) = (d) << (i)))
313+
314+
#define min(x, y) \
315+
({ \
316+
typeof(x) _x = (x); \
317+
typeof(y) _y = (y); \
318+
(void) (&_x == &_y); \
319+
_x < _y ? _x : _y; \
320+
})
321+
#define max(x, y) \
322+
({ \
323+
typeof(x) _x = (x); \
324+
typeof(y) _y = (y); \
325+
(void) (&_x == &_y); \
326+
_x > _y ? _x : _y; \
327+
})
328+
329+
void twin_stack(twin_pixmap_t *trg_px,
330+
twin_pixmap_t *src_px,
331+
int radius,
332+
int first_str,
333+
int first_end,
334+
int second_str,
335+
int second_end,
336+
bool horiz_span)
337+
{
338+
int den = (radius + 1) * (radius + 1);
339+
twin_pointer_t src_ptr, trg_ptr, old_ptr, new_ptr;
340+
twin_argb32_t sumInR, sumOutR, sumR, sumInG, sumOutG, sumG, sumInB, sumOutB,
341+
sumB, _cur, _old, _new, _src;
342+
uint16_t t1, t2, t3;
343+
for (int first = first_str; first < first_end; first++) {
344+
sumInR = sumOutR = sumR = sumInG = sumOutG = sumG = sumInB = sumOutB =
345+
sumB = 0x00000000;
346+
347+
/* Initialize SumOut by padding */
348+
if (horiz_span)
349+
src_ptr = twin_pixmap_pointer(src_px, second_str, first);
350+
else
351+
src_ptr = twin_pixmap_pointer(src_px, first, second_str);
352+
_src = *src_ptr.argb32;
353+
354+
for (int i = second_str; i < second_str + radius; i++) {
355+
sumOutR = _twin_add_ARGB(sumOutR, _src, 0, t1);
356+
sumOutG = _twin_add_ARGB(sumOutG, _src, 8, t2);
357+
sumOutB = _twin_add_ARGB(sumOutB, _src, 16, t3);
358+
for (int j = 0; j < (i - second_str) + 1; j++) {
359+
sumR = _twin_add_ARGB(sumR, _src, 0, t1);
360+
sumG = _twin_add_ARGB(sumG, _src, 8, t2);
361+
sumB = _twin_add_ARGB(sumB, _src, 16, t3);
362+
}
363+
}
364+
365+
/* Initialize SumIn */
366+
for (int i = second_str; i < second_str + radius; i++) {
367+
if (horiz_span)
368+
src_ptr = twin_pixmap_pointer(src_px, i, first);
369+
else
370+
src_ptr = twin_pixmap_pointer(src_px, first, i);
371+
_src = *src_ptr.argb32;
372+
sumInR = _twin_add_ARGB(sumInR, _src, 0, t1);
373+
sumInG = _twin_add_ARGB(sumInG, _src, 8, t2);
374+
sumInB = _twin_add_ARGB(sumInB, _src, 16, t3);
375+
for (int j = 0; j < radius - (i - second_str); j++) {
376+
sumR = _twin_add_ARGB(sumR, _src, 0, t1);
377+
sumG = _twin_add_ARGB(sumG, _src, 8, t2);
378+
sumB = _twin_add_ARGB(sumB, _src, 16, t3);
379+
}
380+
}
381+
382+
for (int cur = second_str; cur < second_end; cur++) {
383+
if (horiz_span) {
384+
src_ptr = twin_pixmap_pointer(src_px, cur, first);
385+
trg_ptr = twin_pixmap_pointer(trg_px, cur, first);
386+
old_ptr = twin_pixmap_pointer(
387+
src_px, max(cur - radius, second_str), first);
388+
new_ptr = twin_pixmap_pointer(
389+
src_px, min(cur + radius, second_end - 1), first);
390+
} else {
391+
src_ptr = twin_pixmap_pointer(src_px, first, cur);
392+
trg_ptr = twin_pixmap_pointer(trg_px, first, cur);
393+
old_ptr = twin_pixmap_pointer(src_px, first,
394+
max(cur - radius, second_str));
395+
new_ptr = twin_pixmap_pointer(
396+
src_px, first, min(cur + radius, second_end - 1));
397+
}
398+
_cur = *src_ptr.argb32;
399+
_old = *old_ptr.argb32;
400+
_new = *new_ptr.argb32;
401+
/* STEP 1 : sum_out + current */
402+
sumOutR = _twin_add_ARGB(sumOutR, _cur, 0, t1);
403+
sumOutG = _twin_add_ARGB(sumOutG, _cur, 8, t2);
404+
sumOutB = _twin_add_ARGB(sumOutB, _cur, 16, t3);
405+
/* STEP 2 : sum_in + new */
406+
sumInR = _twin_add_ARGB(sumInR, _new, 0, t1);
407+
sumInG = _twin_add_ARGB(sumInG, _new, 8, t2);
408+
sumInB = _twin_add_ARGB(sumInB, _new, 16, t3);
409+
/* STEP 3 : sum + sum_in */
410+
sumR = _twin_add(sumR, sumInR, t1);
411+
sumG = _twin_add(sumG, sumInG, t2);
412+
sumB = _twin_add(sumB, sumInB, t3);
413+
/* STEP 4 : sum / denominator */
414+
*trg_ptr.argb32 =
415+
(_twin_div(sumR, den, 0, t1) | _twin_div(sumG, den, 8, t2) |
416+
_twin_div(sumB, den, 16, t3) | (*src_ptr.argb32 & 0xff000000));
417+
/* STEP 5 : sum - sum_out */
418+
sumR = _twin_sub(sumR, sumOutR, t1);
419+
sumG = _twin_sub(sumG, sumOutG, t2);
420+
sumB = _twin_sub(sumB, sumOutB, t3);
421+
/* STEP 6 : sum_out - old */
422+
sumOutR = _twin_sub_ARGB(sumOutR, _old, 0, t1);
423+
sumOutG = _twin_sub_ARGB(sumOutG, _old, 8, t2);
424+
sumOutB = _twin_sub_ARGB(sumOutB, _old, 16, t3);
425+
/* STEP 7 : sum_in - current */
426+
sumInR = _twin_sub_ARGB(sumInR, _cur, 0, t1);
427+
sumInG = _twin_sub_ARGB(sumInG, _cur, 8, t2);
428+
sumInB = _twin_sub_ARGB(sumInB, _cur, 16, t3);
429+
}
430+
}
431+
}
432+
433+
void twin_stack_blur(twin_pixmap_t *px,
434+
int radius,
435+
twin_coord_t left,
436+
twin_coord_t right,
437+
twin_coord_t top,
438+
twin_coord_t bottom)
439+
{
440+
if (px->format != TWIN_ARGB32)
441+
return;
442+
twin_pixmap_t *tmp_px =
443+
twin_pixmap_create(px->format, px->width, px->height);
444+
memcpy(tmp_px->p.v, px->p.v,
445+
px->width * px->height * twin_bytes_per_pixel(px->format));
446+
twin_stack(tmp_px, px, radius, top, bottom, left, right, true);
447+
twin_stack(px, tmp_px, radius, left, right, top, bottom, false);
448+
twin_pixmap_destroy(tmp_px);
449+
return;
450+
}
451+
305452
/* FIXME: source clipping is busted */
306453
static void _twin_composite_simple(twin_pixmap_t *dst,
307454
twin_coord_t dst_x,

src/pixmap.c

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ twin_pixmap_t *twin_pixmap_create(twin_format_t format,
4343
pixmap->stride = stride;
4444
pixmap->disable = 0;
4545
pixmap->animation = NULL;
46+
pixmap->shadow = false;
4647
pixmap->p.v = pixmap + 1;
4748
memset(pixmap->p.v, '\0', space);
4849
return pixmap;

0 commit comments

Comments
 (0)