mirror of
https://github.com/davatorium/rofi.git
synced 2024-11-11 13:50:48 -05:00
Improve blur algorithm.
This commit is contained in:
parent
a4a71f9bd4
commit
157b0e512c
3 changed files with 136 additions and 92 deletions
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
223
source/xcb.c
223
source/xcb.c
|
@ -38,7 +38,7 @@
|
|||
#include <glib.h>
|
||||
#include <cairo.h>
|
||||
#include <cairo-xcb.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
#include <xcb/randr.h>
|
||||
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue