diff --git a/apps/multi.c b/apps/multi.c index 7fd8eb1..d7d0f71 100644 --- a/apps/multi.c +++ b/apps/multi.c @@ -4,14 +4,16 @@ * All rights reserved. */ +#include #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; @@ -38,7 +40,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; @@ -83,7 +85,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; @@ -126,7 +128,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; @@ -174,7 +176,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; @@ -220,6 +222,42 @@ static void apps_jelly_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_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->pixmap->width)); + sy = twin_fixed_div(twin_int_to_fixed(raw_background->height), + twin_int_to_fixed(window->pixmap->height)); + + 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, NULL, 0, 0, + TWIN_SOURCE, screen->width, screen->height); + twin_pointer_t src, dst; + for (int y = 0; y < scaled_background->height; y++) + for (int x = 0; x < scaled_background->width; x++) { + src = twin_pixmap_pointer(scaled_background, x, y); + dst = twin_pixmap_pointer(window->pixmap, x, y); + *dst.argb32 = *src.argb32 | 0xff000000; + } + + twin_stack_blur(window->pixmap); + twin_window_set_name(window, "Test"); + twin_window_show(window); +} + void apps_multi_start(twin_screen_t *screen, const char *name, int x, @@ -233,4 +271,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_test(screen, x, y, w, h); } diff --git a/include/twin.h b/include/twin.h index 356ccaa..974e8aa 100644 --- a/include/twin.h +++ b/include/twin.h @@ -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 @@ -422,6 +423,11 @@ 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_argb32_t shadow_color; twin_window_style_t style; twin_rect_t client; twin_rect_t damage; @@ -648,6 +654,8 @@ void twin_fill(twin_pixmap_t *dst, void twin_premultiply_alpha(twin_pixmap_t *px); +void twin_stack_blur(twin_pixmap_t *px); + /* * event.c */ @@ -1121,7 +1129,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); @@ -1129,6 +1138,8 @@ void twin_window_show(twin_window_t *window); void twin_window_hide(twin_window_t *window); +void twin_shadow_visible(twin_pixmap_t *pixmap, twin_window_t *window); + void twin_window_configure(twin_window_t *window, twin_window_style_t style, twin_coord_t x, diff --git a/src/draw.c b/src/draw.c index e3f1655..7f2531b 100644 --- a/src/draw.c +++ b/src/draw.c @@ -302,6 +302,141 @@ 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, i, t) \ + (((t) = (d) / 9), (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, bool horiz_span) +{ + int first_num, second_num, radius = 2; + 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, t4; + first_num = src_px->height, second_num = src_px->width; + for (int first = 0; first < first_num; first++) { + /* padding sum_out */ + sumInR = sumOutR = sumR = sumInG = sumOutG = sumG = sumInB = sumOutB = + sumB = 0x00000000; + + for (int i = 0; i < radius; i++) { + if (horiz_span) + src_ptr = twin_pixmap_pointer(src_px, 0, first); + else + src_ptr = twin_pixmap_pointer(src_px, first, 0); + _src = *src_ptr.argb32; + 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 + 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); + } + } + + for (int i = 0; i < 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; 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 = 0; cur < second_num; 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, 0), first); + new_ptr = twin_pixmap_pointer( + src_px, min(cur + radius, second_num - 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, 0)); + new_ptr = twin_pixmap_pointer( + src_px, first, min(cur + radius, second_num - 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 / 9 */ + *trg_ptr.argb32 = + (_twin_div(sumR, 0, t1) | _twin_div(sumG, 8, t2) | + _twin_div(sumB, 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 - source_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) +{ + 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, true); + twin_stack(px, tmp_px, 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, @@ -777,3 +912,41 @@ void twin_fill(twin_pixmap_t *dst, (*op)(twin_pixmap_pointer(dst, left, iy), src, right - left); twin_pixmap_damage(dst, left, top, right, bottom); } + +void twin_glass_fill(twin_pixmap_t *dst, + twin_argb32_t pixel, + twin_operator_t operator, + twin_coord_t left, + twin_coord_t top, + twin_coord_t right, + twin_coord_t bottom) +{ + twin_src_op op; + twin_source_u src; + twin_coord_t iy, ix; + + /* offset */ + left += dst->origin_x; + top += dst->origin_y; + right += dst->origin_x; + bottom += dst->origin_y; + + /* clip */ + if (left < dst->clip.left) + left = dst->clip.left; + if (right > dst->clip.right) + right = dst->clip.right; + if (top < dst->clip.top) + top = dst->clip.top; + if (bottom > dst->clip.bottom) + bottom = dst->clip.bottom; + if (left >= right || top >= bottom) + return; + + src.c = pixel; + op = fill[operator][dst->format]; + + for (iy = 0; iy < dst->height; iy++) + for (ix = 0; ix < dst->width; ix++) + (*op)(twin_pixmap_pointer(dst, ix, iy), src, 1); +} diff --git a/src/pixmap.c b/src/pixmap.c index dd234d6..b44a7c8 100644 --- a/src/pixmap.c +++ b/src/pixmap.c @@ -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; diff --git a/src/screen.c b/src/screen.c index 76bcfe2..a9bdd33 100644 --- a/src/screen.c +++ b/src/screen.c @@ -356,7 +356,8 @@ bool twin_screen_dispatch(twin_screen_t *screen, twin_event_t *event) evt = *event; evt.kind = TwinEventLeave; _twin_adj_mouse_evt(&evt, pixmap); - twin_pixmap_dispatch(pixmap, &evt); + if (!pixmap->shadow) + twin_pixmap_dispatch(pixmap, &evt); } pixmap = screen->target = ntarget; @@ -365,7 +366,8 @@ bool twin_screen_dispatch(twin_screen_t *screen, twin_event_t *event) evt = *event; _twin_adj_mouse_evt(&evt, pixmap); evt.kind = TwinEventEnter; - twin_pixmap_dispatch(pixmap, &evt); + if (!pixmap->shadow) + twin_pixmap_dispatch(pixmap, &evt); } } @@ -389,7 +391,7 @@ bool twin_screen_dispatch(twin_screen_t *screen, twin_event_t *event) pixmap = NULL; break; } - if (pixmap) + if (pixmap && !pixmap->shadow) return twin_pixmap_dispatch(pixmap, event); return false; } diff --git a/src/toplevel.c b/src/toplevel.c index 53d6e68..8216086 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -81,7 +81,7 @@ twin_toplevel_t *twin_toplevel_create(twin_screen_t *screen, { twin_toplevel_t *toplevel; twin_window_t *window = - twin_window_create(screen, format, style, x, y, width, height); + twin_window_create(screen, format, style, x, y, width, height, false); if (!window) return NULL; diff --git a/src/window.c b/src/window.c index 72fa65a..0f67a83 100644 --- a/src/window.c +++ b/src/window.c @@ -23,7 +23,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) { twin_window_t *window = malloc(sizeof(twin_window_t)); twin_coord_t left, top, right, bottom; @@ -59,6 +60,20 @@ twin_window_t *twin_window_create(twin_screen_t *screen, twin_pixmap_origin_to_clip(window->pixmap); window->pixmap->window = window; twin_pixmap_move(window->pixmap, x, y); + window->shadow = shadow; + if (window->shadow) { + window->shadow_offset_x, window->shadow_offset_y = 50, 50; + window->shadow_color = 0x55000000; + window->shadow_pixmap = twin_pixmap_create(format, width, height); + window->shadow_pixmap->shadow = true; + twin_pixmap_move(window->shadow_pixmap, x + 50, y + 50); + if (window->shadow_pixmap == NULL) { + twin_pixmap_destroy(window->shadow_pixmap); + return; + } + twin_shadow_visible(window->shadow_pixmap, window); + } else + window->shadow_pixmap = NULL; window->damage = window->client; window->client_grab = false; window->want_focus = false; @@ -78,17 +93,31 @@ void twin_window_destroy(twin_window_t *window) twin_pixmap_destroy(window->pixmap); free(window->name); free(window); + if (window->shadow_pixmap) + twin_pixmap_destroy(window->shadow_pixmap); + free(window->shadow_pixmap); } void twin_window_show(twin_window_t *window) { + if (window->shadow_pixmap) + twin_pixmap_show(window->shadow_pixmap, window->screen, + window->screen->top); if (window->pixmap != window->screen->top) twin_pixmap_show(window->pixmap, window->screen, window->screen->top); } +void twin_shadow_visible(twin_pixmap_t *pixmap, twin_window_t *window) +{ + twin_glass_fill(window->shadow_pixmap, 0x55000000, TWIN_OVER, 0, 0, + pixmap->width, pixmap->height); +} + void twin_window_hide(twin_window_t *window) { twin_pixmap_hide(window->pixmap); + if (window->shadow_pixmap) + twin_pixmap_hide(window->shadow_pixmap); } void twin_window_configure(twin_window_t *window, @@ -101,6 +130,9 @@ void twin_window_configure(twin_window_t *window, bool need_repaint = false; twin_pixmap_disable_update(window->pixmap); + if (window->shadow_pixmap) + twin_pixmap_disable_update(window->shadow_pixmap); + if (style != window->style) { window->style = style; need_repaint = true; @@ -112,6 +144,8 @@ void twin_window_configure(twin_window_t *window, window->pixmap = twin_pixmap_create(old->format, width, height); window->pixmap->window = window; twin_pixmap_move(window->pixmap, x, y); + if (window->shadow) + twin_pixmap_move(window->shadow_pixmap, x + 50, y + 50); if (old->screen) twin_pixmap_show(window->pixmap, window->screen, old); for (i = 0; i < old->disable; i++) @@ -122,12 +156,29 @@ void twin_window_configure(twin_window_t *window, window->client.top, window->client.right, window->client.bottom); twin_pixmap_origin_to_clip(window->pixmap); + + if (window->shadow) { + window->shadow_pixmap = + twin_pixmap_create(old->format, width, height); + window->shadow_pixmap->shadow = true; + twin_pixmap_move(window->shadow_pixmap, x + 50, y + 50); + if (window->shadow_pixmap) { + twin_pixmap_destroy(window->shadow_pixmap); + return; + } + twin_shadow_visible(window->shadow_pixmap, window); + } } - if (x != window->pixmap->x || y != window->pixmap->y) + if (x != window->pixmap->x || y != window->pixmap->y) { twin_pixmap_move(window->pixmap, x, y); + if (window->shadow_pixmap) + twin_pixmap_move(window->shadow_pixmap, x + 50, y + 50); + } if (need_repaint) twin_window_draw(window); twin_pixmap_enable_update(window->pixmap); + if (window->shadow_pixmap) + twin_pixmap_enable_update(window->shadow_pixmap); } void twin_window_style_size(twin_window_style_t style, twin_rect_t *size) @@ -191,6 +242,8 @@ static void twin_window_frame(twin_window_t *window) twin_fill(pixmap, 0x00000000, TWIN_SOURCE, 0, 0, pixmap->width, window->client.top); + if (window->shadow) + twin_shadow_visible(window->shadow_pixmap, window); path = twin_path_create(); @@ -307,12 +360,18 @@ void twin_window_draw(twin_window_t *window) window->damage.right, window->damage.bottom); twin_screen_disable_update(window->screen); + if (window->shadow) + twin_pixmap_disable_update(window->shadow_pixmap); (*window->draw)(window); /* damage matching screen area */ twin_pixmap_damage(pixmap, window->damage.left, window->damage.top, window->damage.right, window->damage.bottom); + if (window->shadow) + twin_pixmap_damage(window->shadow_pixmap, window->damage.left, + window->damage.top, window->damage.right, + window->damage.bottom); twin_screen_enable_update(window->screen); /* clear damage and restore clip */ @@ -321,6 +380,8 @@ void twin_window_draw(twin_window_t *window) twin_pixmap_reset_clip(pixmap); twin_pixmap_clip(pixmap, window->client.left, window->client.top, window->client.right, window->client.bottom); + if (window->shadow) + twin_pixmap_enable_update(window->shadow_pixmap); } /* window keep track of local damage */