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:
parent
edb1139507
commit
4aeffa36b8
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include "backend.h"
|
||||
#include "backend_common.h"
|
||||
#include "x.h"
|
||||
#include "common.h"
|
||||
#include "x.h"
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
int ylimit, xlimit;
|
||||
int swidth = width + ps->cgsize;
|
||||
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);
|
||||
int d = kernel->size, r = d / 2;
|
||||
int swidth = width + r * 2, sheight = height + r * 2;
|
||||
|
||||
ximage = xcb_image_create_native(ps->c, swidth, sheight,
|
||||
XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 0, 0, NULL);
|
||||
assert(d % 2 == 1);
|
||||
assert(d > 0);
|
||||
|
||||
ximage = xcb_image_create_native(c, swidth, sheight, XCB_IMAGE_FORMAT_Z_PIXMAP, 8,
|
||||
0, 0, NULL);
|
||||
if (!ximage) {
|
||||
log_error("failed to create an X image");
|
||||
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;
|
||||
uint32_t sstride = ximage->stride;
|
||||
|
||||
/*
|
||||
* Build the gaussian in sections
|
||||
*/
|
||||
|
||||
/*
|
||||
* center (fill the complete data array)
|
||||
*/
|
||||
|
||||
// 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);
|
||||
// If the window body is smaller than the kernel, we do convolution directly
|
||||
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(
|
||||
kernel, d - x - 1, d - y - 1, width, height);
|
||||
data[y * sstride + x] = sum * 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* top/bottom
|
||||
*/
|
||||
|
||||
x_diff = swidth - (ps->cgsize * 2);
|
||||
if (x_diff > 0 && ylimit > 0) {
|
||||
for (y = 0; y < ylimit; y++) {
|
||||
if (ylimit == ps->cgsize) {
|
||||
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + y];
|
||||
} else {
|
||||
d = (unsigned char)(sum_kernel(ps->gaussian_map, center,
|
||||
y - center, width, height) *
|
||||
opacity * 255.0);
|
||||
if (height < r * 2) {
|
||||
// If the window height is smaller than the kernel, we divide
|
||||
// the window like this:
|
||||
// -r r width-r width+r
|
||||
// +------+-------------+------+
|
||||
// | | | |
|
||||
// +------+-------------+------+
|
||||
for (int y = 0; y < sheight; y++) {
|
||||
for (int x = 0; x < r * 2; x++) {
|
||||
double sum = sum_kernel_normalized(kernel, d - x - 1,
|
||||
d - y - 1, d, height) *
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sides
|
||||
*/
|
||||
// Part 2, top/bottom
|
||||
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++) {
|
||||
if (xlimit == ps->cgsize) {
|
||||
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + x];
|
||||
} else {
|
||||
d = (unsigned char)(sum_kernel(ps->gaussian_map, x - center,
|
||||
center, width, height) *
|
||||
opacity * 255.0);
|
||||
}
|
||||
for (y = ps->cgsize; y < sheight - ps->cgsize; y++) {
|
||||
data[y * sstride + x] = d;
|
||||
data[y * sstride + (swidth - x - 1)] = d;
|
||||
// Part 2, left/right
|
||||
for (int x = 0; x < r * 2; x++) {
|
||||
double tmpsum = shadow_sum[d * (d - 1) + x] * opacity * 255.0;
|
||||
for (int y = r * 2; y < height; y++) {
|
||||
data[y * sstride + x] = tmpsum;
|
||||
data[y * sstride + (swidth - x - 1)] = tmpsum;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_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) {
|
||||
log_error("Failed to make shadow");
|
||||
return false;
|
||||
|
|
|
@ -531,12 +531,8 @@ typedef struct session {
|
|||
/// Gaussian map of shadow.
|
||||
conv *gaussian_map;
|
||||
// for shadow precomputation
|
||||
/// Shadow depth on one side.
|
||||
int cgsize;
|
||||
/// 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;
|
||||
/// Pre-computed table for shadow.
|
||||
double *shadow_sum;
|
||||
/// A region in which shadow is not painted on.
|
||||
region_t shadow_exclude_reg;
|
||||
|
||||
|
|
|
@ -2640,9 +2640,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
|
|||
.cshadow_picture = XCB_NONE,
|
||||
.white_picture = XCB_NONE,
|
||||
.gaussian_map = NULL,
|
||||
.cgsize = 0,
|
||||
.shadow_corner = NULL,
|
||||
.shadow_top = NULL,
|
||||
.shadow_sum = NULL,
|
||||
|
||||
.refresh_rate = 0,
|
||||
.refresh_intv = 0UL,
|
||||
|
|
116
src/kernel.c
116
src/kernel.c
|
@ -6,75 +6,46 @@
|
|||
#include "kernel.h"
|
||||
#include "utils.h"
|
||||
|
||||
/*
|
||||
* A picture will help
|
||||
*
|
||||
* -center 0 width width+center
|
||||
* -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;
|
||||
|
||||
/// Sum a region convolution kernel. Region is defined by a width x height rectangle whose
|
||||
/// top left corner is at (x, y)
|
||||
double sum_kernel(const conv *map, int x, int y, int width, int height) {
|
||||
double ret = 0;
|
||||
/*
|
||||
* 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
|
||||
* Compute set of filter values which are "in range"
|
||||
*/
|
||||
|
||||
fx_start = center - x;
|
||||
if (fx_start < 0)
|
||||
fx_start = 0;
|
||||
fx_end = width + center - x;
|
||||
if (fx_end > g_size)
|
||||
fx_end = g_size;
|
||||
int xstart = x;
|
||||
if (xstart < 0)
|
||||
xstart = 0;
|
||||
int xend = width + x;
|
||||
if (xend > map->size)
|
||||
xend = map->size;
|
||||
|
||||
fy_start = center - y;
|
||||
if (fy_start < 0)
|
||||
fy_start = 0;
|
||||
fy_end = height + center - y;
|
||||
if (fy_end > g_size)
|
||||
fy_end = g_size;
|
||||
int ystart = y;
|
||||
if (ystart < 0)
|
||||
ystart = 0;
|
||||
int yend = height + y;
|
||||
if (yend > map->size)
|
||||
yend = map->size;
|
||||
|
||||
g_line = g_line + fy_start * g_size + fx_start;
|
||||
|
||||
v = 0;
|
||||
|
||||
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++;
|
||||
for (int yi = ystart; yi < yend; yi++) {
|
||||
for (int xi = xstart; xi < xend; xi++) {
|
||||
ret += map->data[yi * map->size + xi];
|
||||
}
|
||||
}
|
||||
|
||||
if (v > 1)
|
||||
v = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -113,4 +84,29 @@ conv *gaussian_kernel(double r) {
|
|||
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 :
|
||||
|
|
|
@ -14,6 +14,11 @@ typedef struct conv {
|
|||
/// 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)
|
||||
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
|
||||
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);
|
||||
|
|
304
src/render.c
304
src/render.c
|
@ -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) {
|
||||
int op = ((!argb && !alpha_pict) ? XCB_RENDER_PICT_OP_SRC
|
||||
: XCB_RENDER_PICT_OP_OVER);
|
||||
xcb_render_composite(ps->c, op, pict, alpha_pict,
|
||||
ps->tgt_buffer.pict, x, y, 0, 0, dx, dy, wid,
|
||||
hei);
|
||||
xcb_render_composite(ps->c, op, pict, alpha_pict, ps->tgt_buffer.pict,
|
||||
x, y, 0, 0, dx, dy, wid, hei);
|
||||
}
|
||||
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,
|
||||
newpict, 0, 0, 0, 0, 0, 0, wid, hei);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
ps->white_picture, XCB_NONE, newpict, 0, 0, 0, 0,
|
||||
0, 0, wid, hei);
|
||||
ps->white_picture, XCB_NONE, newpict, 0, 0,
|
||||
0, 0, 0, 0, wid, hei);
|
||||
// We use an extra PictOpInReverse operation to get correct
|
||||
// pixel alpha. There could be a better solution.
|
||||
if (win_has_alpha(w))
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
pict, XCB_NONE, newpict, 0, 0, 0, 0, 0,
|
||||
0, wid, hei);
|
||||
pict, XCB_NONE, newpict, 0, 0, 0, 0,
|
||||
0, 0, wid, hei);
|
||||
pict = newpict;
|
||||
}
|
||||
}
|
||||
|
@ -256,9 +255,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
|
|||
// top
|
||||
int body_height = hei;
|
||||
// ctop = checked top
|
||||
int ctop = min_i(
|
||||
body_height,
|
||||
t); // Make sure top margin is smaller than height
|
||||
// Make sure top margin is smaller than height
|
||||
int ctop = min_i(body_height, t);
|
||||
if (ctop > 0)
|
||||
COMP_BDR(0, 0, wid, ctop);
|
||||
|
||||
|
@ -268,13 +266,13 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
|
|||
|
||||
// bottom
|
||||
// cbot = checked bottom
|
||||
int cbot =
|
||||
min_i(body_height,
|
||||
b); // Make sure bottom margin is not too large
|
||||
// Make sure bottom margin is not too large
|
||||
int cbot = min_i(body_height, b);
|
||||
if (cbot > 0)
|
||||
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)
|
||||
break;
|
||||
|
||||
|
@ -438,20 +436,31 @@ static void paint_root(session_t *ps, const region_t *reg_paint) {
|
|||
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;
|
||||
int ylimit, xlimit;
|
||||
int swidth = width + ps->cgsize;
|
||||
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);
|
||||
int d = kernel->size, r = d / 2;
|
||||
int swidth = width + r * 2, sheight = height + r * 2;
|
||||
|
||||
ximage = xcb_image_create_native(ps->c, swidth, sheight,
|
||||
XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 0, 0, NULL);
|
||||
assert(d % 2 == 1);
|
||||
assert(d > 0);
|
||||
|
||||
ximage = xcb_image_create_native(c, swidth, sheight, XCB_IMAGE_FORMAT_Z_PIXMAP, 8,
|
||||
0, 0, NULL);
|
||||
if (!ximage) {
|
||||
log_error("failed to create an X image");
|
||||
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;
|
||||
uint32_t sstride = ximage->stride;
|
||||
|
||||
/*
|
||||
* Build the gaussian in sections
|
||||
*/
|
||||
|
||||
/*
|
||||
* center (fill the complete data array)
|
||||
*/
|
||||
|
||||
// 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);
|
||||
// If the window body is smaller than the kernel, we do convolution directly
|
||||
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(
|
||||
kernel, d - x - 1, d - y - 1, width, height);
|
||||
data[y * sstride + x] = sum * 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* top/bottom
|
||||
*/
|
||||
|
||||
x_diff = swidth - (ps->cgsize * 2);
|
||||
if (x_diff > 0 && ylimit > 0) {
|
||||
for (y = 0; y < ylimit; y++) {
|
||||
if (ylimit == ps->cgsize) {
|
||||
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + y];
|
||||
} else {
|
||||
d = (unsigned char)(sum_kernel(ps->gaussian_map, center,
|
||||
y - center, width, height) *
|
||||
opacity * 255.0);
|
||||
if (height < r * 2) {
|
||||
// If the window height is smaller than the kernel, we divide
|
||||
// the window like this:
|
||||
// -r r width-r width+r
|
||||
// +------+-------------+------+
|
||||
// | | | |
|
||||
// +------+-------------+------+
|
||||
for (int y = 0; y < sheight; y++) {
|
||||
for (int x = 0; x < r * 2; x++) {
|
||||
double sum = sum_kernel_normalized(kernel, d - x - 1,
|
||||
d - y - 1, d, height) *
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sides
|
||||
*/
|
||||
// Part 2, top/bottom
|
||||
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++) {
|
||||
if (xlimit == ps->cgsize) {
|
||||
d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + x];
|
||||
} else {
|
||||
d = (unsigned char)(sum_kernel(ps->gaussian_map, x - center,
|
||||
center, width, height) *
|
||||
opacity * 255.0);
|
||||
}
|
||||
for (y = ps->cgsize; y < sheight - ps->cgsize; y++) {
|
||||
data[y * sstride + x] = d;
|
||||
data[y * sstride + (swidth - x - 1)] = d;
|
||||
// Part 2, left/right
|
||||
for (int x = 0; x < r * 2; x++) {
|
||||
double tmpsum = shadow_sum[d * (d - 1) + x] * opacity * 255.0;
|
||||
for (int y = r * 2; y < height; y++) {
|
||||
data[y * sstride + x] = tmpsum;
|
||||
data[y * sstride + (swidth - x - 1)] = tmpsum;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_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) {
|
||||
log_error("failed to make shadow");
|
||||
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
|
||||
// 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);
|
||||
xcb_render_set_picture_filter(ps->c, src_pict, strlen(XRFILTER_CONVOLUTION),
|
||||
XRFILTER_CONVOLUTION, kwid * khei + 2,
|
||||
convolution_blur);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
|
||||
dst_pict, (rd_from_tgt ? x : 0),
|
||||
(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.
|
||||
*/
|
||||
static inline void
|
||||
win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer,
|
||||
const region_t *reg_paint) {
|
||||
static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer,
|
||||
const region_t *reg_paint) {
|
||||
const int x = w->g.x;
|
||||
const int y = w->g.y;
|
||||
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
|
||||
pixman_region32_translate(®_blur, -x, -y);
|
||||
xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache,
|
||||
®_blur);
|
||||
xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, ®_blur);
|
||||
pixman_region32_clear(®_blur);
|
||||
} break;
|
||||
#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) {
|
||||
if (ps->o.xrender_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);
|
||||
ps->sync_fence = XCB_NONE;
|
||||
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(
|
||||
ps, ps->root_width, ps->root_height, pictfmt, 0, NULL);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
|
||||
ps->tgt_buffer.pict, XCB_NONE, new_pict, 0, 0, 0,
|
||||
0, 0, 0, ps->root_width, ps->root_height);
|
||||
ps->tgt_buffer.pict, XCB_NONE, new_pict, 0, 0,
|
||||
0, 0, 0, 0, ps->root_width, ps->root_height);
|
||||
|
||||
// Next, we set the region of paint and highlight it
|
||||
x_set_picture_clip_region(ps, new_pict, 0, 0, region_real);
|
||||
xcb_render_composite(
|
||||
ps->c, XCB_RENDER_PICT_OP_OVER, ps->white_picture,
|
||||
ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0, 0, 0, 0, 0, 0,
|
||||
ps->root_width, ps->root_height);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER,
|
||||
ps->white_picture,
|
||||
ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0, 0,
|
||||
0, 0, 0, 0, ps->root_width, ps->root_height);
|
||||
|
||||
// Finally, clear clip region and put the whole thing on screen
|
||||
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);
|
||||
} else
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
|
||||
ps->tgt_buffer.pict, XCB_NONE, ps->tgt_picture,
|
||||
0, 0, 0, 0, 0, 0, ps->root_width,
|
||||
ps->root_height);
|
||||
ps->tgt_buffer.pict, XCB_NONE,
|
||||
ps->tgt_picture, 0, 0, 0, 0, 0, 0,
|
||||
ps->root_width, ps->root_height);
|
||||
break;
|
||||
#ifdef CONFIG_OPENGL
|
||||
case BKEND_XR_GLX_HYBRID:
|
||||
|
@ -1091,8 +1099,7 @@ static bool xr_init_blur(session_t *ps) {
|
|||
char *name = xcb_str_name(iter.data);
|
||||
// Check for the convolution filter
|
||||
if (strlen(XRFILTER_CONVOLUTION) == len &&
|
||||
!memcmp(XRFILTER_CONVOLUTION, name,
|
||||
strlen(XRFILTER_CONVOLUTION)))
|
||||
!memcmp(XRFILTER_CONVOLUTION, name, strlen(XRFILTER_CONVOLUTION)))
|
||||
ps->xrfilter_convolution_exists = true;
|
||||
}
|
||||
free(pf);
|
||||
|
@ -1165,49 +1172,6 @@ static bool init_alpha_picts(session_t *ps) {
|
|||
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) {
|
||||
// Initialize OpenGL as early as possible
|
||||
if (bkend_use_glx(ps)) {
|
||||
|
@ -1228,8 +1192,7 @@ bool init_render(session_t *ps) {
|
|||
// Initialize window GL shader
|
||||
if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) {
|
||||
#ifdef CONFIG_OPENGL
|
||||
if (!glx_load_prog_main(ps, NULL, ps->o.glx_fshader_win_str,
|
||||
&ps->glx_prog_win))
|
||||
if (!glx_load_prog_main(ps, NULL, ps->o.glx_fshader_win_str, &ps->glx_prog_win))
|
||||
return false;
|
||||
#else
|
||||
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);
|
||||
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->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->white_picture);
|
||||
free(ps->shadow_corner);
|
||||
free(ps->shadow_top);
|
||||
free(ps->shadow_sum);
|
||||
free(ps->gaussian_map);
|
||||
|
||||
// Free other X resources
|
||||
|
|
Loading…
Reference in New Issue