Improvement: --blur-kern
- Add blur convolution kernel customization, --blur-kern. The format is a bit tricky so be sure to read the description in `compton -h`. Not much tests received. - GLX backend: Tolerate missing GLSL uniforms for strangely shaped convolution kernel. - Fix a memory leak that blur-background blacklist is not freed.
This commit is contained in:
parent
4a4f857cc2
commit
2e6fb0203d
2
Makefile
2
Makefile
|
@ -80,7 +80,7 @@ else
|
|||
export LD_ALTEXEC = /usr/bin/ld.gold
|
||||
OBJS += backtrace-symbols.o
|
||||
LIBS += -lbfd
|
||||
CFLAGS += -ggdb
|
||||
CFLAGS += -ggdb -Wshadow
|
||||
# CFLAGS += -Weverything -Wno-disabled-macro-expansion -Wno-padded -Wno-gnu
|
||||
endif
|
||||
|
||||
|
|
|
@ -483,6 +483,8 @@ typedef struct {
|
|||
bool blur_background_fixed;
|
||||
/// Background blur blacklist. A linked list of conditions.
|
||||
c2_lptr_t *blur_background_blacklist;
|
||||
/// Blur convolution kernel.
|
||||
XFixed *blur_kern;
|
||||
/// 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
|
||||
|
|
193
src/compton.c
193
src/compton.c
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
#include "compton.h"
|
||||
#include <ctype.h>
|
||||
|
||||
// === Global constants ===
|
||||
|
||||
|
@ -1348,21 +1349,6 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
|
|||
switch (ps->o.backend) {
|
||||
case BKEND_XRENDER:
|
||||
{
|
||||
const static int convolution_blur_size = 3;
|
||||
// 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
|
||||
XFixed convolution_blur[] = {
|
||||
// Must convert to XFixed with XDoubleToFixed()
|
||||
// Matrix size
|
||||
XDoubleToFixed(convolution_blur_size),
|
||||
XDoubleToFixed(convolution_blur_size),
|
||||
// Matrix
|
||||
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
|
||||
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
|
||||
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
|
||||
};
|
||||
|
||||
// Directly copying from tgt_buffer does not work, so we create a
|
||||
// Picture in the middle.
|
||||
Picture tmp_picture = win_build_picture(ps, w, NULL);
|
||||
|
@ -1370,7 +1356,12 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
|
|||
if (!tmp_picture)
|
||||
return;
|
||||
|
||||
convolution_blur[2 + convolution_blur_size + ((convolution_blur_size - 1) / 2)] = XDoubleToFixed(factor_center);
|
||||
XFixed *convolution_blur = ps->o.blur_kern;
|
||||
int kwid = XFixedToDouble((ps->o.blur_kern[0])),
|
||||
khei = XFixedToDouble((ps->o.blur_kern[1]));
|
||||
|
||||
// Modify the factor of the center pixel
|
||||
convolution_blur[2 + (khei / 2) * kwid + kwid / 2] = XDoubleToFixed(factor_center);
|
||||
|
||||
// Minimize the region we try to blur, if the window itself is not
|
||||
// opaque, only the frame is.
|
||||
|
@ -1386,7 +1377,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
|
|||
// Copy the content to tmp_picture, then copy back. The filter must
|
||||
// be applied on tgt_buffer, to get the nearby pixels outside the
|
||||
// window.
|
||||
XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed));
|
||||
XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, convolution_blur, kwid * khei + 2);
|
||||
XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, x, y, 0, 0, 0, 0, wid, hei);
|
||||
xrfilter_reset(ps, tgt_buffer);
|
||||
XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
|
||||
|
@ -1665,10 +1656,18 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
|
|||
XFixesSetPictureClipRegion(ps->dpy, ps->tgt_picture, 0, 0, region);
|
||||
|
||||
#ifdef MONITOR_REPAINT
|
||||
XRenderComposite(
|
||||
ps->dpy, PictOpSrc, ps->black_picture, None,
|
||||
ps->tgt_picture, 0, 0, 0, 0, 0, 0,
|
||||
ps->root_width, ps->root_height);
|
||||
switch (ps->o.backend) {
|
||||
case BKEND_XRENDER:
|
||||
XRenderComposite(ps->dpy, PictOpSrc, ps->black_picture, None,
|
||||
ps->tgt_picture, 0, 0, 0, 0, 0, 0,
|
||||
ps->root_width, ps->root_height);
|
||||
break;
|
||||
case BKEND_GLX:
|
||||
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (t && t->reg_ignore) {
|
||||
|
@ -4234,6 +4233,14 @@ usage(void) {
|
|||
"--blur-background-fixed\n"
|
||||
" Use fixed blur strength instead of adjusting according to window\n"
|
||||
" opacity.\n"
|
||||
"--blur-kern matrix\n"
|
||||
" Specify the blur convolution kernel, with the following format:\n"
|
||||
" WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...\n"
|
||||
" The element in the center must not be included, it will be forever\n"
|
||||
" 1.0 or changing based on opacity, depending on whether you have\n"
|
||||
" --blur-background-fixed.\n"
|
||||
" A 7x7 Guassian blur kernel looks like:\n"
|
||||
" --blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003'\n"
|
||||
"--blur-background-exclude condition\n"
|
||||
" Exclude conditions for background blur.\n"
|
||||
"--invert-color-include condition\n"
|
||||
|
@ -4476,10 +4483,113 @@ open_config_file(char *cpath, char **ppath) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a floating-point number in matrix.
|
||||
*/
|
||||
static inline const char *
|
||||
parse_matrix_readnum(const char *src, double *dest) {
|
||||
char *pc = NULL;
|
||||
double val = strtod(src, &pc);
|
||||
if (!pc || pc == src) {
|
||||
printf_errf("(\"%s\"): No number found.", src);
|
||||
return src;
|
||||
}
|
||||
|
||||
while (*pc && (isspace(*pc) || ',' == *pc))
|
||||
++pc;
|
||||
|
||||
*dest = val;
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a matrix.
|
||||
*/
|
||||
static inline XFixed *
|
||||
parse_matrix(session_t *ps, const char *src) {
|
||||
int wid = 0, hei = 0;
|
||||
const char *pc = NULL;
|
||||
XFixed *matrix = NULL;
|
||||
|
||||
// Get matrix width and height
|
||||
{
|
||||
double val = 0.0;
|
||||
if (src == (pc = parse_matrix_readnum(src, &val)))
|
||||
goto parse_matrix_err;
|
||||
src = pc;
|
||||
wid = val;
|
||||
if (src == (pc = parse_matrix_readnum(src, &val)))
|
||||
goto parse_matrix_err;
|
||||
src = pc;
|
||||
hei = val;
|
||||
}
|
||||
|
||||
// Validate matrix width and height
|
||||
if (wid <= 0 || hei <= 0) {
|
||||
printf_errf("(): Invalid matrix width/height.");
|
||||
goto parse_matrix_err;
|
||||
}
|
||||
if (!(wid % 2 && hei % 2)) {
|
||||
printf_errf("(): Width/height not odd.");
|
||||
goto parse_matrix_err;
|
||||
}
|
||||
if (wid > 16 || hei > 16) {
|
||||
printf_errf("(): Matrix width/height too large.");
|
||||
goto parse_matrix_err;
|
||||
}
|
||||
|
||||
// Allocate memory
|
||||
matrix = calloc(wid * hei + 2, sizeof(XFixed));
|
||||
if (!matrix) {
|
||||
printf_errf("(): Failed to allocate memory for matrix.");
|
||||
goto parse_matrix_err;
|
||||
}
|
||||
|
||||
// Read elements
|
||||
{
|
||||
int skip = hei / 2 * wid + wid / 2;
|
||||
bool hasneg = false;
|
||||
for (int i = 0; i < wid * hei; ++i) {
|
||||
// Ignore the center element
|
||||
if (i == skip) {
|
||||
matrix[2 + i] = XDoubleToFixed(0);
|
||||
continue;
|
||||
}
|
||||
double val = 0;
|
||||
if (src == (pc = parse_matrix_readnum(src, &val)))
|
||||
goto parse_matrix_err;
|
||||
src = pc;
|
||||
if (val < 0) hasneg = true;
|
||||
matrix[2 + i] = XDoubleToFixed(val);
|
||||
}
|
||||
if (BKEND_XRENDER == ps->o.backend && hasneg)
|
||||
printf_errf("(): A convolution kernel with negative values "
|
||||
"may not work properly under X Render backend.");
|
||||
}
|
||||
|
||||
// Detect trailing characters
|
||||
for ( ;*pc; ++pc)
|
||||
if (!isspace(*pc) && ',' != *pc) {
|
||||
printf_errf("(): Trailing characters in matrix string.");
|
||||
goto parse_matrix_err;
|
||||
}
|
||||
|
||||
// Fill in width and height
|
||||
matrix[0] = XDoubleToFixed(wid);
|
||||
matrix[1] = XDoubleToFixed(hei);
|
||||
|
||||
return matrix;
|
||||
|
||||
parse_matrix_err:
|
||||
free(matrix);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a condition list in configuration file.
|
||||
*/
|
||||
static void
|
||||
static inline void
|
||||
parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst,
|
||||
const char *name) {
|
||||
config_setting_t *setting = config_lookup(pcfg, name);
|
||||
|
@ -4756,6 +4866,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
|
|||
{ "glx-no-rebind-pixmap", no_argument, NULL, 298 },
|
||||
{ "glx-swap-method", required_argument, NULL, 299 },
|
||||
{ "fade-exclude", required_argument, NULL, 300 },
|
||||
{ "blur-kern", required_argument, NULL, 301 },
|
||||
// Must terminate with a NULL entry
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
@ -4979,6 +5090,12 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
|
|||
// --fade-exclude
|
||||
condlst_add(ps, &ps->o.fade_blacklist, optarg);
|
||||
break;
|
||||
case 301:
|
||||
// --blur-kern
|
||||
free(ps->o.blur_kern);
|
||||
if (!(ps->o.blur_kern = parse_matrix(ps, optarg)))
|
||||
exit(1);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
|
@ -5042,6 +5159,29 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
|
|||
ps->o.track_leader = true;
|
||||
}
|
||||
|
||||
// Fill default blur kernel
|
||||
if (ps->o.blur_background && !ps->o.blur_kern) {
|
||||
const static int convolution_blur_size = 3;
|
||||
// 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
|
||||
const static XFixed convolution_blur[] = {
|
||||
// Must convert to XFixed with XDoubleToFixed()
|
||||
// Matrix size
|
||||
XDoubleToFixed(convolution_blur_size),
|
||||
XDoubleToFixed(convolution_blur_size),
|
||||
// Matrix
|
||||
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
|
||||
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
|
||||
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
|
||||
};
|
||||
ps->o.blur_kern = malloc(sizeof(convolution_blur));
|
||||
if (!ps->o.blur_kern) {
|
||||
printf_errf("(): Failed to allocate memory for convolution kernel.");
|
||||
exit(1);
|
||||
}
|
||||
memcpy(ps->o.blur_kern, &convolution_blur, sizeof(convolution_blur));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5864,6 +6004,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
|
|||
.blur_background_frame = false,
|
||||
.blur_background_fixed = false,
|
||||
.blur_background_blacklist = NULL,
|
||||
.blur_kern = NULL,
|
||||
.inactive_dim = 0.0,
|
||||
.inactive_dim_fixed = false,
|
||||
.invert_color_list = NULL,
|
||||
|
@ -5929,6 +6070,12 @@ session_init(session_t *ps_old, int argc, char **argv) {
|
|||
.glXWaitVideoSyncSGI = NULL,
|
||||
.glXGetSyncValuesOML = NULL,
|
||||
.glXWaitForMscOML = NULL,
|
||||
|
||||
#ifdef CONFIG_VSYNC_OPENGL_GLSL
|
||||
.glx_prog_blur_unifm_offset_x = -1,
|
||||
.glx_prog_blur_unifm_offset_y = -1,
|
||||
.glx_prog_blur_unifm_factor_center = -1,
|
||||
#endif
|
||||
#endif
|
||||
|
||||
.xfixes_event = 0,
|
||||
|
@ -6277,6 +6424,7 @@ session_destroy(session_t *ps) {
|
|||
free_wincondlst(&ps->o.fade_blacklist);
|
||||
free_wincondlst(&ps->o.focus_blacklist);
|
||||
free_wincondlst(&ps->o.invert_color_list);
|
||||
free_wincondlst(&ps->o.blur_background_blacklist);
|
||||
#endif
|
||||
|
||||
// Free tracked atom list
|
||||
|
@ -6338,6 +6486,7 @@ session_destroy(session_t *ps) {
|
|||
free(ps->o.display);
|
||||
free(ps->o.logpath);
|
||||
free(ps->o.config_file);
|
||||
free(ps->o.blur_kern);
|
||||
free(ps->pfds_read);
|
||||
free(ps->pfds_write);
|
||||
free(ps->pfds_except);
|
||||
|
|
88
src/opengl.c
88
src/opengl.c
|
@ -191,28 +191,60 @@ bool
|
|||
glx_init_blur(session_t *ps) {
|
||||
#ifdef CONFIG_VSYNC_OPENGL_GLSL
|
||||
// Build shader
|
||||
static const char *FRAG_SHADER_BLUR =
|
||||
"#version 110\n"
|
||||
"uniform float offset_x;\n"
|
||||
"uniform float offset_y;\n"
|
||||
"uniform float factor_center;\n"
|
||||
"uniform sampler2D tex_scr;\n"
|
||||
"\n"
|
||||
"void main() {\n"
|
||||
" vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n"
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y - offset_y));\n"
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y));\n"
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y + offset_y));\n"
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y - offset_y));\n"
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n"
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + offset_y));\n"
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y - offset_y));\n"
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y));\n"
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y + offset_y));\n"
|
||||
" gl_FragColor = sum / (factor_center + 8.0);\n"
|
||||
"}\n"
|
||||
;
|
||||
ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR);
|
||||
{
|
||||
static const char *FRAG_SHADER_BLUR_PREFIX =
|
||||
"#version 110\n"
|
||||
"uniform float offset_x;\n"
|
||||
"uniform float offset_y;\n"
|
||||
"uniform float factor_center;\n"
|
||||
"uniform sampler2D tex_scr;\n"
|
||||
"\n"
|
||||
"void main() {\n"
|
||||
" vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n";
|
||||
static const char *FRAG_SHADER_BLUR_ADD =
|
||||
" sum += float(%.7g) * texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x * float(%d), gl_TexCoord[0].y + offset_y * float(%d)));\n";
|
||||
static const char *FRAG_SHADER_BLUR_SUFFIX =
|
||||
" sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n"
|
||||
" gl_FragColor = sum / (factor_center + float(%.7g));\n"
|
||||
"}\n";
|
||||
int wid = XFixedToDouble(ps->o.blur_kern[0]), hei = XFixedToDouble(ps->o.blur_kern[1]);
|
||||
int nele = wid * hei - 1;
|
||||
int len = strlen(FRAG_SHADER_BLUR_PREFIX) + (strlen(FRAG_SHADER_BLUR_ADD) + 42) * nele
|
||||
+ strlen(FRAG_SHADER_BLUR_SUFFIX) + 12 + 1;
|
||||
char *shader_str = calloc(len, sizeof(char));
|
||||
if (!shader_str) {
|
||||
printf_errf("(): Failed to allocate %d bytes for shader string.", len);
|
||||
return false;
|
||||
}
|
||||
{
|
||||
char *pc = shader_str;
|
||||
strcpy(pc, FRAG_SHADER_BLUR_PREFIX);
|
||||
pc += strlen(FRAG_SHADER_BLUR_PREFIX);
|
||||
assert(strlen(shader_str) < len);
|
||||
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < hei; ++i) {
|
||||
for (int j = 0; j < wid; ++j) {
|
||||
if (hei / 2 == i && wid / 2 == j)
|
||||
continue;
|
||||
double val = XFixedToDouble(ps->o.blur_kern[2 + i * wid + j]);
|
||||
sum += val;
|
||||
sprintf(pc, FRAG_SHADER_BLUR_ADD, val, j - wid / 2, i - hei / 2);
|
||||
pc += strlen(pc);
|
||||
assert(strlen(shader_str) < len);
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, sum);
|
||||
assert(strlen(shader_str) < len);
|
||||
#ifdef DEBUG_GLX_GLSL
|
||||
fputs(shader_str, stdout);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, shader_str);
|
||||
}
|
||||
|
||||
if (!ps->glx_frag_shader_blur) {
|
||||
printf_errf("(): Failed to create fragment shader.");
|
||||
return false;
|
||||
|
@ -227,8 +259,7 @@ glx_init_blur(session_t *ps) {
|
|||
#define P_GET_UNIFM_LOC(name, target) { \
|
||||
ps->target = glGetUniformLocation(ps->glx_prog_blur, name); \
|
||||
if (ps->target < 0) { \
|
||||
printf_errf("(): Failed to get location of uniform '" name "'."); \
|
||||
return false; \
|
||||
printf_errf("(): Failed to get location of uniform '" name "'. Might be troublesome."); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
@ -759,9 +790,12 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
|
|||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
#ifdef CONFIG_VSYNC_OPENGL_GLSL
|
||||
glUseProgram(ps->glx_prog_blur);
|
||||
glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / width);
|
||||
glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / height);
|
||||
glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center);
|
||||
if (ps->glx_prog_blur_unifm_offset_x >= 0)
|
||||
glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / width);
|
||||
if (ps->glx_prog_blur_unifm_offset_y >= 0)
|
||||
glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / height);
|
||||
if (ps->glx_prog_blur_unifm_factor_center >= 0)
|
||||
glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center);
|
||||
#endif
|
||||
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue