Skip to content

Commit 6296804

Browse files
committed
Render drop shadow for active window
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 the twin_drop_shadow() function to handle the pixels within the drop shadow area of the active window's pixel map. The drop shadow effect of the window is only visible when the window is on the top layer, ensuring the active window stands out visually. Implement the twin_shadow_border() function to create a darker border of the active window that gives a more dimensional appearance. In the twin_drop_shadow() function, twin_stack_blur() will apply a blur effect to the pixels beneath the active window's pixel map, which aims to create a frosted glass appearance. Furthermore, implement the twin_cover() function to cover the target pixels with the desired color. Ref: https://melatonin.dev/blog/implementing-marios-stack-blur-15-times-in-cpp/ Close sysprog21#34 Signed-off-by: Wei-Hsin Yeh <weihsinyeh168@gmail.com>
1 parent 053a651 commit 6296804

File tree

10 files changed

+691
-3
lines changed

10 files changed

+691
-3
lines changed

apps/multi.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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
{
@@ -272,6 +273,47 @@ 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);
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_pixmap_destroy(scaled_background);
313+
twin_pixmap_destroy(raw_background);
314+
twin_window_show(window);
315+
}
316+
275317
void apps_multi_start(twin_screen_t *screen,
276318
const char *name,
277319
int x,
@@ -286,4 +328,5 @@ void apps_multi_start(twin_screen_t *screen,
286328
apps_ascii_start(screen, x += 20, y += 20, w, h);
287329
apps_jelly_start(screen, x += 20, y += 20, w / 2, h);
288330
apps_flower_start(screen, x += 20, y += 20, w, h);
331+
apps_test(screen, x += 20, y += 20, w, h);
289332
}

configs/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ config CURSOR
5555
default n
5656
depends on !BACKEND_VNC
5757

58+
config DROP_SHADOW
59+
bool "Render drop shadow for active window"
60+
default y
61+
5862
endmenu
5963

6064
menu "Image Loaders"

include/twin.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,12 @@ typedef struct _twin_pixmap {
194194
* Pixels
195195
*/
196196
twin_animation_t *animation;
197+
/*
198+
* When the pixel map is within the active window, it will have a drop
199+
* shadow to enhance its visual distinction.
200+
*/
201+
bool shadow;
202+
197203
twin_pointer_t p;
198204
/*
199205
* When representing a window, this point
@@ -422,6 +428,11 @@ typedef void (*twin_destroy_func_t)(twin_window_t *window);
422428
struct _twin_window {
423429
twin_screen_t *screen;
424430
twin_pixmap_t *pixmap;
431+
432+
/* Set the shadow range for horizontal and vertical directions. */
433+
twin_coord_t shadow_offset_x;
434+
twin_coord_t shadow_offset_y;
435+
425436
twin_window_style_t style;
426437
twin_rect_t client;
427438
twin_rect_t damage;
@@ -626,6 +637,15 @@ void twin_dispatch(twin_context_t *ctx);
626637
* draw.c
627638
*/
628639

640+
void twin_stack_blur(twin_pixmap_t *px,
641+
int radius,
642+
twin_coord_t left,
643+
twin_coord_t right,
644+
twin_coord_t top,
645+
twin_coord_t bottom);
646+
647+
void twin_shadow_border(twin_pixmap_t *shadow);
648+
629649
void twin_composite(twin_pixmap_t *dst,
630650
twin_coord_t dst_x,
631651
twin_coord_t dst_y,
@@ -649,6 +669,12 @@ void twin_fill(twin_pixmap_t *dst,
649669

650670
void twin_premultiply_alpha(twin_pixmap_t *px);
651671

672+
void twin_cover(twin_pixmap_t *dst,
673+
twin_argb32_t color,
674+
twin_coord_t x,
675+
twin_coord_t y,
676+
twin_coord_t width);
677+
652678
/*
653679
* event.c
654680
*/

include/twin_private.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,21 @@ typedef int64_t twin_xfixed_t;
187187
((((s) << 5) & 0xfc00) | (((s) >> 1) & 0x300)) | \
188188
((((s) << 8) & 0xf80000) | (((s) << 3) & 0x70000)) | 0xff000000)
189189

190+
#define min(x, y) \
191+
({ \
192+
typeof(x) _x = (x); \
193+
typeof(y) _y = (y); \
194+
(void) (&_x == &_y); \
195+
_x < _y ? _x : _y; \
196+
})
197+
#define max(x, y) \
198+
({ \
199+
typeof(x) _x = (x); \
200+
typeof(y) _y = (y); \
201+
(void) (&_x == &_y); \
202+
_x > _y ? _x : _y; \
203+
})
204+
190205
typedef union {
191206
twin_pointer_t p;
192207
twin_argb32_t c;

src/draw-pixman.c

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <pixman.h>
88
#include "twin_private.h"
99

10+
#define TWIN_TITLE_HEIGHT 20
11+
1012
static void twin_argb32_to_pixman_color(twin_argb32_t argb,
1113
pixman_color_t *color)
1214
{
@@ -61,6 +63,219 @@ static void pixmap_matrix_scale(pixman_image_t *src, twin_matrix_t *matrix)
6163
pixman_image_set_transform(src, &transform);
6264
}
6365

66+
#define _twin_add_ARGB(s, d, i, t) (((t) = (s) + twin_get_8(d, i)))
67+
#define _twin_add(s, d, t) (((t) = (s) + (d)))
68+
#define _twin_div(d, den, i, t) \
69+
(((t) = (d) / (den)), (t) = twin_get_8((t), 0), \
70+
(twin_argb32_t) twin_sat(t) << (i))
71+
#define _twin_sub_ARGB(s, d, i, t) (((t) = (s) - twin_get_8(d, i)))
72+
#define _twin_sub(s, d, t) (((t) = (s) - (d)))
73+
#define twin_put_8(d, i, t) (((t) = (d) << (i)))
74+
75+
void twin_stack(twin_pixmap_t *trg_px,
76+
twin_pixmap_t *src_px,
77+
int radius,
78+
int first_str,
79+
int first_end,
80+
int second_str,
81+
int second_end,
82+
bool horiz_scan)
83+
{
84+
int den = (radius + 1) * (radius + 1);
85+
twin_pointer_t src_ptr, trg_ptr, old_ptr, new_ptr;
86+
twin_argb32_t sumInR, sumOutR, sumR, sumInG, sumOutG, sumG, sumInB, sumOutB,
87+
sumB, _cur, _old, _new, _src;
88+
uint16_t t1, t2, t3;
89+
for (int first = first_str; first < first_end; first++) {
90+
sumInR = sumOutR = sumR = sumInG = sumOutG = sumG = sumInB = sumOutB =
91+
sumB = 0x00000000;
92+
93+
/* Initialize SumOut by padding */
94+
if (horiz_scan)
95+
src_ptr = twin_pixmap_pointer(src_px, second_str, first);
96+
else
97+
src_ptr = twin_pixmap_pointer(src_px, first, second_str);
98+
_src = *src_ptr.argb32;
99+
100+
for (int i = second_str; i < second_str + radius; i++) {
101+
sumOutR = _twin_add_ARGB(sumOutR, _src, 0, t1);
102+
sumOutG = _twin_add_ARGB(sumOutG, _src, 8, t2);
103+
sumOutB = _twin_add_ARGB(sumOutB, _src, 16, t3);
104+
for (int j = 0; j < (i - second_str) + 1; j++) {
105+
sumR = _twin_add_ARGB(sumR, _src, 0, t1);
106+
sumG = _twin_add_ARGB(sumG, _src, 8, t2);
107+
sumB = _twin_add_ARGB(sumB, _src, 16, t3);
108+
}
109+
}
110+
111+
/* Initialize SumIn */
112+
for (int i = second_str; i < second_str + radius; i++) {
113+
if (horiz_scan)
114+
src_ptr = twin_pixmap_pointer(src_px, i, first);
115+
else
116+
src_ptr = twin_pixmap_pointer(src_px, first, i);
117+
_src = *src_ptr.argb32;
118+
sumInR = _twin_add_ARGB(sumInR, _src, 0, t1);
119+
sumInG = _twin_add_ARGB(sumInG, _src, 8, t2);
120+
sumInB = _twin_add_ARGB(sumInB, _src, 16, t3);
121+
for (int j = 0; j < radius - (i - second_str); j++) {
122+
sumR = _twin_add_ARGB(sumR, _src, 0, t1);
123+
sumG = _twin_add_ARGB(sumG, _src, 8, t2);
124+
sumB = _twin_add_ARGB(sumB, _src, 16, t3);
125+
}
126+
}
127+
128+
for (int cur = second_str; cur < second_end; cur++) {
129+
if (horiz_scan) {
130+
src_ptr = twin_pixmap_pointer(src_px, cur, first);
131+
trg_ptr = twin_pixmap_pointer(trg_px, cur, first);
132+
old_ptr = twin_pixmap_pointer(
133+
src_px, max(cur - radius, second_str), first);
134+
new_ptr = twin_pixmap_pointer(
135+
src_px, min(cur + radius, second_end - 1), first);
136+
} else {
137+
src_ptr = twin_pixmap_pointer(src_px, first, cur);
138+
trg_ptr = twin_pixmap_pointer(trg_px, first, cur);
139+
old_ptr = twin_pixmap_pointer(src_px, first,
140+
max(cur - radius, second_str));
141+
new_ptr = twin_pixmap_pointer(
142+
src_px, first, min(cur + radius, second_end - 1));
143+
}
144+
_cur = *src_ptr.argb32;
145+
_old = *old_ptr.argb32;
146+
_new = *new_ptr.argb32;
147+
/* STEP 1 : sum_out + current */
148+
sumOutR = _twin_add_ARGB(sumOutR, _cur, 0, t1);
149+
sumOutG = _twin_add_ARGB(sumOutG, _cur, 8, t2);
150+
sumOutB = _twin_add_ARGB(sumOutB, _cur, 16, t3);
151+
/* STEP 2 : sum_in + new */
152+
sumInR = _twin_add_ARGB(sumInR, _new, 0, t1);
153+
sumInG = _twin_add_ARGB(sumInG, _new, 8, t2);
154+
sumInB = _twin_add_ARGB(sumInB, _new, 16, t3);
155+
/* STEP 3 : sum + sum_in */
156+
sumR = _twin_add(sumR, sumInR, t1);
157+
sumG = _twin_add(sumG, sumInG, t2);
158+
sumB = _twin_add(sumB, sumInB, t3);
159+
/* STEP 4 : sum / denominator */
160+
*trg_ptr.argb32 =
161+
(_twin_div(sumR, den, 0, t1) | _twin_div(sumG, den, 8, t2) |
162+
_twin_div(sumB, den, 16, t3) | (*src_ptr.argb32 & 0xff000000));
163+
/* STEP 5 : sum - sum_out */
164+
sumR = _twin_sub(sumR, sumOutR, t1);
165+
sumG = _twin_sub(sumG, sumOutG, t2);
166+
sumB = _twin_sub(sumB, sumOutB, t3);
167+
/* STEP 6 : sum_out - old */
168+
sumOutR = _twin_sub_ARGB(sumOutR, _old, 0, t1);
169+
sumOutG = _twin_sub_ARGB(sumOutG, _old, 8, t2);
170+
sumOutB = _twin_sub_ARGB(sumOutB, _old, 16, t3);
171+
/* STEP 7 : sum_in - current */
172+
sumInR = _twin_sub_ARGB(sumInR, _cur, 0, t1);
173+
sumInG = _twin_sub_ARGB(sumInG, _cur, 8, t2);
174+
sumInB = _twin_sub_ARGB(sumInB, _cur, 16, t3);
175+
}
176+
}
177+
}
178+
179+
void twin_stack_blur(twin_pixmap_t *px,
180+
int radius,
181+
twin_coord_t left,
182+
twin_coord_t right,
183+
twin_coord_t top,
184+
twin_coord_t bottom)
185+
{
186+
if (px->format != TWIN_ARGB32)
187+
return;
188+
twin_pixmap_t *tmp_px =
189+
twin_pixmap_create(px->format, px->width, px->height);
190+
memcpy(tmp_px->p.v, px->p.v,
191+
px->width * px->height * twin_bytes_per_pixel(px->format));
192+
/*
193+
* Originally, performing a 2D convolution on each pixel takes O(width *
194+
* height * k²). However, by first scanning horizontally and then vertically
195+
* across the pixel map, and applying a 1D convolution to each pixel, the
196+
* complexity is reduced to O(2 * width * height * k).
197+
*/
198+
twin_stack(tmp_px, px, radius, top, bottom, left, right, true);
199+
twin_stack(px, tmp_px, radius, left, right, top, bottom, false);
200+
twin_pixmap_destroy(tmp_px);
201+
return;
202+
}
203+
204+
void twin_shadow_border(twin_pixmap_t *shadow)
205+
{
206+
twin_coord_t right_edge =
207+
(*shadow).width - (*shadow).window->shadow_offset_x,
208+
bottom_edge =
209+
(*shadow).height - (*shadow).window->shadow_offset_y,
210+
right_span = right_edge, clip, stride = 2, corner_offset,
211+
offset = min((*shadow).window->shadow_offset_x,
212+
(*shadow).window->shadow_offset_y);
213+
twin_pointer_t dst;
214+
twin_source_u src;
215+
twin_argb32_t color_x,
216+
color_y = 0xff000000,
217+
color_shift = ((((twin_coord_t) (0xff)) / (offset >> 2))) << 24;
218+
219+
for (twin_coord_t y = TWIN_TITLE_HEIGHT; y < (*shadow).height; y++) {
220+
color_x = 0xff000000;
221+
/* Render the right edge of the shadow border. */
222+
if (y < bottom_edge) {
223+
/*
224+
* Create a shadow with a diagonal shape, extending from the
225+
* top-left to the bottom-right.
226+
*/
227+
clip = max(
228+
0, shadow->window->shadow_offset_x - (y - TWIN_TITLE_HEIGHT));
229+
for (twin_coord_t x = right_edge; x < (*shadow).width - clip; x++) {
230+
dst = twin_pixmap_pointer(shadow, x, y);
231+
src.c = color_x;
232+
_twin_c_over_argb32(dst, src, 1);
233+
if ((x - right_edge) % stride == 0) {
234+
if (color_x > color_shift)
235+
color_x -= color_shift;
236+
color_x &= 0xff000000;
237+
}
238+
}
239+
} else
240+
/* Calculate the range of the corner. */
241+
right_span++;
242+
243+
/* Render the bottom edge of the shadow border. */
244+
if (y > bottom_edge) {
245+
/*
246+
* Create a shadow with a diagonal shape, extending from the
247+
* top-left to the bottom-right.
248+
*/
249+
clip = max(0, y - bottom_edge);
250+
dst = twin_pixmap_pointer(shadow, clip, y);
251+
src.c = color_y;
252+
_twin_c_over_argb32(dst, src, right_edge - clip);
253+
254+
/* Render the bottom-right corner of the shadow border. */
255+
256+
/*
257+
* Handle the case where the vertical shadow offset is larger than
258+
* the horizontal shadow offset.
259+
*/
260+
corner_offset = min(right_span - right_edge, offset);
261+
262+
for (twin_coord_t i = 0; i < corner_offset; i++) {
263+
/* The corner's pixels are symmetrical to the diagonal. */
264+
dst = twin_pixmap_pointer(shadow, right_edge + i, y);
265+
_twin_c_over_argb32(dst, src, 1);
266+
dst = twin_pixmap_pointer(
267+
shadow, right_edge + (y - bottom_edge), bottom_edge + i);
268+
_twin_c_over_argb32(dst, src, 1);
269+
}
270+
if ((shadow->y + y - bottom_edge) % stride == 0) {
271+
if (color_y > color_shift)
272+
color_y -= color_shift;
273+
color_y &= 0xff000000;
274+
}
275+
}
276+
}
277+
}
278+
64279
void twin_composite(twin_pixmap_t *_dst,
65280
twin_coord_t dst_x,
66281
twin_coord_t dst_y,
@@ -190,3 +405,16 @@ void twin_premultiply_alpha(twin_pixmap_t *px)
190405
p.argb32[x] = _twin_apply_alpha(p.argb32[x]);
191406
}
192407
}
408+
409+
void twin_cover(twin_pixmap_t *dst,
410+
twin_argb32_t color,
411+
twin_coord_t x,
412+
twin_coord_t y,
413+
twin_coord_t width)
414+
{
415+
twin_pointer_t pt;
416+
for (twin_coord_t i = 0; i < width; i++) {
417+
pt = twin_pixmap_pointer(dst, x + i, y);
418+
*pt.argb32 = color;
419+
}
420+
}

0 commit comments

Comments
 (0)