Merge pull request #117 from yshui/convkern-refactor
Use struct conv for blur kernel as well
This commit is contained in:
commit
57c5fcab91
|
@ -15,6 +15,7 @@
|
|||
#include "region.h"
|
||||
#include "string_utils.h"
|
||||
#include "utils.h"
|
||||
#include "kernel.h"
|
||||
|
||||
#include "backend/gl/gl_common.h"
|
||||
|
||||
|
@ -678,13 +679,10 @@ bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_ca
|
|||
}
|
||||
|
||||
for (int i = 0; i < MAX_BLUR_PASS && ps->o.blur_kerns[i]; ++i) {
|
||||
xcb_render_fixed_t *kern = ps->o.blur_kerns[i];
|
||||
if (!kern)
|
||||
break;
|
||||
|
||||
auto kern = ps->o.blur_kerns[i];
|
||||
// Build shader
|
||||
int wid = XFIXED_TO_DOUBLE(kern[0]), hei = XFIXED_TO_DOUBLE(kern[1]);
|
||||
int nele = wid * hei - 1;
|
||||
int width = kern->w, height = kern->h;
|
||||
int nele = width * height - 1;
|
||||
unsigned int len =
|
||||
strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) +
|
||||
strlen(extension) + (strlen(shader_add) + strlen(texture_func) + 42) * nele +
|
||||
|
@ -696,15 +694,16 @@ bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_ca
|
|||
assert(strlen(shader_str) < len);
|
||||
|
||||
double sum = 0.0;
|
||||
for (int j = 0; j < hei; ++j) {
|
||||
for (int k = 0; k < wid; ++k) {
|
||||
if (hei / 2 == j && wid / 2 == k)
|
||||
for (int j = 0; j < height; ++j) {
|
||||
for (int k = 0; k < width; ++k) {
|
||||
if (height / 2 == j && width / 2 == k)
|
||||
continue;
|
||||
double val = XFIXED_TO_DOUBLE(kern[2 + j * wid + k]);
|
||||
if (0.0 == val)
|
||||
double val = kern->data[j * width + k];
|
||||
if (val == 0) {
|
||||
continue;
|
||||
}
|
||||
sum += val;
|
||||
sprintf(pc, shader_add, val, texture_func, k - wid / 2, j - hei / 2);
|
||||
sprintf(pc, shader_add, val, texture_func, k - width / 2, j - height / 2);
|
||||
pc += strlen(pc);
|
||||
assert(strlen(shader_str) < len);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/present.h>
|
||||
#include <xcb/sync.h>
|
||||
#include <xcb/render.h>
|
||||
#include <xcb/composite.h>
|
||||
#include <xcb/present.h>
|
||||
#include <xcb/render.h>
|
||||
#include <xcb/sync.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "backend/backend.h"
|
||||
#include "backend/backend_common.h"
|
||||
|
@ -154,19 +154,13 @@ static void compose(void *backend_data, session_t *ps, win *w, void *win_data, i
|
|||
0, dst_x, dst_y, w->widthb, w->heightb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset filter on a <code>Picture</code>.
|
||||
*/
|
||||
static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
|
||||
const char *filter = "Nearest";
|
||||
xcb_render_set_picture_filter(ps->c, p, strlen(filter), filter, 0, NULL);
|
||||
}
|
||||
|
||||
static bool blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_paint) {
|
||||
static bool
|
||||
blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_paint) {
|
||||
struct _xrender_data *xd = backend_data;
|
||||
const pixman_box32_t *reg = pixman_region32_extents((region_t *)reg_paint);
|
||||
const int height = reg->y2 - reg->y1;
|
||||
const int width = reg->x2 - reg->x1;
|
||||
static const char *default_filter = "Nearest";
|
||||
|
||||
// Create a buffer for storing blurred picture, make it just big enough
|
||||
// for the blur region
|
||||
|
@ -202,20 +196,18 @@ static bool blur(void *backend_data, session_t *ps, double opacity, const region
|
|||
int i;
|
||||
for (i = 0; ps->o.blur_kerns[i]; i++) {
|
||||
assert(i < MAX_BLUR_PASS - 1);
|
||||
xcb_render_fixed_t *convolution_blur = ps->o.blur_kerns[i];
|
||||
int kwid = XFIXED_TO_DOUBLE(convolution_blur[0]),
|
||||
khei = XFIXED_TO_DOUBLE(convolution_blur[1]);
|
||||
|
||||
// Copy from source picture to destination. The filter must
|
||||
// be applied on source picture, to get the nearby pixels outside the
|
||||
// window.
|
||||
xcb_render_set_picture_filter(ps->c, src_pict, strlen(XRFILTER_CONVOLUTION),
|
||||
XRFILTER_CONVOLUTION, kwid * khei + 2, convolution_blur);
|
||||
// TODO cache converted blur_kerns
|
||||
x_set_picture_convolution_kernel(ps->c, src_pict, ps->o.blur_kerns[i]);
|
||||
|
||||
if (ps->o.blur_kerns[i + 1] || i == 0) {
|
||||
// This is not the last pass, or this is the first pass
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
|
||||
dst_pict, src_x, src_y, 0, 0, 0, 0, width, height);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict,
|
||||
XCB_NONE, dst_pict, src_x, src_y, 0, 0, 0, 0,
|
||||
width, height);
|
||||
} else {
|
||||
// This is the last pass, and this is also not the first
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict,
|
||||
|
@ -223,7 +215,9 @@ static bool blur(void *backend_data, session_t *ps, double opacity, const region
|
|||
reg->y1, width, height);
|
||||
}
|
||||
|
||||
xrfilter_reset(ps, src_pict);
|
||||
// reset filter
|
||||
xcb_render_set_picture_filter(ps->c, src_pict, strlen(default_filter),
|
||||
default_filter, 0, NULL);
|
||||
|
||||
src_pict = tmp_picture[current];
|
||||
dst_pict = tmp_picture[!current];
|
||||
|
@ -243,8 +237,8 @@ static bool blur(void *backend_data, session_t *ps, double opacity, const region
|
|||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
render_win(void *backend_data, session_t *ps, win *w, void *win_data, const region_t *reg_paint) {
|
||||
static void render_win(void *backend_data, session_t *ps, win *w, void *win_data,
|
||||
const region_t *reg_paint) {
|
||||
struct _xrender_data *xd = backend_data;
|
||||
struct _xrender_win_data *wd = win_data;
|
||||
|
||||
|
@ -278,8 +272,9 @@ render_win(void *backend_data, session_t *ps, win *w, void *win_data, const regi
|
|||
// Handle invert color
|
||||
x_set_picture_clip_region(ps->c, wd->rendered_pict, 0, 0, ®_paint_local);
|
||||
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE, xd->white_pixel, XCB_NONE,
|
||||
wd->rendered_pict, 0, 0, 0, 0, 0, 0, w->widthb, w->heightb);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, wd->rendered_pict, 0, 0,
|
||||
0, 0, 0, 0, w->widthb, w->heightb);
|
||||
// We use an extra PictOpInReverse operation to get correct pixel
|
||||
// alpha. There could be a better solution.
|
||||
if (win_has_alpha(w))
|
||||
|
@ -305,8 +300,9 @@ render_win(void *backend_data, session_t *ps, win *w, void *win_data, const regi
|
|||
|
||||
// Step 2: multiply alpha value
|
||||
// XXX test
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->white_pixel, alpha_pict,
|
||||
wd->rendered_pict, 0, 0, 0, 0, 0, 0, w->widthb, w->heightb);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->white_pixel,
|
||||
alpha_pict, wd->rendered_pict, 0, 0, 0, 0, 0, 0,
|
||||
w->widthb, w->heightb);
|
||||
}
|
||||
|
||||
if (w->dim) {
|
||||
|
@ -361,7 +357,8 @@ static void *prepare_win(void *backend_data, session_t *ps, win *w) {
|
|||
// leave this here until we have chance to re-think the backend API
|
||||
if (w->shadow) {
|
||||
xcb_pixmap_t pixmap;
|
||||
build_shadow(ps, 1, w->widthb, w->heightb, xd->shadow_pixel, &pixmap, &wd->shadow_pict);
|
||||
build_shadow(ps, 1, w->widthb, w->heightb, xd->shadow_pixel, &pixmap,
|
||||
&wd->shadow_pict);
|
||||
xcb_free_pixmap(ps->c, pixmap);
|
||||
}
|
||||
return wd;
|
||||
|
@ -389,12 +386,12 @@ static void *init(session_t *ps) {
|
|||
|
||||
xd->black_pixel = solid_picture(ps, true, 1, 0, 0, 0);
|
||||
xd->white_pixel = solid_picture(ps, true, 1, 1, 1, 1);
|
||||
xd->shadow_pixel =
|
||||
solid_picture(ps, true, 1, ps->o.shadow_red, ps->o.shadow_green, ps->o.shadow_blue);
|
||||
xd->shadow_pixel = solid_picture(ps, true, 1, ps->o.shadow_red,
|
||||
ps->o.shadow_green, ps->o.shadow_blue);
|
||||
|
||||
if (ps->overlay != XCB_NONE) {
|
||||
xd->target =
|
||||
x_create_picture_with_visual_and_pixmap(ps->c, ps->vis, ps->overlay, 0, NULL);
|
||||
xd->target = x_create_picture_with_visual_and_pixmap(
|
||||
ps->c, ps->vis, ps->overlay, 0, NULL);
|
||||
xd->target_win = ps->overlay;
|
||||
} else {
|
||||
xcb_render_create_picture_value_list_t pa = {
|
||||
|
@ -411,16 +408,17 @@ static void *init(session_t *ps) {
|
|||
abort();
|
||||
}
|
||||
|
||||
xd->back_pixmap =
|
||||
x_create_pixmap(ps->c, pictfmt->depth, ps->root, ps->root_width, ps->root_height);
|
||||
xd->back = x_create_picture_with_pictfmt_and_pixmap(ps->c, pictfmt, xd->back_pixmap, 0, NULL);
|
||||
xd->back_pixmap = x_create_pixmap(ps->c, pictfmt->depth, ps->root, ps->root_width,
|
||||
ps->root_height);
|
||||
xd->back = x_create_picture_with_pictfmt_and_pixmap(ps->c, pictfmt,
|
||||
xd->back_pixmap, 0, NULL);
|
||||
|
||||
xcb_pixmap_t root_pixmap = x_get_root_back_pixmap(ps);
|
||||
if (root_pixmap == XCB_NONE) {
|
||||
xd->root_pict = solid_picture(ps, false, 1, 0.5, 0.5, 0.5);
|
||||
} else {
|
||||
xd->root_pict =
|
||||
x_create_picture_with_visual_and_pixmap(ps->c, ps->vis, root_pixmap, 0, NULL);
|
||||
xd->root_pict = x_create_picture_with_visual_and_pixmap(
|
||||
ps->c, ps->vis, root_pixmap, 0, NULL);
|
||||
}
|
||||
|
||||
if (ps->present_exists) {
|
||||
|
@ -475,8 +473,9 @@ static void present(void *backend_data, session_t *ps) {
|
|||
// To make sure rendering won't get stuck if user toggles vsync on the
|
||||
// fly.
|
||||
xcb_sync_reset_fence(ps->c, xd->idle_fence);
|
||||
xcb_present_pixmap(ps->c, xd->target_win, xd->back_pixmap, 0, XCB_NONE, XCB_NONE,
|
||||
0, 0, XCB_NONE, XCB_NONE, xd->idle_fence, 0, 0, 1, 0, 0, NULL);
|
||||
xcb_present_pixmap(ps->c, xd->target_win, xd->back_pixmap, 0, XCB_NONE,
|
||||
XCB_NONE, 0, 0, XCB_NONE, XCB_NONE, xd->idle_fence, 0,
|
||||
0, 1, 0, 0, NULL);
|
||||
} else {
|
||||
// compose() sets clip region, so clear it first to make
|
||||
// sure we update the whole screen.
|
||||
|
@ -484,8 +483,9 @@ static void present(void *backend_data, session_t *ps) {
|
|||
|
||||
// TODO buffer-age-like optimization might be possible here.
|
||||
// but that will require a different backend API
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->back, XCB_NONE, xd->target,
|
||||
0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->back, XCB_NONE,
|
||||
xd->target, 0, 0, 0, 0, 0, 0, ps->root_width,
|
||||
ps->root_height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,10 +127,6 @@
|
|||
// Window opacity / dim state changed
|
||||
#define WFLAG_OPCT_CHANGE 0x0004
|
||||
|
||||
// xcb-render specific macros
|
||||
#define XFIXED_TO_DOUBLE(value) (((double) (value)) / 65536)
|
||||
#define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t) (((double) (value)) * 65536))
|
||||
|
||||
// === Types ===
|
||||
typedef struct glx_fbconfig glx_fbconfig_t;
|
||||
|
||||
|
|
147
src/config.c
147
src/config.c
|
@ -17,6 +17,7 @@
|
|||
#include "log.h"
|
||||
#include "region.h"
|
||||
#include "types.h"
|
||||
#include "kernel.h"
|
||||
#include "win.h"
|
||||
|
||||
#include "config.h"
|
||||
|
@ -43,109 +44,116 @@ parse_long(const char *s, long *dest) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Parse a floating-point number in matrix.
|
||||
* Parse a floating-point number in from a string,
|
||||
* also strips the trailing space and comma after the number.
|
||||
*
|
||||
* @param[in] src string to parse
|
||||
* @param[out] dest return the number parsed from the string
|
||||
* @return pointer to the last character parsed
|
||||
*/
|
||||
const char *
|
||||
parse_matrix_readnum(const char *src, double *dest) {
|
||||
parse_readnum(const char *src, double *dest) {
|
||||
const char *pc = NULL;
|
||||
double val = strtod_simple(src, &pc);
|
||||
if (!pc || pc == src) {
|
||||
log_error("No number found: %s", src);
|
||||
return src;
|
||||
}
|
||||
|
||||
while (*pc && (isspace(*pc) || ',' == *pc))
|
||||
while (*pc && (isspace(*pc) || *pc == ',')) {
|
||||
++pc;
|
||||
|
||||
}
|
||||
*dest = val;
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a matrix.
|
||||
*
|
||||
* @param[in] src the blur kernel string
|
||||
* @param[out] endptr return where the end of kernel is in the string
|
||||
* @param[out] hasneg whether the kernel has negative values
|
||||
*/
|
||||
xcb_render_fixed_t *
|
||||
parse_matrix(const char *src, const char **endptr, bool *hasneg) {
|
||||
int wid = 0, hei = 0;
|
||||
conv *
|
||||
parse_blur_kern(const char *src, const char **endptr, bool *hasneg) {
|
||||
int width = 0, height = 0;
|
||||
*hasneg = false;
|
||||
|
||||
const char *pc = NULL;
|
||||
|
||||
// Get matrix width and height
|
||||
{
|
||||
double val = 0.0;
|
||||
if (src == (pc = parse_matrix_readnum(src, &val)))
|
||||
goto err1;
|
||||
src = pc;
|
||||
wid = val;
|
||||
if (src == (pc = parse_matrix_readnum(src, &val)))
|
||||
goto err1;
|
||||
src = pc;
|
||||
hei = val;
|
||||
}
|
||||
double val = 0.0;
|
||||
if (src == (pc = parse_readnum(src, &val)))
|
||||
goto err1;
|
||||
src = pc;
|
||||
width = val;
|
||||
if (src == (pc = parse_readnum(src, &val)))
|
||||
goto err1;
|
||||
src = pc;
|
||||
height = val;
|
||||
|
||||
// Validate matrix width and height
|
||||
if (wid <= 0 || hei <= 0) {
|
||||
log_error("Invalid matrix width/height.");
|
||||
if (width <= 0 || height <= 0) {
|
||||
log_error("Blue kernel width/height can't be negative.");
|
||||
goto err1;
|
||||
}
|
||||
if (!(wid % 2 && hei % 2)) {
|
||||
log_error("Width/height not odd.");
|
||||
if (!(width % 2 && height % 2)) {
|
||||
log_error("Blur kernel idth/height must be odd.");
|
||||
goto err1;
|
||||
}
|
||||
if (wid > 16 || hei > 16)
|
||||
log_warn("Matrix width/height too large, may slow down"
|
||||
if (width > 16 || height > 16)
|
||||
log_warn("Blur kernel width/height too large, may slow down"
|
||||
"rendering, and/or consume lots of memory");
|
||||
|
||||
// Allocate memory
|
||||
auto matrix = ccalloc(wid * hei + 2, xcb_render_fixed_t);
|
||||
conv *matrix = cvalloc(sizeof(conv) + width * height * sizeof(double));
|
||||
|
||||
// Read elements
|
||||
{
|
||||
int skip = hei / 2 * wid + wid / 2;
|
||||
for (int i = 0; i < wid * hei; ++i) {
|
||||
// Ignore the center element
|
||||
if (i == skip) {
|
||||
matrix[2 + i] = DOUBLE_TO_XFIXED(0);
|
||||
continue;
|
||||
}
|
||||
double val = 0;
|
||||
if (src == (pc = parse_matrix_readnum(src, &val)))
|
||||
goto err2;
|
||||
src = pc;
|
||||
if (val < 0) *hasneg = true;
|
||||
matrix[2 + i] = DOUBLE_TO_XFIXED(val);
|
||||
int skip = height / 2 * width + width / 2;
|
||||
for (int i = 0; i < width * height; ++i) {
|
||||
// Ignore the center element
|
||||
if (i == skip) {
|
||||
matrix->data[i] = 0;
|
||||
continue;
|
||||
}
|
||||
if (src == (pc = parse_readnum(src, &val))) {
|
||||
goto err2;
|
||||
}
|
||||
src = pc;
|
||||
if (val < 0) {
|
||||
*hasneg = true;
|
||||
}
|
||||
matrix->data[i] = val;
|
||||
}
|
||||
|
||||
// Detect trailing characters
|
||||
for ( ;*pc && ';' != *pc; ++pc)
|
||||
if (!isspace(*pc) && ',' != *pc) {
|
||||
log_error("Trailing characters in matrix string.");
|
||||
for (;*pc && *pc != ';'; pc++) {
|
||||
if (!isspace(*pc) && *pc != ',') {
|
||||
// TODO isspace is locale aware, be careful
|
||||
log_error("Trailing characters in blur kernel string.");
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
|
||||
// Jump over spaces after ';'
|
||||
if (';' == *pc) {
|
||||
++pc;
|
||||
while (*pc && isspace(*pc))
|
||||
if (*pc == ';') {
|
||||
pc++;
|
||||
while (*pc && isspace(*pc)) {
|
||||
++pc;
|
||||
}
|
||||
}
|
||||
|
||||
// Require an end of string if endptr is not provided, otherwise
|
||||
// copy end pointer to endptr
|
||||
if (endptr)
|
||||
if (endptr) {
|
||||
*endptr = pc;
|
||||
else if (*pc) {
|
||||
log_error("Only one matrix expected.");
|
||||
} else if (*pc) {
|
||||
log_error("Only one blur kernel expected.");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
// Fill in width and height
|
||||
matrix[0] = DOUBLE_TO_XFIXED(wid);
|
||||
matrix[1] = DOUBLE_TO_XFIXED(hei);
|
||||
|
||||
matrix->w = width;
|
||||
matrix->h = height;
|
||||
return matrix;
|
||||
|
||||
err2:
|
||||
|
@ -154,25 +162,19 @@ err1:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a convolution kernel.
|
||||
*
|
||||
* Output:
|
||||
* hasneg: whether the convolution kernel has negative values
|
||||
*/
|
||||
xcb_render_fixed_t *
|
||||
parse_conv_kern(const char *src, const char **endptr, bool *hasneg) {
|
||||
return parse_matrix(src, endptr, hasneg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a list of convolution kernels.
|
||||
*
|
||||
* Output:
|
||||
* hasneg: whether any of the convolution kernel has negative values
|
||||
* @param[in] src string to parse
|
||||
* @param[out] dest pointer to an array of kernels, must points to an array
|
||||
* of `max` elements.
|
||||
* @param[in] max maximum number of kernels supported
|
||||
* @param[out] hasneg whether any of the kernels have negative values
|
||||
* @return if the `src` string is a valid kernel list string
|
||||
*/
|
||||
bool
|
||||
parse_conv_kern_lst(const char *src, xcb_render_fixed_t **dest, int max, bool *hasneg) {
|
||||
parse_blur_kern_lst(const char *src, conv **dest, int max, bool *hasneg) {
|
||||
// TODO just return a predefined kernels, not parse predefined strings...
|
||||
static const struct {
|
||||
const char *name;
|
||||
const char *kern_str;
|
||||
|
@ -188,11 +190,11 @@ parse_conv_kern_lst(const char *src, xcb_render_fixed_t **dest, int max, bool *h
|
|||
};
|
||||
|
||||
*hasneg = false;
|
||||
|
||||
for (unsigned int i = 0;
|
||||
i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i)
|
||||
i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i) {
|
||||
if (!strcmp(CONV_KERN_PREDEF[i].name, src))
|
||||
return parse_conv_kern_lst(CONV_KERN_PREDEF[i].kern_str, dest, max, hasneg);
|
||||
return parse_blur_kern_lst(CONV_KERN_PREDEF[i].kern_str, dest, max, hasneg);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
const char *pc = src;
|
||||
|
@ -207,8 +209,11 @@ parse_conv_kern_lst(const char *src, xcb_render_fixed_t **dest, int max, bool *h
|
|||
i = 0;
|
||||
while (pc && *pc && i < max - 1) {
|
||||
bool tmp_hasneg;
|
||||
if (!(dest[i++] = parse_conv_kern(pc, &pc, &tmp_hasneg)))
|
||||
dest[i] = parse_blur_kern(pc, &pc, &tmp_hasneg);
|
||||
if (!dest[i]) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
*hasneg |= tmp_hasneg;
|
||||
}
|
||||
|
||||
|
|
10
src/config.h
10
src/config.h
|
@ -24,6 +24,7 @@
|
|||
#include "compiler.h"
|
||||
#include "win.h"
|
||||
#include "types.h"
|
||||
#include "kernel.h"
|
||||
|
||||
typedef struct session session_t;
|
||||
|
||||
|
@ -207,7 +208,7 @@ typedef struct options_t {
|
|||
/// Background blur blacklist. A linked list of conditions.
|
||||
c2_lptr_t *blur_background_blacklist;
|
||||
/// Blur convolution kernel.
|
||||
xcb_render_fixed_t *blur_kerns[MAX_BLUR_PASS];
|
||||
conv *blur_kerns[MAX_BLUR_PASS];
|
||||
/// How much to dim an inactive window. 0.0 - 1.0, 0 to disable.
|
||||
double inactive_dim;
|
||||
/// Whether to use fixed inactive dim opacity, instead of deciding
|
||||
|
@ -245,13 +246,8 @@ extern const char *const VSYNC_STRS[NUM_VSYNC + 1];
|
|||
extern const char *const BACKEND_STRS[NUM_BKEND + 1];
|
||||
|
||||
attr_warn_unused_result bool parse_long(const char *, long *);
|
||||
attr_warn_unused_result const char *parse_matrix_readnum(const char *, double *);
|
||||
attr_warn_unused_result xcb_render_fixed_t *
|
||||
parse_matrix(const char *, const char **, bool *hasneg);
|
||||
attr_warn_unused_result xcb_render_fixed_t *
|
||||
parse_conv_kern(const char *, const char **, bool *hasneg);
|
||||
attr_warn_unused_result bool
|
||||
parse_conv_kern_lst(const char *, xcb_render_fixed_t **, int, bool *hasneg);
|
||||
parse_blur_kern_lst(const char *, conv **, int, bool *hasneg);
|
||||
attr_warn_unused_result bool parse_geometry(session_t *, const char *, region_t *);
|
||||
attr_warn_unused_result bool parse_rule_opacity(c2_lptr_t **, const char *);
|
||||
|
||||
|
|
|
@ -379,7 +379,7 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
|||
&opt->blur_background_fixed);
|
||||
// --blur-kern
|
||||
if (config_lookup_string(&cfg, "blur-kern", &sval) &&
|
||||
!parse_conv_kern_lst(sval, opt->blur_kerns, MAX_BLUR_PASS, conv_kern_hasneg)) {
|
||||
!parse_blur_kern_lst(sval, opt->blur_kerns, MAX_BLUR_PASS, conv_kern_hasneg)) {
|
||||
log_fatal("Cannot parse \"blur-kern\"");
|
||||
goto err;
|
||||
}
|
||||
|
|
23
src/opengl.c
23
src/opengl.c
|
@ -23,6 +23,7 @@
|
|||
#include "utils.h"
|
||||
#include "win.h"
|
||||
#include "region.h"
|
||||
#include "kernel.h"
|
||||
#include "backend/gl/gl_common.h"
|
||||
#include "backend/gl/glx.h"
|
||||
|
||||
|
@ -357,16 +358,13 @@ glx_init_blur(session_t *ps) {
|
|||
}
|
||||
|
||||
for (int i = 0; i < MAX_BLUR_PASS && ps->o.blur_kerns[i]; ++i) {
|
||||
xcb_render_fixed_t *kern = ps->o.blur_kerns[i];
|
||||
if (!kern)
|
||||
break;
|
||||
|
||||
auto kern = ps->o.blur_kerns[i];
|
||||
glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i];
|
||||
|
||||
// Build shader
|
||||
{
|
||||
int wid = XFIXED_TO_DOUBLE(kern[0]), hei = XFIXED_TO_DOUBLE(kern[1]);
|
||||
int nele = wid * hei - 1;
|
||||
int width = kern->w, height = kern->h;
|
||||
int nele = width * height - 1;
|
||||
unsigned int len = strlen(FRAG_SHADER_BLUR_PREFIX) +
|
||||
strlen(sampler_type) +
|
||||
strlen(extension) +
|
||||
|
@ -380,15 +378,16 @@ glx_init_blur(session_t *ps) {
|
|||
assert(strlen(shader_str) < len);
|
||||
|
||||
double sum = 0.0;
|
||||
for (int j = 0; j < hei; ++j) {
|
||||
for (int k = 0; k < wid; ++k) {
|
||||
if (hei / 2 == j && wid / 2 == k)
|
||||
for (int j = 0; j < height; ++j) {
|
||||
for (int k = 0; k < width; ++k) {
|
||||
if (height / 2 == j && width / 2 == k)
|
||||
continue;
|
||||
double val = XFIXED_TO_DOUBLE(kern[2 + j * wid + k]);
|
||||
if (0.0 == val)
|
||||
double val = kern->data[j * width + k];
|
||||
if (val == 0) {
|
||||
continue;
|
||||
}
|
||||
sum += val;
|
||||
sprintf(pc, shader_add, val, texture_func, k - wid / 2, j - hei / 2);
|
||||
sprintf(pc, shader_add, val, texture_func, k - width / 2, j - height / 2);
|
||||
pc += strlen(pc);
|
||||
assert(strlen(shader_str) < len);
|
||||
}
|
||||
|
|
|
@ -711,7 +711,7 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
|||
break;
|
||||
case 301:
|
||||
// --blur-kern
|
||||
if (!parse_conv_kern_lst(optarg, opt->blur_kerns,
|
||||
if (!parse_blur_kern_lst(optarg, opt->blur_kerns,
|
||||
MAX_BLUR_PASS, &conv_kern_hasneg))
|
||||
exit(1);
|
||||
break;
|
||||
|
@ -818,35 +818,18 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
|||
|
||||
// Fill default blur kernel
|
||||
if (opt->blur_background && !opt->blur_kerns[0]) {
|
||||
// Convolution filter parameter (box blur)
|
||||
// gaussian or binomial filters are definitely superior, yet looks
|
||||
// like they aren't supported as of xorg-server-1.13.0
|
||||
static const xcb_render_fixed_t convolution_blur[] = {
|
||||
// Must convert to XFixed with DOUBLE_TO_XFIXED()
|
||||
// Matrix size
|
||||
DOUBLE_TO_XFIXED(3),
|
||||
DOUBLE_TO_XFIXED(3),
|
||||
// Matrix
|
||||
DOUBLE_TO_XFIXED(1),
|
||||
DOUBLE_TO_XFIXED(1),
|
||||
DOUBLE_TO_XFIXED(1),
|
||||
DOUBLE_TO_XFIXED(1),
|
||||
DOUBLE_TO_XFIXED(1),
|
||||
DOUBLE_TO_XFIXED(1),
|
||||
DOUBLE_TO_XFIXED(1),
|
||||
DOUBLE_TO_XFIXED(1),
|
||||
DOUBLE_TO_XFIXED(1),
|
||||
};
|
||||
opt->blur_kerns[0] = ccalloc(ARR_SIZE(convolution_blur), xcb_render_fixed_t);
|
||||
memcpy(opt->blur_kerns[0], convolution_blur, sizeof(convolution_blur));
|
||||
bool ret = parse_blur_kern_lst("3x3box", opt->blur_kerns, MAX_BLUR_PASS, &conv_kern_hasneg);
|
||||
assert(ret);
|
||||
}
|
||||
|
||||
if (opt->resize_damage < 0)
|
||||
if (opt->resize_damage < 0) {
|
||||
log_warn("Negative --resize-damage will not work correctly.");
|
||||
}
|
||||
|
||||
if (opt->backend == BKEND_XRENDER && conv_kern_hasneg)
|
||||
if (opt->backend == BKEND_XRENDER && conv_kern_hasneg) {
|
||||
log_warn("A convolution kernel with negative values may not work "
|
||||
"properly under X Render backend.");
|
||||
}
|
||||
}
|
||||
|
||||
// vim: set noet sw=8 ts=8 :
|
||||
|
|
56
src/render.c
56
src/render.c
|
@ -318,7 +318,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
|
|||
const int r = extents.right;
|
||||
|
||||
#define COMP_BDR(cx, cy, cwid, chei) \
|
||||
paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * w->opacity, \
|
||||
paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * w->opacity, \
|
||||
reg_paint, pict)
|
||||
|
||||
// Sanitize the margins, in case some broken WM makes
|
||||
|
@ -582,18 +582,6 @@ static inline void win_paint_shadow(session_t *ps, win *w, region_t *reg_paint)
|
|||
w->shadow_paint.ptex, reg_paint, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a convolution kernel.
|
||||
*/
|
||||
static inline void normalize_conv_kern(int wid, int hei, xcb_render_fixed_t *kern) {
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < wid * hei; ++i)
|
||||
sum += XFIXED_TO_DOUBLE(kern[i]);
|
||||
double factor = 1.0 / sum;
|
||||
for (int i = 0; i < wid * hei; ++i)
|
||||
kern[i] = DOUBLE_TO_XFIXED(XFIXED_TO_DOUBLE(kern[i]) * factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Blur an area on a buffer.
|
||||
*
|
||||
|
@ -630,8 +618,9 @@ static bool xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, i
|
|||
for (int i = 0; blur_kerns[i]; ++i) {
|
||||
assert(i < MAX_BLUR_PASS - 1);
|
||||
xcb_render_fixed_t *convolution_blur = blur_kerns[i];
|
||||
int kwid = XFIXED_TO_DOUBLE(convolution_blur[0]),
|
||||
khei = XFIXED_TO_DOUBLE(convolution_blur[1]);
|
||||
// `x / 65536.0` converts from X fixed point to double
|
||||
int kwid = ((double)convolution_blur[0]) / 65536.0,
|
||||
khei = ((double)convolution_blur[1]) / 65536.0;
|
||||
bool rd_from_tgt = (tgt_buffer == src_pict);
|
||||
|
||||
// Copy from source picture to destination. The filter must
|
||||
|
@ -685,7 +674,9 @@ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture
|
|||
case BKEND_XR_GLX_HYBRID: {
|
||||
// Normalize blur kernels
|
||||
for (int i = 0; i < MAX_BLUR_PASS; ++i) {
|
||||
xcb_render_fixed_t *kern_src = ps->o.blur_kerns[i];
|
||||
// Note: `x * 65536` converts double `x` to a X fixed point
|
||||
// representation. `x / 65536` is the other way.
|
||||
auto kern_src = ps->o.blur_kerns[i];
|
||||
xcb_render_fixed_t *kern_dst = ps->blur_kerns_cache[i];
|
||||
assert(i < MAX_BLUR_PASS);
|
||||
if (!kern_src) {
|
||||
|
@ -693,30 +684,33 @@ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture
|
|||
break;
|
||||
}
|
||||
|
||||
assert(!kern_dst ||
|
||||
(kern_src[0] == kern_dst[0] && kern_src[1] == kern_dst[1]));
|
||||
assert(!kern_dst || (kern_src->w == kern_dst[0] / 65536 &&
|
||||
kern_src->h == kern_dst[1] / 65536));
|
||||
|
||||
// Skip for fixed factor_center if the cache exists already
|
||||
if (ps->o.blur_background_fixed && kern_dst)
|
||||
continue;
|
||||
|
||||
int kwid = XFIXED_TO_DOUBLE(kern_src[0]),
|
||||
khei = XFIXED_TO_DOUBLE(kern_src[1]);
|
||||
|
||||
// Allocate cache space if needed
|
||||
if (!kern_dst) {
|
||||
kern_dst = ccalloc(kwid * khei + 2, xcb_render_fixed_t);
|
||||
kern_dst = ccalloc(kern_src->w * kern_src->h + 2,
|
||||
xcb_render_fixed_t);
|
||||
ps->blur_kerns_cache[i] = kern_dst;
|
||||
}
|
||||
|
||||
double sum = factor_center;
|
||||
for (int j = 0; j < kern_src->w * kern_src->h; j++) {
|
||||
sum += kern_src->data[j];
|
||||
}
|
||||
// Copy src to dst, normalizing in the process
|
||||
for (int j = 0; j < kern_src->w * kern_src->h; j++) {
|
||||
kern_dst[j + 2] = kern_src->data[j] / sum * 65536;
|
||||
}
|
||||
// Modify the factor of the center pixel
|
||||
kern_src[2 + (khei / 2) * kwid + kwid / 2] =
|
||||
DOUBLE_TO_XFIXED(factor_center);
|
||||
|
||||
// Copy over
|
||||
memcpy(kern_dst, kern_src,
|
||||
(kwid * khei + 2) * sizeof(xcb_render_fixed_t));
|
||||
normalize_conv_kern(kwid, khei, kern_dst + 2);
|
||||
kern_dst[2 + (kern_src->h / 2) * kern_src->w + kern_src->w / 2] =
|
||||
factor_center / sum * 65536;
|
||||
kern_dst[0] = kern_src->w * 65536;
|
||||
kern_dst[1] = kern_src->h * 65536;
|
||||
}
|
||||
|
||||
// Minimize the region we try to blur, if the window itself is not
|
||||
|
@ -1017,8 +1011,8 @@ void paint_all(session_t *ps, win *const t, bool ignore_damage) {
|
|||
glFlush();
|
||||
glXWaitX();
|
||||
assert(ps->tgt_buffer.pixmap);
|
||||
paint_bind_tex(ps, &ps->tgt_buffer, ps->root_width, ps->root_height, false,
|
||||
ps->depth, ps->vis, !ps->o.glx_no_rebind_pixmap);
|
||||
paint_bind_tex(ps, &ps->tgt_buffer, ps->root_width, ps->root_height,
|
||||
false, ps->depth, ps->vis, !ps->o.glx_no_rebind_pixmap);
|
||||
if (ps->o.vsync_use_glfinish)
|
||||
glFinish();
|
||||
else
|
||||
|
|
27
src/x.c
27
src/x.c
|
@ -17,6 +17,7 @@
|
|||
#include "region.h"
|
||||
#include "compiler.h"
|
||||
#include "common.h"
|
||||
#include "kernel.h"
|
||||
#include "x.h"
|
||||
#include "log.h"
|
||||
#include "backend/gl/glx.h"
|
||||
|
@ -513,3 +514,29 @@ bool x_fence_sync(xcb_connection_t *c, xcb_sync_fence_t f) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// xcb-render specific macros
|
||||
#define XFIXED_TO_DOUBLE(value) (((double) (value)) / 65536)
|
||||
#define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t) (((double) (value)) * 65536))
|
||||
|
||||
/**
|
||||
* Set the picture filter of a xrender picture to a convolution
|
||||
* kernel.
|
||||
*
|
||||
* @param c xcb connection
|
||||
* @param pict the picture
|
||||
* @param kern the convolution kernel
|
||||
*/
|
||||
void
|
||||
x_set_picture_convolution_kernel(xcb_connection_t *c,
|
||||
xcb_render_picture_t pict, conv *kernel) {
|
||||
auto buf = ccalloc(kernel->w * kernel->h + 2, xcb_render_fixed_t);
|
||||
static const char *filter = "convolution";
|
||||
buf[0] = DOUBLE_TO_XFIXED(kernel->w);
|
||||
buf[1] = DOUBLE_TO_XFIXED(kernel->h);
|
||||
for (int i = 0; i < kernel->w * kernel->h; i++) {
|
||||
buf[i + 2] = DOUBLE_TO_XFIXED(kernel->data[i]);
|
||||
}
|
||||
xcb_render_set_picture_filter(c, pict, sizeof(filter), filter, kernel->w * kernel->h + 2, buf);
|
||||
free(buf);
|
||||
}
|
||||
|
|
13
src/x.h
13
src/x.h
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "compiler.h"
|
||||
#include "region.h"
|
||||
#include "kernel.h"
|
||||
|
||||
typedef struct session session_t;
|
||||
|
||||
|
@ -167,3 +168,15 @@ xcb_pixmap_t x_get_root_back_pixmap(session_t *ps);
|
|||
bool x_is_root_back_pixmap_atom(session_t *ps, xcb_atom_t atom);
|
||||
|
||||
bool x_fence_sync(xcb_connection_t *, xcb_sync_fence_t);
|
||||
|
||||
/**
|
||||
* Set the picture filter of a xrender picture to a convolution
|
||||
* kernel.
|
||||
*
|
||||
* @param c xcb connection
|
||||
* @param pict the picture
|
||||
* @param kern the convolution kernel
|
||||
*/
|
||||
void
|
||||
x_set_picture_convolution_kernel(xcb_connection_t *c,
|
||||
xcb_render_picture_t pict, conv *kernel);
|
||||
|
|
Loading…
Reference in New Issue