From 157b0e512c007b9ce845a0cb21338f1aee2a3edd Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Tue, 16 Feb 2021 01:46:04 +0100 Subject: [PATCH] Improve blur algorithm. --- include/xcb.h | 2 +- source/view.c | 3 +- source/xcb.c | 223 ++++++++++++++++++++++++++++++-------------------- 3 files changed, 136 insertions(+), 92 deletions(-) diff --git a/include/xcb.h b/include/xcb.h index a2217ced..77962e51 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -194,5 +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); +void cairo_image_surface_blur(cairo_surface_t* surface, double radius, double deviation); #endif diff --git a/source/view.c b/source/view.c index 4e4abb4e..1006a217 100644 --- a/source/view.c +++ b/source/view.c @@ -678,6 +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 ( widget *win, const char* const fake_background ) { if ( CacheState.fake_bg == NULL ) { @@ -726,7 +727,7 @@ static void rofi_view_setup_fake_transparency ( widget *win, const char* const f cairo_destroy ( dr ); cairo_surface_destroy ( s ); if ( blur > 0 ){ - cairo_image_surface_blur( CacheState.fake_bg, (double)blur ); + cairo_image_surface_blur( CacheState.fake_bg, (double)blur, 0 ); TICK_N("BLUR"); } } diff --git a/source/xcb.c b/source/xcb.c index a3c3197a..a2ebefb4 100644 --- a/source/xcb.c +++ b/source/xcb.c @@ -38,7 +38,7 @@ #include #include #include - +#include #include #include #include @@ -113,101 +113,144 @@ 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)); +/* This blur function was originally created my MacSlow and published on his website: + * http://macslow.thepimp.net. I'm not entirely sure he's proud of it, but it has + * proved immeasurably useful for me. */ -typedef struct { - uint32_t a,b,c,d; -} Filter __attribute__((packed)); +static uint32_t* create_kernel(double radius, double deviation, uint32_t *sum2) { + int size = 2 * (int)(radius) + 1; + uint32_t* kernel = (uint32_t*)(g_malloc(sizeof(uint32_t) * (size + 1))); + double radiusf = fabs(radius) + 1.0; + double value = -radius; + double sum = 0.0; + int i; -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); - } - } + if(deviation == 0.0) { + deviation = sqrt( -(radiusf * radiusf) / (2.0 * log(1.0 / 255.0))); } - g_free(precalc); + kernel[0] = size; + + for(i = 0; i < size; i++) { + kernel[1 + i] = INT16_MAX / (2.506628275 * deviation) * exp(-((value * value) / (2.0 * (deviation * deviation)))) ; + + sum += kernel[1 + i]; + value += 1.0; + } + + *sum2 = sum; + + return kernel; } +void cairo_image_surface_blur(cairo_surface_t* surface, double radius, double deviation) +{ + uint32_t* horzBlur; + uint32_t * kernel = 0; + cairo_format_t format; + unsigned int channels; + + if(cairo_surface_status(surface)) return ; + + uint8_t *data = cairo_image_surface_get_data(surface); + format = cairo_image_surface_get_format(surface); + const int width = cairo_image_surface_get_width(surface); + const int height = cairo_image_surface_get_height(surface); + const int stride = cairo_image_surface_get_stride(surface); + + if(format == CAIRO_FORMAT_ARGB32) channels = 4; + else return ; + + horzBlur = (uint32_t*)(g_malloc(sizeof(uint32_t) * height * stride)); + TICK(); + uint32_t sum = 0; + kernel = create_kernel(radius, deviation, &sum); + TICK_N("BLUR: kernel"); + + /* Horizontal pass. */ + uint32_t *horzBlur_ptr = horzBlur; + for(int iY = 0; iY < height; iY++) { + const int iYs = iY*stride; + for(int iX = 0; iX < width; iX++) { + uint32_t red = 0; + uint32_t green = 0; + uint32_t blue = 0; + uint32_t alpha = 0; + int offset = (int)(kernel[0]) / -2; + + for(int i = 0; i < (int)(kernel[0]); i++) { + int x = iX + offset; + + if(x < 0 || x >= width){ + offset++; + continue; + } + + uint8_t *dataPtr = &data[iYs + x * channels]; + const uint32_t kernip1 = kernel[i + 1]; + + blue += kernip1 * dataPtr[0]; + green += kernip1 * dataPtr[1]; + red += kernip1 * dataPtr[2]; + alpha += kernip1 * dataPtr[3]; + offset++; + } + + + *horzBlur_ptr++ = blue/sum; + *horzBlur_ptr++ = green/sum; + *horzBlur_ptr++ = red/sum; + *horzBlur_ptr++ = alpha/sum; + } + } + TICK_N("BLUR: hori"); + + /* Vertical pass. */ + for(int iY = 0; iY < height; iY++) { + for(int iX = 0; iX < width; iX++) { + uint32_t red = 0; + uint32_t green = 0; + uint32_t blue = 0; + uint32_t alpha = 0; + int offset = (int)(kernel[0]) / -2; + + const int iXs = iX*channels; + for(int i = 0; i < (int)(kernel[0]); i++) { + int y = iY + offset; + + if(y < 0 || y >= height) { + offset++; + continue; + } + + uint32_t *dataPtr = &horzBlur[y * stride + iXs]; + const uint32_t kernip1 = kernel[i + 1]; + + blue += kernip1 * dataPtr[0]; + green += kernip1 * dataPtr[1]; + red += kernip1 * dataPtr[2]; + alpha += kernip1 * dataPtr[3]; + + offset++; + } + + + *data++ = blue/sum; + *data++ = green/sum; + *data++ = red/sum; + *data++ = alpha/sum; + } + } + TICK_N("BLUR: vert"); + + free(kernel); + free(horzBlur); + + return ; +} + + + cairo_surface_t *x11_helper_get_screenshot_surface_window ( xcb_window_t window, int size ) { xcb_get_geometry_cookie_t cookie;