From 606d9d12bfc2aded28f3463aa051179fc8d06e5d Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Mon, 15 Feb 2021 22:11:26 +0100 Subject: [PATCH] Test blurring of background image (screenshot/background/image). window { blur: {radius}; } Does not work on true transparency. --- include/xcb.h | 1 + source/view.c | 12 +++++-- source/xcb.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/include/xcb.h b/include/xcb.h index a88d8d92..a2217ced 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -194,4 +194,5 @@ extern WindowManagerQuirk current_window_manager; * @returns NULL if window was not found, or unmapped, otherwise returns a cairo_surface. */ cairo_surface_t *x11_helper_get_screenshot_surface_window ( xcb_window_t window, int size ); +void cairo_image_surface_blur(cairo_surface_t* surface, unsigned int radius); #endif diff --git a/source/view.c b/source/view.c index 34e70998..4e4abb4e 100644 --- a/source/view.c +++ b/source/view.c @@ -678,7 +678,7 @@ static void filter_elements ( thread_state *ts, G_GNUC_UNUSED gpointer user_data g_mutex_unlock ( t->mutex ); } } -static void rofi_view_setup_fake_transparency ( const char* const fake_background ) +static void rofi_view_setup_fake_transparency ( widget *win, const char* const fake_background ) { if ( CacheState.fake_bg == NULL ) { cairo_surface_t *s = NULL; @@ -713,6 +713,8 @@ static void rofi_view_setup_fake_transparency ( const char* const fake_backgroun } else { CacheState.fake_bg = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, CacheState.mon.w, CacheState.mon.h ); + + int blur = rofi_theme_get_integer ( WIDGET ( win ), "blur", 0 ); cairo_t *dr = cairo_create ( CacheState.fake_bg ); if ( CacheState.fake_bgrel ) { cairo_set_source_surface ( dr, s, 0, 0 ); @@ -723,6 +725,10 @@ static void rofi_view_setup_fake_transparency ( const char* const fake_backgroun cairo_paint ( dr ); cairo_destroy ( dr ); cairo_surface_destroy ( s ); + if ( blur > 0 ){ + cairo_image_surface_blur( CacheState.fake_bg, (double)blur ); + TICK_N("BLUR"); + } } } TICK_N ( "Fake transparency" ); @@ -860,10 +866,10 @@ void __create_window ( MenuFlags menu_flags ) TICK_N ( "setup window name and class" ); const char *transparency = rofi_theme_get_string ( WIDGET ( win ), "transparency", NULL ); if ( transparency ) { - rofi_view_setup_fake_transparency ( transparency ); + rofi_view_setup_fake_transparency ( WIDGET (win), transparency ); } else if ( config.fake_transparency && config.fake_background ) { - rofi_view_setup_fake_transparency ( config.fake_background ); + rofi_view_setup_fake_transparency ( WIDGET ( win ), config.fake_background ); } if ( xcb->sncontext != NULL ) { sn_launchee_context_setup_window ( xcb->sncontext, CacheState.main_window ); diff --git a/source/xcb.c b/source/xcb.c index 2067a106..a3c3197a 100644 --- a/source/xcb.c +++ b/source/xcb.c @@ -113,6 +113,101 @@ static xcb_visualtype_t * lookup_visual ( xcb_screen_t *s, xcb_visualid_t visu return 0; } +typedef union { + uint32_t value; + struct { + uint8_t a,b,c,d; + }vals; +} Pixel __attribute__((packed)); + +typedef struct { + uint32_t a,b,c,d; +} Filter __attribute__((packed)); + +void cairo_image_surface_blur(cairo_surface_t* surface, unsigned int radius) +{ + // Currently we only support argb32 + if ( cairo_image_surface_get_format ( surface ) != CAIRO_FORMAT_ARGB32 ) { + g_warning("Invalid format for blurring."); + return; + } + // Steve Hanov, 2009 + // Tweaks by Dave Davenport. + // Released into the public domain. + + // get width, height + const uint_fast32_t stride = cairo_image_surface_get_stride(surface); + if ( stride%4 != 0 ) { + g_warning("Stride is not multiple of 4: %lu", stride%4); + return; + } + const uint_fast32_t width = stride/4; + const uint_fast32_t height = cairo_image_surface_get_height(surface); + const double mul = 1.0 / (double)(4.0*radius*radius); + Filter* precalc = (Filter*)g_malloc_n(stride*height,sizeof(Filter)); + Pixel * src = (Pixel *)cairo_image_surface_get_data(surface); + + // The number of times to perform the averaging. According to wikipedia, + // three iterations is good enough to pass for a gaussian. + const uint_fast32_t MAX_ITERATIONS = 3; + + const uint_fast32_t wr1 = width-radius-1; + const uint_fast32_t hr1 = height-radius-1; + for (uint_fast32_t iteration = 0; iteration < MAX_ITERATIONS; iteration++) { + Pixel *pixel = (Pixel *)src; + Filter *filter = (Filter*)precalc; + for (uint_fast32_t y = 0; y < height; y++) { + for (uint_fast32_t x = 0; x < width; x++) { + Pixel t = pixel[0]; + filter->a = t.vals.a; + filter->b = t.vals.b; + filter->c = t.vals.c; + filter->d = t.vals.d; + if (x!=0) { + filter->a += filter[-1].a; + filter->b += filter[-1].b; + filter->c += filter[-1].c; + filter->d += filter[-1].d; + } + if (y!=0) { + filter->a += filter[-width].a; + filter->b += filter[-width].b; + filter->c += filter[-width].c; + filter->d += filter[-width].d; + } + if (x!=0 && y!=0) { + filter->a -= filter[-width-1].a; + filter->b -= filter[-width-1].b; + filter->c -= filter[-width-1].c; + filter->d -= filter[-width-1].d; + } + filter++ ; + pixel++; + } + } + pixel = (Pixel *)src; + for (uint_fast32_t y = 0; y < (height); y++) { + const uint_fast32_t index = width*y; + const uint_fast32_t t = (y < radius)? 0: width*(y - radius); + const uint_fast32_t b = (y > hr1)? (height-1)*width: (y + radius)*width; + for (uint_fast32_t x = 0; x < (width ); x++) { + const uint_fast32_t l = (x < radius)? 0:(x - radius); + const uint_fast32_t r = (x > wr1)? (width-1):(x + radius); + int tota = precalc[r+b].a + precalc[l+t].a- precalc[l+b].a - precalc[r+t].a; + int totb = precalc[r+b].b + precalc[l+t].b- precalc[l+b].b - precalc[r+t].b; + int totc = precalc[r+b].c + precalc[l+t].c- precalc[l+b].c - precalc[r+t].c; + int totd = precalc[r+b].d + precalc[l+t].d- precalc[l+b].d - precalc[r+t].d; + pixel[index+x].vals.a = (uint8_t)(tota*mul); + pixel[index+x].vals.b = (uint8_t)(totb*mul); + pixel[index+x].vals.c = (uint8_t)(totc*mul); + pixel[index+x].vals.d = (uint8_t)(totd*mul); + } + } + } + + g_free(precalc); +} + cairo_surface_t *x11_helper_get_screenshot_surface_window ( xcb_window_t window, int size ) { xcb_get_geometry_cookie_t cookie;