Skip to content

Commit

Permalink
Render drop shadow for active window
Browse files Browse the repository at this point in the history
Implement the twin_window_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.

Add the twin_stack_blur() function to implement Mario's Stack Blur
algorithm, which blurs the target pixel map. Additionally, create a blur
window in apps_blur() to show the effect of twin_stack_blur().

Implement the twin_shadow_border() function to create a darker border of
the active window that gives a more dimensional appearance.

The function twin_window_drop_shadow() will first apply the
twin_shadow_border() and then apply twin_stack_blur() to blur the darker
border.

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 #34

Signed-off-by: Wei-Hsin Yeh <[email protected]>
  • Loading branch information
weihsinyeh committed Feb 4, 2025
1 parent 122f696 commit 5f47a9a
Show file tree
Hide file tree
Showing 9 changed files with 473 additions and 9 deletions.
53 changes: 53 additions & 0 deletions apps/multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#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)
{
Expand Down Expand Up @@ -272,6 +273,55 @@ static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h)
twin_window_show(window);
}

#if defined(CONFIG_LOADER_PNG)
static void apps_blur(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);
if (!raw_background)
return;
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
twin_window_set_name(window, "Blur");
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,
scaled_background->width, scaled_background->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_stack_blur(window->pixmap, 5, window->client.left,
window->client.right, window->client.top,
window->client.bottom);

twin_pixmap_destroy(scaled_background);
twin_pixmap_destroy(raw_background);
twin_window_show(window);
return;
}
#endif

void apps_multi_start(twin_screen_t *screen,
const char *name,
int x,
Expand All @@ -286,4 +336,7 @@ void apps_multi_start(twin_screen_t *screen,
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);
#if defined(CONFIG_LOADER_PNG)
apps_blur(screen, x += 20, y += 20, w / 2, h / 2);
#endif
}
22 changes: 22 additions & 0 deletions configs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,28 @@ config CURSOR
default n
depends on !BACKEND_VNC

config DROP_SHADOW
bool "Render drop shadow for active window"
default y

config HORIZONTAL_OFFSET
int "Horizontal offset"
default 1
range 1 10
depends on DROP_SHADOW

config VERTICAL_OFFSET
int "Vertical offset"
default 1
range 1 10
depends on DROP_SHADOW

config SHADOW_BLUR
int "Shadow blur radius"
default 10
range 1 10
depends on DROP_SHADOW

endmenu

menu "Image Loaders"
Expand Down
34 changes: 34 additions & 0 deletions include/twin.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,15 @@ typedef struct _twin_pixmap {
* Pixels
*/
twin_animation_t *animation;

#if defined(CONFIG_DROP_SHADOW)
/*
* When the pixel map is within the active window, it will have a drop
* shadow to enhance its visual distinction.
*/
bool shadow;
#endif

twin_pointer_t p;
/*
* When representing a window, this point
Expand Down Expand Up @@ -423,6 +432,13 @@ typedef void (*twin_destroy_func_t)(twin_window_t *window);
struct _twin_window {
twin_screen_t *screen;
twin_pixmap_t *pixmap;

#if defined(CONFIG_DROP_SHADOW)
/* Set the shadow range for horizontal and vertical directions. */
twin_coord_t shadow_x;
twin_coord_t shadow_y;
#endif

twin_window_style_t style;
twin_rect_t client;
twin_rect_t damage;
Expand Down Expand Up @@ -652,8 +668,26 @@ void twin_fill(twin_pixmap_t *dst,
* draw-common.c
*/

/* Blur the specified area in the pixel map. */
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);

void twin_premultiply_alpha(twin_pixmap_t *px);

/*
* Overwrite the original pixel values for a specified number of pixels in
* width.
*/
void twin_cover(twin_pixmap_t *dst,
twin_argb32_t color,
twin_coord_t x,
twin_coord_t y,
twin_coord_t width);

/*
* event.c
*/
Expand Down
50 changes: 49 additions & 1 deletion include/twin_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,46 @@ typedef int64_t twin_xfixed_t;
(((t) = twin_get_8(d, i) + twin_get_8(s, i)), (twin_argb32_t) twin_sat(t) \
<< (i))

#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 twin_argb32_to_rgb16(s) \
((((s) >> 3) & 0x001f) | (((s) >> 5) & 0x07e0) | (((s) >> 8) & 0xf800))
#define twin_rgb16_to_argb32(s) \
(((((s) << 3) & 0xf8) | (((s) >> 2) & 0x7)) | \
((((s) << 5) & 0xfc00) | (((s) >> 1) & 0x300)) | \
((((s) << 8) & 0xf80000) | (((s) << 3) & 0x70000)) | 0xff000000)

#if defined(__GNUC__) || defined(__clang__)
#ifndef min
#define min(x, y) \
({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; \
})
#endif
#ifndef max
#define max(x, y) \
({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x > _y ? _x : _y; \
})
#endif
#else /* generic implementation: potential side effects */
#define min(x, y) ((x) < (y) ? (x) : (y))
#define max(x, y) ((x) > (y) ? (x) : (y))
#endif

typedef union {
twin_pointer_t p;
twin_argb32_t c;
Expand Down Expand Up @@ -468,7 +501,7 @@ void _twin_path_sfinish(twin_path_t *path);
#define twin_glyph_snap_y(g) (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g))

/*
* dispatch stuff
* Dispatch stuff
*/
typedef struct _twin_queue {
struct _twin_queue *next;
Expand Down Expand Up @@ -593,6 +626,21 @@ void _twin_button_init(twin_button_t *button,
twin_style_t font_style,
twin_dispatch_proc_t dispatch);

/*
* Visual effect stuff
*/

#if defined(CONFIG_DROP_SHADOW)
/*
* Add a shadow with the specified color, horizontal offset, and vertical
* offset.
*/
void twin_shadow_border(twin_pixmap_t *shadow,
twin_argb32_t color,
twin_coord_t shift_x,
twin_coord_t shift_y);
#endif

/* utility */

#ifdef _MSC_VER
Expand Down
Loading

0 comments on commit 5f47a9a

Please sign in to comment.