Refactor shadow generation

Trying to make the code easier to understand. The logic is unchanged.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-01-01 00:15:51 +00:00
parent edb1139507
commit 4aeffa36b8
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
6 changed files with 298 additions and 330 deletions

View File

@ -2,8 +2,8 @@
#include "backend.h" #include "backend.h"
#include "backend_common.h" #include "backend_common.h"
#include "x.h"
#include "common.h" #include "common.h"
#include "x.h"
/** /**
* Generate a 1x1 <code>Picture</code> of a particular color. * Generate a 1x1 <code>Picture</code> of a particular color.
@ -46,20 +46,31 @@ solid_picture(session_t *ps, bool argb, double a, double r, double g, double b)
return picture; return picture;
} }
static xcb_image_t *make_shadow(session_t *ps, double opacity, int width, int height) { static xcb_image_t *
make_shadow(xcb_connection_t *c, const conv *kernel, const double *shadow_sum,
double opacity, int width, int height) {
/*
* We classify shadows into 4 kinds of regions
* r = shadow radius
* (0, 0) is the top left of the window itself
* -r r width-r width+r
* -r +-----+---------+-----+
* | 1 | 2 | 1 |
* r +-----+---------+-----+
* | 2 | 3 | 2 |
* height-r +-----+---------+-----+
* | 1 | 2 | 1 |
* height+r +-----+---------+-----+
*/
xcb_image_t *ximage; xcb_image_t *ximage;
int ylimit, xlimit; int d = kernel->size, r = d / 2;
int swidth = width + ps->cgsize; int swidth = width + r * 2, sheight = height + r * 2;
int sheight = height + ps->cgsize;
int center = ps->cgsize / 2;
int x, y;
unsigned char d;
int x_diff;
int opacity_int = (int)(opacity * 25);
ximage = xcb_image_create_native(ps->c, swidth, sheight, assert(d % 2 == 1);
XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 0, 0, NULL); assert(d > 0);
ximage = xcb_image_create_native(c, swidth, sheight, XCB_IMAGE_FORMAT_Z_PIXMAP, 8,
0, 0, NULL);
if (!ximage) { if (!ximage) {
log_error("failed to create an X image"); log_error("failed to create an X image");
return 0; return 0;
@ -68,92 +79,91 @@ static xcb_image_t *make_shadow(session_t *ps, double opacity, int width, int he
unsigned char *data = ximage->data; unsigned char *data = ximage->data;
uint32_t sstride = ximage->stride; uint32_t sstride = ximage->stride;
/* // If the window body is smaller than the kernel, we do convolution directly
* Build the gaussian in sections if (width < r * 2 && height < r * 2) {
*/ for (int y = 0; y < sheight; y++) {
for (int x = 0; x < swidth; x++) {
/* double sum = sum_kernel_normalized(
* center (fill the complete data array) kernel, d - x - 1, d - y - 1, width, height);
*/ data[y * sstride + x] = sum * 255.0;
// XXX If the center part of the shadow would be entirely covered by
// the body of the window, we shouldn't need to fill the center here.
// XXX In general, we want to just fill the part that is not behind
// the window, in order to reduce CPU load and make transparent window
// look correct
if (ps->cgsize > 0) {
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + ps->cgsize];
} else {
d = (unsigned char)(sum_kernel(ps->gaussian_map, center, center, width,
height) *
opacity * 255.0);
}
memset(data, d, sheight * swidth);
/*
* corners
*/
ylimit = ps->cgsize;
if (ylimit > sheight / 2)
ylimit = (sheight + 1) / 2;
xlimit = ps->cgsize;
if (xlimit > swidth / 2)
xlimit = (swidth + 1) / 2;
for (y = 0; y < ylimit; y++) {
for (x = 0; x < xlimit; x++) {
if (xlimit == ps->cgsize && ylimit == ps->cgsize) {
d = ps->shadow_corner[opacity_int * (ps->cgsize + 1) *
(ps->cgsize + 1) +
y * (ps->cgsize + 1) + x];
} else {
d = (unsigned char)(sum_kernel(ps->gaussian_map, x - center,
y - center, width, height) *
opacity * 255.0);
} }
data[y * sstride + x] = d;
data[(sheight - y - 1) * sstride + x] = d;
data[(sheight - y - 1) * sstride + (swidth - x - 1)] = d;
data[y * sstride + (swidth - x - 1)] = d;
} }
return ximage;
} }
/* if (height < r * 2) {
* top/bottom // If the window height is smaller than the kernel, we divide
*/ // the window like this:
// -r r width-r width+r
x_diff = swidth - (ps->cgsize * 2); // +------+-------------+------+
if (x_diff > 0 && ylimit > 0) { // | | | |
for (y = 0; y < ylimit; y++) { // +------+-------------+------+
if (ylimit == ps->cgsize) { for (int y = 0; y < sheight; y++) {
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + y]; for (int x = 0; x < r * 2; x++) {
} else { double sum = sum_kernel_normalized(kernel, d - x - 1,
d = (unsigned char)(sum_kernel(ps->gaussian_map, center, d - y - 1, d, height) *
y - center, width, height) * 255.0;
opacity * 255.0); data[y * sstride + x] = sum;
data[y * sstride + swidth - x - 1] = sum;
} }
memset(&data[y * sstride + ps->cgsize], d, x_diff); }
memset(&data[(sheight - y - 1) * sstride + ps->cgsize], d, x_diff); for (int y = 0; y < sheight; y++) {
double sum =
sum_kernel_normalized(kernel, 0, d - y - 1, d, height) * 255.0;
memset(&data[y * sstride + r * 2], sum, width - 2 * r);
}
return ximage;
}
if (width < r * 2) {
// Similarly, for width smaller than kernel
for (int y = 0; y < r * 2; y++) {
for (int x = 0; x < swidth; x++) {
double sum = sum_kernel_normalized(kernel, d - x - 1,
d - y - 1, width, d) *
255.0;
data[y * sstride + x] = sum;
data[(sheight - y - 1) * sstride + x] = sum;
}
}
for (int x = 0; x < swidth; x++) {
double sum =
sum_kernel_normalized(kernel, d - x - 1, 0, width, d) * 255.0;
for (int y = r * 2; y < height; y++) {
data[y * sstride + x] = sum;
}
}
return ximage;
}
// Fill part 3
for (int y = r; y < height + r; y++) {
memset(data + sstride * y + r, 255, width);
}
// Part 1
for (int y = 0; y < r * 2; y++) {
for (int x = 0; x < r * 2; x++) {
double tmpsum = shadow_sum[y * d + x] * opacity * 255.0;
data[y * sstride + x] = tmpsum;
data[(sheight - y - 1) * sstride + x] = tmpsum;
data[(sheight - y - 1) * sstride + (swidth - x - 1)] = tmpsum;
data[y * sstride + (swidth - x - 1)] = tmpsum;
} }
} }
/* // Part 2, top/bottom
* sides for (int y = 0; y < r * 2; y++) {
*/ double tmpsum = shadow_sum[d * y + d - 1] * opacity * 255.0;
memset(&data[y * sstride + r * 2], tmpsum, width - r * 2);
memset(&data[(sheight - y - 1) * sstride + r * 2], tmpsum, width - r * 2);
}
for (x = 0; x < xlimit; x++) { // Part 2, left/right
if (xlimit == ps->cgsize) { for (int x = 0; x < r * 2; x++) {
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + x]; double tmpsum = shadow_sum[d * (d - 1) + x] * opacity * 255.0;
} else { for (int y = r * 2; y < height; y++) {
d = (unsigned char)(sum_kernel(ps->gaussian_map, x - center, data[y * sstride + x] = tmpsum;
center, width, height) * data[y * sstride + (swidth - x - 1)] = tmpsum;
opacity * 255.0);
}
for (y = ps->cgsize; y < sheight - ps->cgsize; y++) {
data[y * sstride + x] = d;
data[y * sstride + (swidth - x - 1)] = d;
} }
} }
@ -171,7 +181,8 @@ bool build_shadow(session_t *ps, double opacity, const int width, const int heig
xcb_render_picture_t shadow_picture = None, shadow_picture_argb = None; xcb_render_picture_t shadow_picture = None, shadow_picture_argb = None;
xcb_gcontext_t gc = None; xcb_gcontext_t gc = None;
shadow_image = make_shadow(ps, opacity, width, height); shadow_image =
make_shadow(ps->c, ps->gaussian_map, ps->shadow_sum, opacity, width, height);
if (!shadow_image) { if (!shadow_image) {
log_error("Failed to make shadow"); log_error("Failed to make shadow");
return false; return false;

View File

@ -531,12 +531,8 @@ typedef struct session {
/// Gaussian map of shadow. /// Gaussian map of shadow.
conv *gaussian_map; conv *gaussian_map;
// for shadow precomputation // for shadow precomputation
/// Shadow depth on one side. /// Pre-computed table for shadow.
int cgsize; double *shadow_sum;
/// Pre-computed color table for corners of shadow.
unsigned char *shadow_corner;
/// Pre-computed color table for a side of shadow.
unsigned char *shadow_top;
/// A region in which shadow is not painted on. /// A region in which shadow is not painted on.
region_t shadow_exclude_reg; region_t shadow_exclude_reg;

View File

@ -2640,9 +2640,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
.cshadow_picture = XCB_NONE, .cshadow_picture = XCB_NONE,
.white_picture = XCB_NONE, .white_picture = XCB_NONE,
.gaussian_map = NULL, .gaussian_map = NULL,
.cgsize = 0, .shadow_sum = NULL,
.shadow_corner = NULL,
.shadow_top = NULL,
.refresh_rate = 0, .refresh_rate = 0,
.refresh_intv = 0UL, .refresh_intv = 0UL,

View File

@ -6,75 +6,46 @@
#include "kernel.h" #include "kernel.h"
#include "utils.h" #include "utils.h"
/* /// Sum a region convolution kernel. Region is defined by a width x height rectangle whose
* A picture will help /// top left corner is at (x, y)
* double sum_kernel(const conv *map, int x, int y, int width, int height) {
* -center 0 width width+center double ret = 0;
* -center +-----+-------------------+-----+
* | | | |
* | | | |
* 0 +-----+-------------------+-----+
* | | | |
* | | | |
* | | | |
* height +-----+-------------------+-----+
* | | | |
* height+ | | | |
* center +-----+-------------------+-----+
*/
double sum_kernel(const conv *map, int x, int y, int width,
int height) {
int fx, fy;
const double *g_data;
const double *g_line = map->data;
int g_size = map->size;
int center = g_size / 2;
int fx_start, fx_end;
int fy_start, fy_end;
double v;
/* /*
* Compute set of filter values which are "in range", * Compute set of filter values which are "in range"
* that's the set with:
* 0 <= x + (fx-center) && x + (fx-center) < width &&
* 0 <= y + (fy-center) && y + (fy-center) < height
*
* 0 <= x + (fx - center) x + fx - center < width
* center - x <= fx fx < width + center - x
*/ */
fx_start = center - x; int xstart = x;
if (fx_start < 0) if (xstart < 0)
fx_start = 0; xstart = 0;
fx_end = width + center - x; int xend = width + x;
if (fx_end > g_size) if (xend > map->size)
fx_end = g_size; xend = map->size;
fy_start = center - y; int ystart = y;
if (fy_start < 0) if (ystart < 0)
fy_start = 0; ystart = 0;
fy_end = height + center - y; int yend = height + y;
if (fy_end > g_size) if (yend > map->size)
fy_end = g_size; yend = map->size;
g_line = g_line + fy_start * g_size + fx_start; for (int yi = ystart; yi < yend; yi++) {
for (int xi = xstart; xi < xend; xi++) {
v = 0; ret += map->data[yi * map->size + xi];
for (fy = fy_start; fy < fy_end; fy++) {
g_data = g_line;
g_line += g_size;
for (fx = fx_start; fx < fx_end; fx++) {
v += *g_data++;
} }
} }
if (v > 1) return ret;
v = 1; }
return v; double sum_kernel_normalized(const conv *map, int x, int y, int width, int height) {
double ret = sum_kernel(map, x, y, width, height);
if (ret < 0) {
ret = 0;
}
if (ret > 1) {
ret = 1;
}
return ret;
} }
static double attr_const gaussian(double r, double x, double y) { static double attr_const gaussian(double r, double x, double y) {
@ -113,4 +84,29 @@ conv *gaussian_kernel(double r) {
return c; return c;
} }
/// preprocess kernels to make shadow generation faster
/// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive
void shadow_preprocess(conv *map, double **shadow_sum) {
const int d = map->size;
if (*shadow_sum)
free(*shadow_sum);
auto sum = *shadow_sum = ccalloc(d * d, double);
sum[0] = map->data[0];
for (int x = 1; x < d; x++) {
sum[x] = sum[x - 1] + map->data[x];
}
for (int y = 1; y < d; y++) {
sum[y * d] = sum[(y - 1) * d] + map->data[y * d];
for (int x = 1; x < d; x++) {
double tmp = sum[(y - 1) * d + x] + sum[y * d + x - 1] -
sum[(y - 1) * d + x - 1];
sum[y * d + x] = tmp + map->data[y * d + x];
}
}
}
// vim: set noet sw=8 ts=8 : // vim: set noet sw=8 ts=8 :

View File

@ -14,6 +14,11 @@ typedef struct conv {
/// Calculate the sum of a rectangle part of the convolution kernel /// Calculate the sum of a rectangle part of the convolution kernel
/// the rectangle is defined by top left (x, y), and a size (width x height) /// the rectangle is defined by top left (x, y), and a size (width x height)
double attr_const sum_kernel(const conv *map, int x, int y, int width, int height); double attr_const sum_kernel(const conv *map, int x, int y, int width, int height);
double attr_const sum_kernel_normalized(const conv *map, int x, int y, int width, int height);
/// Create a kernel with gaussian distribution of radius r /// Create a kernel with gaussian distribution of radius r
conv *gaussian_kernel(double r); conv *gaussian_kernel(double r);
/// preprocess kernels to make shadow generation faster
/// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive
void shadow_preprocess(conv *map, double **shadow_sum);

View File

@ -99,9 +99,8 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, doubl
if (alpha_step != 0) { if (alpha_step != 0) {
int op = ((!argb && !alpha_pict) ? XCB_RENDER_PICT_OP_SRC int op = ((!argb && !alpha_pict) ? XCB_RENDER_PICT_OP_SRC
: XCB_RENDER_PICT_OP_OVER); : XCB_RENDER_PICT_OP_OVER);
xcb_render_composite(ps->c, op, pict, alpha_pict, xcb_render_composite(ps->c, op, pict, alpha_pict, ps->tgt_buffer.pict,
ps->tgt_buffer.pict, x, y, 0, 0, dx, dy, wid, x, y, 0, 0, dx, dy, wid, hei);
hei);
} }
break; break;
} }
@ -221,14 +220,14 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, pict, XCB_NONE, xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, pict, XCB_NONE,
newpict, 0, 0, 0, 0, 0, 0, wid, hei); newpict, 0, 0, 0, 0, 0, 0, wid, hei);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE, xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE,
ps->white_picture, XCB_NONE, newpict, 0, 0, 0, 0, ps->white_picture, XCB_NONE, newpict, 0, 0,
0, 0, wid, hei); 0, 0, 0, 0, wid, hei);
// We use an extra PictOpInReverse operation to get correct // We use an extra PictOpInReverse operation to get correct
// pixel alpha. There could be a better solution. // pixel alpha. There could be a better solution.
if (win_has_alpha(w)) if (win_has_alpha(w))
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE, xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE,
pict, XCB_NONE, newpict, 0, 0, 0, 0, 0, pict, XCB_NONE, newpict, 0, 0, 0, 0,
0, wid, hei); 0, 0, wid, hei);
pict = newpict; pict = newpict;
} }
} }
@ -256,9 +255,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
// top // top
int body_height = hei; int body_height = hei;
// ctop = checked top // ctop = checked top
int ctop = min_i( // Make sure top margin is smaller than height
body_height, int ctop = min_i(body_height, t);
t); // Make sure top margin is smaller than height
if (ctop > 0) if (ctop > 0)
COMP_BDR(0, 0, wid, ctop); COMP_BDR(0, 0, wid, ctop);
@ -268,13 +266,13 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
// bottom // bottom
// cbot = checked bottom // cbot = checked bottom
int cbot = // Make sure bottom margin is not too large
min_i(body_height, int cbot = min_i(body_height, b);
b); // Make sure bottom margin is not too large
if (cbot > 0) if (cbot > 0)
COMP_BDR(0, hei - cbot, wid, cbot); COMP_BDR(0, hei - cbot, wid, cbot);
body_height -= cbot; // Height of window exclude the margin // Height of window exclude the margin
body_height -= cbot;
if (body_height <= 0) if (body_height <= 0)
break; break;
@ -438,20 +436,31 @@ static void paint_root(session_t *ps, const region_t *reg_paint) {
ps->root_tile_paint.pict); ps->root_tile_paint.pict);
} }
static xcb_image_t *make_shadow(session_t *ps, double opacity, int width, int height) { static xcb_image_t *
make_shadow(xcb_connection_t *c, const conv *kernel, const double *shadow_sum,
double opacity, int width, int height) {
/*
* We classify shadows into 4 kinds of regions
* r = shadow radius
* (0, 0) is the top left of the window itself
* -r r width-r width+r
* -r +-----+---------+-----+
* | 1 | 2 | 1 |
* r +-----+---------+-----+
* | 2 | 3 | 2 |
* height-r +-----+---------+-----+
* | 1 | 2 | 1 |
* height+r +-----+---------+-----+
*/
xcb_image_t *ximage; xcb_image_t *ximage;
int ylimit, xlimit; int d = kernel->size, r = d / 2;
int swidth = width + ps->cgsize; int swidth = width + r * 2, sheight = height + r * 2;
int sheight = height + ps->cgsize;
int center = ps->cgsize / 2;
int x, y;
unsigned char d;
int x_diff;
int opacity_int = (int)(opacity * 25);
ximage = xcb_image_create_native(ps->c, swidth, sheight, assert(d % 2 == 1);
XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 0, 0, NULL); assert(d > 0);
ximage = xcb_image_create_native(c, swidth, sheight, XCB_IMAGE_FORMAT_Z_PIXMAP, 8,
0, 0, NULL);
if (!ximage) { if (!ximage) {
log_error("failed to create an X image"); log_error("failed to create an X image");
return 0; return 0;
@ -460,92 +469,91 @@ static xcb_image_t *make_shadow(session_t *ps, double opacity, int width, int he
unsigned char *data = ximage->data; unsigned char *data = ximage->data;
uint32_t sstride = ximage->stride; uint32_t sstride = ximage->stride;
/* // If the window body is smaller than the kernel, we do convolution directly
* Build the gaussian in sections if (width < r * 2 && height < r * 2) {
*/ for (int y = 0; y < sheight; y++) {
for (int x = 0; x < swidth; x++) {
/* double sum = sum_kernel_normalized(
* center (fill the complete data array) kernel, d - x - 1, d - y - 1, width, height);
*/ data[y * sstride + x] = sum * 255.0;
// XXX If the center part of the shadow would be entirely covered by
// the body of the window, we shouldn't need to fill the center here.
// XXX In general, we want to just fill the part that is not behind
// the window, in order to reduce CPU load and make transparent window
// look correct
if (ps->cgsize > 0) {
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + ps->cgsize];
} else {
d = (unsigned char)(sum_kernel(ps->gaussian_map, center, center, width,
height) *
opacity * 255.0);
}
memset(data, d, sheight * swidth);
/*
* corners
*/
ylimit = ps->cgsize;
if (ylimit > sheight / 2)
ylimit = (sheight + 1) / 2;
xlimit = ps->cgsize;
if (xlimit > swidth / 2)
xlimit = (swidth + 1) / 2;
for (y = 0; y < ylimit; y++) {
for (x = 0; x < xlimit; x++) {
if (xlimit == ps->cgsize && ylimit == ps->cgsize) {
d = ps->shadow_corner[opacity_int * (ps->cgsize + 1) *
(ps->cgsize + 1) +
y * (ps->cgsize + 1) + x];
} else {
d = (unsigned char)(sum_kernel(ps->gaussian_map, x - center,
y - center, width, height) *
opacity * 255.0);
} }
data[y * sstride + x] = d;
data[(sheight - y - 1) * sstride + x] = d;
data[(sheight - y - 1) * sstride + (swidth - x - 1)] = d;
data[y * sstride + (swidth - x - 1)] = d;
} }
return ximage;
} }
/* if (height < r * 2) {
* top/bottom // If the window height is smaller than the kernel, we divide
*/ // the window like this:
// -r r width-r width+r
x_diff = swidth - (ps->cgsize * 2); // +------+-------------+------+
if (x_diff > 0 && ylimit > 0) { // | | | |
for (y = 0; y < ylimit; y++) { // +------+-------------+------+
if (ylimit == ps->cgsize) { for (int y = 0; y < sheight; y++) {
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + y]; for (int x = 0; x < r * 2; x++) {
} else { double sum = sum_kernel_normalized(kernel, d - x - 1,
d = (unsigned char)(sum_kernel(ps->gaussian_map, center, d - y - 1, d, height) *
y - center, width, height) * 255.0;
opacity * 255.0); data[y * sstride + x] = sum;
data[y * sstride + swidth - x - 1] = sum;
} }
memset(&data[y * sstride + ps->cgsize], d, x_diff); }
memset(&data[(sheight - y - 1) * sstride + ps->cgsize], d, x_diff); for (int y = 0; y < sheight; y++) {
double sum =
sum_kernel_normalized(kernel, 0, d - y - 1, d, height) * 255.0;
memset(&data[y * sstride + r * 2], sum, width - 2 * r);
}
return ximage;
}
if (width < r * 2) {
// Similarly, for width smaller than kernel
for (int y = 0; y < r * 2; y++) {
for (int x = 0; x < swidth; x++) {
double sum = sum_kernel_normalized(kernel, d - x - 1,
d - y - 1, width, d) *
255.0;
data[y * sstride + x] = sum;
data[(sheight - y - 1) * sstride + x] = sum;
}
}
for (int x = 0; x < swidth; x++) {
double sum =
sum_kernel_normalized(kernel, d - x - 1, 0, width, d) * 255.0;
for (int y = r * 2; y < height; y++) {
data[y * sstride + x] = sum;
}
}
return ximage;
}
// Fill part 3
for (int y = r; y < height + r; y++) {
memset(data + sstride * y + r, 255, width);
}
// Part 1
for (int y = 0; y < r * 2; y++) {
for (int x = 0; x < r * 2; x++) {
double tmpsum = shadow_sum[y * d + x] * opacity * 255.0;
data[y * sstride + x] = tmpsum;
data[(sheight - y - 1) * sstride + x] = tmpsum;
data[(sheight - y - 1) * sstride + (swidth - x - 1)] = tmpsum;
data[y * sstride + (swidth - x - 1)] = tmpsum;
} }
} }
/* // Part 2, top/bottom
* sides for (int y = 0; y < r * 2; y++) {
*/ double tmpsum = shadow_sum[d * y + d - 1] * opacity * 255.0;
memset(&data[y * sstride + r * 2], tmpsum, width - r * 2);
memset(&data[(sheight - y - 1) * sstride + r * 2], tmpsum, width - r * 2);
}
for (x = 0; x < xlimit; x++) { // Part 2, left/right
if (xlimit == ps->cgsize) { for (int x = 0; x < r * 2; x++) {
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + x]; double tmpsum = shadow_sum[d * (d - 1) + x] * opacity * 255.0;
} else { for (int y = r * 2; y < height; y++) {
d = (unsigned char)(sum_kernel(ps->gaussian_map, x - center, data[y * sstride + x] = tmpsum;
center, width, height) * data[y * sstride + (swidth - x - 1)] = tmpsum;
opacity * 255.0);
}
for (y = ps->cgsize; y < sheight - ps->cgsize; y++) {
data[y * sstride + x] = d;
data[y * sstride + (swidth - x - 1)] = d;
} }
} }
@ -565,7 +573,8 @@ static bool win_build_shadow(session_t *ps, win *w, double opacity) {
xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE; xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE;
xcb_gcontext_t gc = XCB_NONE; xcb_gcontext_t gc = XCB_NONE;
shadow_image = make_shadow(ps, opacity, width, height); shadow_image =
make_shadow(ps->c, ps->gaussian_map, ps->shadow_sum, opacity, width, height);
if (!shadow_image) { if (!shadow_image) {
log_error("failed to make shadow"); log_error("failed to make shadow");
return XCB_NONE; return XCB_NONE;
@ -698,9 +707,9 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wi
// Copy from source picture to destination. The filter must // Copy from source picture to destination. The filter must
// be applied on source picture, to get the nearby pixels outside the // be applied on source picture, to get the nearby pixels outside the
// window. // window.
xcb_render_set_picture_filter( xcb_render_set_picture_filter(ps->c, src_pict, strlen(XRFILTER_CONVOLUTION),
ps->c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION, XRFILTER_CONVOLUTION, kwid * khei + 2,
kwid * khei + 2, convolution_blur); convolution_blur);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE, xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
dst_pict, (rd_from_tgt ? x : 0), dst_pict, (rd_from_tgt ? x : 0),
(rd_from_tgt ? y : 0), 0, 0, (rd_from_tgt ? 0 : x), (rd_from_tgt ? y : 0), 0, 0, (rd_from_tgt ? 0 : x),
@ -726,9 +735,8 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wi
/** /**
* Blur the background of a window. * Blur the background of a window.
*/ */
static inline void static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer,
win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer, const region_t *reg_paint) {
const region_t *reg_paint) {
const int x = w->g.x; const int x = w->g.x;
const int y = w->g.y; const int y = w->g.y;
const int wid = w->widthb; const int wid = w->widthb;
@ -794,8 +802,7 @@ win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer,
} }
// Translate global coordinates to local ones // Translate global coordinates to local ones
pixman_region32_translate(&reg_blur, -x, -y); pixman_region32_translate(&reg_blur, -x, -y);
xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, &reg_blur);
&reg_blur);
pixman_region32_clear(&reg_blur); pixman_region32_clear(&reg_blur);
} break; } break;
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
@ -815,7 +822,8 @@ win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer,
void paint_all(session_t *ps, region_t *region, const region_t *region_real, win *const t) { void paint_all(session_t *ps, region_t *region, const region_t *region_real, win *const t) {
if (ps->o.xrender_sync_fence) { if (ps->o.xrender_sync_fence) {
if (!x_fence_sync(ps, ps->sync_fence)) { if (!x_fence_sync(ps, ps->sync_fence)) {
log_error("x_fence_sync failed, xrender-sync-fence will be disabled from now on."); log_error("x_fence_sync failed, xrender-sync-fence will be "
"disabled from now on.");
xcb_sync_destroy_fence(ps->c, ps->sync_fence); xcb_sync_destroy_fence(ps->c, ps->sync_fence);
ps->sync_fence = XCB_NONE; ps->sync_fence = XCB_NONE;
ps->o.xrender_sync_fence = false; ps->o.xrender_sync_fence = false;
@ -997,15 +1005,15 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
xcb_render_picture_t new_pict = x_create_picture_with_pictfmt( xcb_render_picture_t new_pict = x_create_picture_with_pictfmt(
ps, ps->root_width, ps->root_height, pictfmt, 0, NULL); ps, ps->root_width, ps->root_height, pictfmt, 0, NULL);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
ps->tgt_buffer.pict, XCB_NONE, new_pict, 0, 0, 0, ps->tgt_buffer.pict, XCB_NONE, new_pict, 0, 0,
0, 0, 0, ps->root_width, ps->root_height); 0, 0, 0, 0, ps->root_width, ps->root_height);
// Next, we set the region of paint and highlight it // Next, we set the region of paint and highlight it
x_set_picture_clip_region(ps, new_pict, 0, 0, region_real); x_set_picture_clip_region(ps, new_pict, 0, 0, region_real);
xcb_render_composite( xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER,
ps->c, XCB_RENDER_PICT_OP_OVER, ps->white_picture, ps->white_picture,
ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0, 0, 0, 0, 0, 0, ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0, 0,
ps->root_width, ps->root_height); 0, 0, 0, 0, ps->root_width, ps->root_height);
// Finally, clear clip region and put the whole thing on screen // Finally, clear clip region and put the whole thing on screen
x_set_picture_clip_region(ps, new_pict, 0, 0, &ps->screen_reg); x_set_picture_clip_region(ps, new_pict, 0, 0, &ps->screen_reg);
@ -1015,9 +1023,9 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
xcb_render_free_picture(ps->c, new_pict); xcb_render_free_picture(ps->c, new_pict);
} else } else
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
ps->tgt_buffer.pict, XCB_NONE, ps->tgt_picture, ps->tgt_buffer.pict, XCB_NONE,
0, 0, 0, 0, 0, 0, ps->root_width, ps->tgt_picture, 0, 0, 0, 0, 0, 0,
ps->root_height); ps->root_width, ps->root_height);
break; break;
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
case BKEND_XR_GLX_HYBRID: case BKEND_XR_GLX_HYBRID:
@ -1091,8 +1099,7 @@ static bool xr_init_blur(session_t *ps) {
char *name = xcb_str_name(iter.data); char *name = xcb_str_name(iter.data);
// Check for the convolution filter // Check for the convolution filter
if (strlen(XRFILTER_CONVOLUTION) == len && if (strlen(XRFILTER_CONVOLUTION) == len &&
!memcmp(XRFILTER_CONVOLUTION, name, !memcmp(XRFILTER_CONVOLUTION, name, strlen(XRFILTER_CONVOLUTION)))
strlen(XRFILTER_CONVOLUTION)))
ps->xrfilter_convolution_exists = true; ps->xrfilter_convolution_exists = true;
} }
free(pf); free(pf);
@ -1165,49 +1172,6 @@ static bool init_alpha_picts(session_t *ps) {
return true; return true;
} }
/// precompute shadow corners and sides to save time for large windows
static void presum_gaussian(session_t *ps, conv *map) {
ps->cgsize = map->size;
const int center = map->size / 2;
const int r = ps->cgsize + 1; // radius of the kernel
const int width = ps->cgsize * 2, height = ps->cgsize * 2;
if (ps->shadow_corner)
free(ps->shadow_corner);
if (ps->shadow_top)
free(ps->shadow_top);
// clang-format off
ps->shadow_corner = cvalloc(r*r*26);
ps->shadow_top = cvalloc(r*26);
for (int x = 0; x < r; x++) {
double sum = sum_kernel(map, x-center, center, width, height);
int tmp = ps->shadow_top[25*r+x] = (unsigned char)(sum*255.0);
for (int opacity = 0; opacity < 25; opacity++) {
ps->shadow_top[opacity*r+x] = tmp*opacity/25;
}
}
for (int x = 0; x < r; x++) {
for (int y = 0; y <= x; y++) {
double sum =
sum_kernel(map, x-center, y-center, width, height);
ps->shadow_corner[25*r*r+y*r+x] = (unsigned char)(sum*255.0);
ps->shadow_corner[25*r*r+x*r+y] = ps->shadow_corner[25*r*r+y*r+x];
for (int opacity = 0; opacity < 25; opacity++) {
ps->shadow_corner[opacity*r*r+y*r+x] =
ps->shadow_corner[opacity*r*r+x*r+y] =
ps->shadow_corner[25*r*r+y*r+x]*opacity/25;
}
}
}
// clang-format on
}
bool init_render(session_t *ps) { bool init_render(session_t *ps) {
// Initialize OpenGL as early as possible // Initialize OpenGL as early as possible
if (bkend_use_glx(ps)) { if (bkend_use_glx(ps)) {
@ -1228,8 +1192,7 @@ bool init_render(session_t *ps) {
// Initialize window GL shader // Initialize window GL shader
if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) { if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) {
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
if (!glx_load_prog_main(ps, NULL, ps->o.glx_fshader_win_str, if (!glx_load_prog_main(ps, NULL, ps->o.glx_fshader_win_str, &ps->glx_prog_win))
&ps->glx_prog_win))
return false; return false;
#else #else
log_error("GLSL supported not compiled in, can't load " log_error("GLSL supported not compiled in, can't load "
@ -1259,7 +1222,7 @@ bool init_render(session_t *ps) {
} }
ps->gaussian_map = gaussian_kernel(ps->o.shadow_radius); ps->gaussian_map = gaussian_kernel(ps->o.shadow_radius);
presum_gaussian(ps, ps->gaussian_map); shadow_preprocess(ps->gaussian_map, &ps->shadow_sum);
ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0); ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0);
ps->white_picture = solid_picture(ps, true, 1, 1, 1, 1); ps->white_picture = solid_picture(ps, true, 1, 1, 1, 1);
@ -1317,8 +1280,7 @@ void deinit_render(session_t *ps) {
free_picture(ps->c, &ps->black_picture); free_picture(ps->c, &ps->black_picture);
free_picture(ps->c, &ps->white_picture); free_picture(ps->c, &ps->white_picture);
free(ps->shadow_corner); free(ps->shadow_sum);
free(ps->shadow_top);
free(ps->gaussian_map); free(ps->gaussian_map);
// Free other X resources // Free other X resources