mirror of
https://github.com/yshui/picom.git
synced 2024-11-25 14:06:08 -05:00
backend: split image_op into image_op and set_image_property
Currently there is some inconsistency in how image_op is implemented across backends. The glx backend applies some of the image operations lazily, and not always in the order the operations were made; while the xrender backend applies the operations eagerly. This can lead to different render result in some cases. Instead of trying to preserving the order of operations, which would be unnecessary, we re-model the API to better reflect the implementation. We make it clear that setting the property doesn't change the image data, and properties are only applied during composition and in a specific order. This makes sure the render result looks consistent across backends. Should also improve the performance of the xrender backend, even if only slightly. Also distill out the property management code so they can be shared. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
21dfe20794
commit
2a60836a9b
10 changed files with 491 additions and 278 deletions
|
@ -308,9 +308,9 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
|||
} else {
|
||||
auto new_img = ps->backend_data->ops->clone_image(
|
||||
ps->backend_data, w->shadow_image, ®_visible);
|
||||
ps->backend_data->ops->image_op(
|
||||
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
|
||||
NULL, ®_visible, (double[]){w->opacity});
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_OPACITY, new_img,
|
||||
&w->opacity);
|
||||
ps->backend_data->ops->compose(
|
||||
ps->backend_data, new_img, w->g.x + w->shadow_dx,
|
||||
w->g.y + w->shadow_dy, ®_shadow, ®_visible);
|
||||
|
@ -321,9 +321,9 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
|||
|
||||
// Set max brightness
|
||||
if (ps->o.max_brightness < 1.0) {
|
||||
ps->backend_data->ops->image_op(
|
||||
ps->backend_data, IMAGE_OP_MAX_BRIGHTNESS, w->win_image, NULL,
|
||||
®_visible, &ps->o.max_brightness);
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_MAX_BRIGHTNESS, w->win_image,
|
||||
&ps->o.max_brightness);
|
||||
}
|
||||
|
||||
// Draw window on target
|
||||
|
@ -363,18 +363,17 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
|||
auto new_img = ps->backend_data->ops->clone_image(
|
||||
ps->backend_data, w->win_image, ®_visible_local);
|
||||
if (w->invert_color) {
|
||||
ps->backend_data->ops->image_op(
|
||||
ps->backend_data, IMAGE_OP_INVERT_COLOR_ALL, new_img,
|
||||
NULL, ®_visible_local, NULL);
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_INVERTED, new_img, NULL);
|
||||
}
|
||||
if (w->dim) {
|
||||
double dim_opacity = ps->o.inactive_dim;
|
||||
if (!ps->o.inactive_dim_fixed) {
|
||||
dim_opacity *= w->opacity;
|
||||
}
|
||||
ps->backend_data->ops->image_op(
|
||||
ps->backend_data, IMAGE_OP_DIM_ALL, new_img, NULL,
|
||||
®_visible_local, (double[]){dim_opacity});
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_DIM_LEVEL, new_img,
|
||||
&dim_opacity);
|
||||
}
|
||||
if (w->frame_opacity != 1) {
|
||||
auto reg_frame = win_get_region_frame_local_by_val(w);
|
||||
|
@ -384,9 +383,9 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
|||
pixman_region32_fini(®_frame);
|
||||
}
|
||||
if (w->opacity != 1) {
|
||||
ps->backend_data->ops->image_op(
|
||||
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
|
||||
NULL, ®_visible_local, (double[]){w->opacity});
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_OPACITY, new_img,
|
||||
&w->opacity);
|
||||
}
|
||||
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
|
||||
w->g.y, ®_paint_in_bound,
|
||||
|
|
|
@ -32,23 +32,36 @@ typedef struct backend_base {
|
|||
|
||||
typedef void (*backend_ready_callback_t)(void *);
|
||||
|
||||
// When image properties are actually applied to the image, they are applied in a
|
||||
// particular order:
|
||||
//
|
||||
// Color inversion -> Dimming -> Opacity multiply -> Limit maximum brightness
|
||||
enum image_properties {
|
||||
// Whether the color of the image is inverted
|
||||
// 1 boolean, default: false
|
||||
IMAGE_PROPERTY_INVERTED,
|
||||
// How much the image is dimmed
|
||||
// 1 double, default: 0
|
||||
IMAGE_PROPERTY_DIM_LEVEL,
|
||||
// Image opacity, i.e. an alpha value multiplied to the alpha channel
|
||||
// 1 double, default: 1
|
||||
IMAGE_PROPERTY_OPACITY,
|
||||
// The effective size of the image, the image will be tiled to fit.
|
||||
// 2 int, default: the actual size of the image
|
||||
IMAGE_PROPERTY_EFFECTIVE_SIZE,
|
||||
// Limit how bright image can be. The image brightness is estimated by averaging
|
||||
// the pixels in the image, and dimming will be applied to scale the average
|
||||
// brightness down to the max brightness value.
|
||||
// 1 double, default: 1
|
||||
IMAGE_PROPERTY_MAX_BRIGHTNESS,
|
||||
};
|
||||
|
||||
enum image_operations {
|
||||
// Invert the color of the entire image, `reg_op` ignored
|
||||
IMAGE_OP_INVERT_COLOR_ALL,
|
||||
// Dim the entire image, argument is the percentage. `reg_op` ignored
|
||||
IMAGE_OP_DIM_ALL,
|
||||
// Apply the image properties, reset the image properties to their defaults
|
||||
// afterwards.
|
||||
IMAGE_OP_BAKE_PROPERTIES,
|
||||
// Multiply the alpha channel by the argument
|
||||
IMAGE_OP_APPLY_ALPHA,
|
||||
// Same as APPLY_ALPHA, but `reg_op` is ignored and the operation applies to the
|
||||
// full image
|
||||
IMAGE_OP_APPLY_ALPHA_ALL,
|
||||
// Change the effective size of the image, without touching the backing image
|
||||
// itself. When the image is used, the backing image should be tiled to fill its
|
||||
// effective size. `reg_op` and `reg_visible` is ignored. `arg` is two integers,
|
||||
// width and height, in that order.
|
||||
IMAGE_OP_RESIZE_TILE,
|
||||
// Limit how bright image can be
|
||||
IMAGE_OP_MAX_BRIGHTNESS,
|
||||
};
|
||||
|
||||
struct gaussian_blur_args {
|
||||
|
@ -203,7 +216,20 @@ struct backend_operations {
|
|||
* they were originally applied. This might lead to inconsistencies.*/
|
||||
|
||||
/**
|
||||
* Manipulate an image
|
||||
* Change image properties
|
||||
*
|
||||
* @param backend_data backend data
|
||||
* @param prop the property to change
|
||||
* @param image_data an image data structure returned by the backend
|
||||
* @param args property value
|
||||
* @return whether the operation is successful
|
||||
*/
|
||||
bool (*set_image_property)(backend_t *backend_data, enum image_properties prop,
|
||||
void *image_data, void *args);
|
||||
|
||||
/**
|
||||
* Manipulate an image. Image properties are untouched by and have no effects on
|
||||
* operations other than BAKE.
|
||||
*
|
||||
* @param backend_data backend data
|
||||
* @param op the operation to perform
|
||||
|
@ -214,13 +240,14 @@ struct backend_operations {
|
|||
* be visible on target. this is a hint to the backend
|
||||
* for optimization purposes.
|
||||
* @param args extra arguments, operation specific
|
||||
* @return a new image data structure containing the result
|
||||
* @return whether the operation is successful
|
||||
*/
|
||||
bool (*image_op)(backend_t *backend_data, enum image_operations op, void *image_data,
|
||||
const region_t *reg_op, const region_t *reg_visible, void *args);
|
||||
|
||||
/**
|
||||
* Read the color of the pixel at given position of the given image
|
||||
* Read the color of the pixel at given position of the given image. Image
|
||||
* properties have no effect. BAKE them first before reading the pixels.
|
||||
*
|
||||
* @param backend_data backend_data
|
||||
* @param image_data an image data structure previously returned by the
|
||||
|
@ -233,7 +260,7 @@ struct backend_operations {
|
|||
struct color *output);
|
||||
|
||||
/// Create another instance of the `image_data`. All `image_op` and
|
||||
/// `image_set_property` calls on the returned image should not affect the
|
||||
/// `set_image_property` calls on the returned image should not affect the
|
||||
/// original image
|
||||
void *(*clone_image)(backend_t *base, const void *image_data,
|
||||
const region_t *reg_visible);
|
||||
|
|
|
@ -426,6 +426,51 @@ struct dual_kawase_params *generate_dual_kawase_params(void *args) {
|
|||
return params;
|
||||
}
|
||||
|
||||
void *default_clone_image(backend_t *base attr_unused, const void *image_data,
|
||||
const region_t *reg_visible attr_unused) {
|
||||
auto new_img = ccalloc(1, struct backend_image);
|
||||
*new_img = *(struct backend_image *)image_data;
|
||||
new_img->inner->refcount++;
|
||||
return new_img;
|
||||
}
|
||||
|
||||
bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
|
||||
void *image_data, void *arg) {
|
||||
struct backend_image *tex = image_data;
|
||||
int *iargs = arg;
|
||||
bool *bargs = arg;
|
||||
double *dargs = arg;
|
||||
switch (op) {
|
||||
case IMAGE_PROPERTY_INVERTED: tex->color_inverted = bargs[0]; break;
|
||||
case IMAGE_PROPERTY_DIM_LEVEL: tex->dim = dargs[0]; break;
|
||||
case IMAGE_PROPERTY_OPACITY: tex->opacity = dargs[0]; break;
|
||||
case IMAGE_PROPERTY_EFFECTIVE_SIZE:
|
||||
// texture is already set to repeat, so nothing else we need to do
|
||||
tex->ewidth = iargs[0];
|
||||
tex->eheight = iargs[1];
|
||||
break;
|
||||
case IMAGE_PROPERTY_MAX_BRIGHTNESS: tex->max_brightness = dargs[0]; break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool default_is_image_transparent(backend_t *base attr_unused, void *image_data) {
|
||||
struct backend_image *img = image_data;
|
||||
return img->opacity < 1 || img->inner->has_alpha;
|
||||
}
|
||||
|
||||
struct backend_image *default_new_backend_image(int w, int h) {
|
||||
auto ret = ccalloc(1, struct backend_image);
|
||||
ret->opacity = 1;
|
||||
ret->dim = 0;
|
||||
ret->max_brightness = 1;
|
||||
ret->eheight = h;
|
||||
ret->ewidth = w;
|
||||
ret->color_inverted = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_backend_base(struct backend_base *base, session_t *ps) {
|
||||
base->c = ps->c;
|
||||
base->loop = ps->loop;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "backend.h"
|
||||
#include "config.h"
|
||||
#include "region.h"
|
||||
|
||||
|
@ -25,6 +26,22 @@ struct dual_kawase_params {
|
|||
int expand;
|
||||
};
|
||||
|
||||
struct backend_image_inner_base {
|
||||
int refcount;
|
||||
bool has_alpha;
|
||||
};
|
||||
|
||||
struct backend_image {
|
||||
// Backend dependent inner image data
|
||||
struct backend_image_inner_base *inner;
|
||||
double opacity;
|
||||
double dim;
|
||||
double max_brightness;
|
||||
// Effective size of the image
|
||||
int ewidth, eheight;
|
||||
bool color_inverted;
|
||||
};
|
||||
|
||||
bool build_shadow(xcb_connection_t *, xcb_drawable_t, double opacity, int width,
|
||||
int height, const conv *kernel, xcb_render_picture_t shadow_pixel,
|
||||
xcb_pixmap_t *pixmap, xcb_render_picture_t *pict);
|
||||
|
@ -51,3 +68,9 @@ void init_backend_base(struct backend_base *base, session_t *ps);
|
|||
|
||||
struct conv **generate_blur_kernel(enum blur_method method, void *args, int *kernel_count);
|
||||
struct dual_kawase_params *generate_dual_kawase_params(void *args);
|
||||
|
||||
void *default_clone_image(backend_t *base, const void *image_data, const region_t *reg);
|
||||
bool default_is_image_transparent(backend_t *base attr_unused, void *image_data);
|
||||
bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
|
||||
void *image_data, void *arg);
|
||||
struct backend_image *default_new_backend_image(int w, int h);
|
||||
|
|
|
@ -121,6 +121,12 @@ bool dummy_image_op(struct backend_base *base, enum image_operations op attr_unu
|
|||
return true;
|
||||
}
|
||||
|
||||
bool dummy_set_image_property(struct backend_base *base, enum image_properties prop attr_unused,
|
||||
void *image, void *arg attr_unused) {
|
||||
dummy_check_image(base, image);
|
||||
return true;
|
||||
}
|
||||
|
||||
void *dummy_clone_image(struct backend_base *base, const void *image,
|
||||
const region_t *reg_visible attr_unused) {
|
||||
auto img = (const struct dummy_image *)image;
|
||||
|
@ -160,6 +166,7 @@ struct backend_operations dummy_ops = {
|
|||
|
||||
.image_op = dummy_image_op,
|
||||
.clone_image = dummy_clone_image,
|
||||
.set_image_property = dummy_set_image_property,
|
||||
.create_blur_context = dummy_create_blur_context,
|
||||
.destroy_blur_context = dummy_destroy_blur_context,
|
||||
.get_blur_size = dummy_get_blur_size,
|
||||
|
|
|
@ -271,25 +271,26 @@ _gl_average_texture_color(backend_t *base, GLuint source_texture, GLuint destina
|
|||
* Returned texture must not be deleted, since it's owned by the gl_image. It will be
|
||||
* deleted when the gl_image is released.
|
||||
*/
|
||||
static GLuint gl_average_texture_color(backend_t *base, struct gl_image *img) {
|
||||
static GLuint gl_average_texture_color(backend_t *base, struct backend_image *img) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
auto inner = (struct gl_texture *)img;
|
||||
|
||||
// Prepare textures which will be used for destination and source of rendering
|
||||
// during downscaling.
|
||||
const int texture_count = ARR_SIZE(img->inner->auxiliary_texture);
|
||||
if (!img->inner->auxiliary_texture[0]) {
|
||||
assert(!img->inner->auxiliary_texture[1]);
|
||||
glGenTextures(texture_count, img->inner->auxiliary_texture);
|
||||
const int texture_count = ARR_SIZE(inner->auxiliary_texture);
|
||||
if (!inner->auxiliary_texture[0]) {
|
||||
assert(!inner->auxiliary_texture[1]);
|
||||
glGenTextures(texture_count, inner->auxiliary_texture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
for (int i = 0; i < texture_count; i++) {
|
||||
glBindTexture(GL_TEXTURE_2D, img->inner->auxiliary_texture[i]);
|
||||
glBindTexture(GL_TEXTURE_2D, inner->auxiliary_texture[i]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
|
||||
(GLint[]){0, 0, 0, 0});
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, img->inner->width,
|
||||
img->inner->height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, inner->width,
|
||||
inner->height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,7 +303,7 @@ static GLuint gl_average_texture_color(backend_t *base, struct gl_image *img) {
|
|||
// Enable shaders
|
||||
glUseProgram(gd->brightness_shader.prog);
|
||||
glUniform2f(glGetUniformLocationChecked(gd->brightness_shader.prog, "texsize"),
|
||||
(GLfloat)img->inner->width, (GLfloat)img->inner->height);
|
||||
(GLfloat)inner->width, (GLfloat)inner->height);
|
||||
|
||||
// Prepare vertex attributes
|
||||
GLuint vao;
|
||||
|
@ -327,8 +328,8 @@ static GLuint gl_average_texture_color(backend_t *base, struct gl_image *img) {
|
|||
|
||||
// Do actual recursive render to 1x1 texture
|
||||
GLuint result_texture = _gl_average_texture_color(
|
||||
base, img->inner->texture, img->inner->auxiliary_texture[0],
|
||||
img->inner->auxiliary_texture[1], fbo, img->inner->width, img->inner->height);
|
||||
base, inner->texture, inner->auxiliary_texture[0],
|
||||
inner->auxiliary_texture[1], fbo, inner->width, inner->height);
|
||||
|
||||
// Cleanup vertex attributes
|
||||
glDisableVertexAttribArray(vert_coord_loc);
|
||||
|
@ -365,10 +366,11 @@ static GLuint gl_average_texture_color(backend_t *base, struct gl_image *img) {
|
|||
* @param reg_tgt the clip region, in Xorg coordinate system
|
||||
* @param reg_visible ignored
|
||||
*/
|
||||
static void _gl_compose(backend_t *base, struct gl_image *img, GLuint target,
|
||||
static void _gl_compose(backend_t *base, struct backend_image *img, GLuint target,
|
||||
GLint *coord, GLuint *indices, int nrects) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
if (!img || !img->inner->texture) {
|
||||
auto inner = (struct gl_texture *)img->inner;
|
||||
if (!img || !inner->texture) {
|
||||
log_error("Missing texture.");
|
||||
return;
|
||||
}
|
||||
|
@ -406,7 +408,7 @@ static void _gl_compose(backend_t *base, struct gl_image *img, GLuint target,
|
|||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, brightness);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, img->inner->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, inner->texture);
|
||||
|
||||
GLuint vao;
|
||||
glGenVertexArrays(1, &vao);
|
||||
|
@ -516,7 +518,8 @@ x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int text
|
|||
void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y,
|
||||
const region_t *reg_tgt, const region_t *reg_visible attr_unused) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
struct gl_image *img = image_data;
|
||||
struct backend_image *img = image_data;
|
||||
auto inner = (struct gl_texture *)img->inner;
|
||||
|
||||
// Painting
|
||||
int nrects;
|
||||
|
@ -536,8 +539,8 @@ void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y,
|
|||
|
||||
auto coord = ccalloc(nrects * 16, GLint);
|
||||
auto indices = ccalloc(nrects * 6, GLuint);
|
||||
x_rect_to_coords(nrects, rects, dst_x, dst_y, img->inner->height, gd->height,
|
||||
img->inner->y_inverted, coord, indices);
|
||||
x_rect_to_coords(nrects, rects, dst_x, dst_y, inner->height, gd->height,
|
||||
inner->y_inverted, coord, indices);
|
||||
_gl_compose(base, img, gd->back_fbo, coord, indices, nrects);
|
||||
|
||||
free(indices);
|
||||
|
@ -1067,33 +1070,26 @@ void gl_fill(backend_t *base, struct color c, const region_t *clip) {
|
|||
return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true);
|
||||
}
|
||||
|
||||
void gl_release_image(backend_t *base, void *image_data) {
|
||||
struct gl_image *wd = image_data;
|
||||
static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
wd->inner->refcount--;
|
||||
assert(wd->inner->refcount >= 0);
|
||||
if (wd->inner->refcount > 0) {
|
||||
free(wd);
|
||||
return;
|
||||
}
|
||||
gd->release_user_data(base, inner);
|
||||
assert(inner->user_data == NULL);
|
||||
|
||||
gd->release_user_data(base, wd->inner);
|
||||
assert(wd->inner->user_data == NULL);
|
||||
|
||||
glDeleteTextures(1, &wd->inner->texture);
|
||||
glDeleteTextures(2, wd->inner->auxiliary_texture);
|
||||
free(wd->inner);
|
||||
free(wd);
|
||||
glDeleteTextures(1, &inner->texture);
|
||||
glDeleteTextures(2, inner->auxiliary_texture);
|
||||
free(inner);
|
||||
gl_check_err();
|
||||
}
|
||||
|
||||
void *gl_clone(backend_t *base attr_unused, const void *image_data,
|
||||
const region_t *reg_visible attr_unused) {
|
||||
const struct gl_image *img = image_data;
|
||||
auto new_img = ccalloc(1, struct gl_image);
|
||||
*new_img = *img;
|
||||
new_img->inner->refcount++;
|
||||
return new_img;
|
||||
void gl_release_image(backend_t *base, void *image_data) {
|
||||
struct backend_image *wd = image_data;
|
||||
auto inner = (struct gl_texture *)wd->inner;
|
||||
inner->refcount--;
|
||||
assert(inner->refcount >= 0);
|
||||
if (inner->refcount == 0) {
|
||||
gl_release_image_inner(base, inner);
|
||||
}
|
||||
free(wd);
|
||||
}
|
||||
|
||||
static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
|
||||
|
@ -1750,24 +1746,58 @@ GLuint gl_new_texture(GLenum target) {
|
|||
return texture;
|
||||
}
|
||||
|
||||
/// Decouple `img` from the image it references, also applies all the lazy operations
|
||||
static inline void gl_image_decouple(backend_t *base, struct gl_image *img) {
|
||||
/// Actually duplicate a texture into a new one, if this texture is shared
|
||||
static inline void gl_image_decouple(backend_t *base, struct backend_image *img) {
|
||||
if (img->inner->refcount == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto gd = (struct gl_data *)base;
|
||||
auto inner = (struct gl_texture *)img->inner;
|
||||
auto new_tex = ccalloc(1, struct gl_texture);
|
||||
|
||||
new_tex->texture = gl_new_texture(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, new_tex->texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img->inner->width, img->inner->height, 0,
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
new_tex->y_inverted = true;
|
||||
new_tex->height = img->inner->height;
|
||||
new_tex->width = img->inner->width;
|
||||
new_tex->height = inner->height;
|
||||
new_tex->width = inner->width;
|
||||
new_tex->refcount = 1;
|
||||
new_tex->user_data = gd->decouple_texture_user_data(base, img->inner->user_data);
|
||||
new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data);
|
||||
|
||||
GLuint fbo;
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
inner->texture, 0);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glBindTexture(GL_TEXTURE_2D, new_tex->texture);
|
||||
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, new_tex->width, new_tex->height, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
|
||||
img->inner = (struct backend_image_inner_base *)new_tex;
|
||||
inner->refcount--;
|
||||
}
|
||||
|
||||
/// Decouple `img` from the image it references, also applies all the lazy operations
|
||||
static inline void gl_image_bake(backend_t *base, struct backend_image *img) {
|
||||
if (!img->color_inverted && img->opacity == 1 && img->max_brightness == 1 &&
|
||||
img->dim == 0) {
|
||||
// Nothing to bake
|
||||
return;
|
||||
}
|
||||
auto gd = (struct gl_data *)base;
|
||||
auto new_tex = ccalloc(1, struct gl_texture);
|
||||
auto inner = (struct gl_texture *)img->inner;
|
||||
|
||||
new_tex->texture = gl_new_texture(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, new_tex->texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, inner->width, inner->height, 0, GL_BGRA,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
new_tex->y_inverted = true;
|
||||
new_tex->height = inner->height;
|
||||
new_tex->width = inner->width;
|
||||
new_tex->refcount = 1;
|
||||
new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data);
|
||||
|
||||
GLuint fbo;
|
||||
glGenFramebuffers(1, &fbo);
|
||||
|
@ -1786,16 +1816,16 @@ static inline void gl_image_decouple(backend_t *base, struct gl_image *img) {
|
|||
0, 0, // texture coord
|
||||
|
||||
// top right
|
||||
img->inner->width, 0, // vertex coord
|
||||
img->inner->width, 0, // texture coord
|
||||
inner->width, 0, // vertex coord
|
||||
inner->width, 0, // texture coord
|
||||
|
||||
// bottom right
|
||||
img->inner->width, img->inner->height,
|
||||
img->inner->width, img->inner->height,
|
||||
inner->width, inner->height,
|
||||
inner->width, inner->height,
|
||||
|
||||
// bottom left
|
||||
0, img->inner->height,
|
||||
0, img->inner->height,
|
||||
0, inner->height,
|
||||
0, inner->height,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
@ -1803,8 +1833,11 @@ static inline void gl_image_decouple(backend_t *base, struct gl_image *img) {
|
|||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
|
||||
img->inner->refcount--;
|
||||
img->inner = new_tex;
|
||||
inner->refcount--;
|
||||
if (inner->refcount == 0) {
|
||||
gl_release_image_inner(base, inner);
|
||||
}
|
||||
img->inner = (struct backend_image_inner_base *)new_tex;
|
||||
|
||||
// Clear lazy operation flags
|
||||
img->color_inverted = false;
|
||||
|
@ -1814,16 +1847,17 @@ static inline void gl_image_decouple(backend_t *base, struct gl_image *img) {
|
|||
gl_check_err();
|
||||
}
|
||||
|
||||
static void gl_image_apply_alpha(backend_t *base, struct gl_image *img,
|
||||
static void gl_image_apply_alpha(backend_t *base, struct backend_image *img,
|
||||
const region_t *reg_op, double alpha) {
|
||||
// Result color = 0 (GL_ZERO) + alpha (GL_CONSTANT_ALPHA) * original color
|
||||
auto inner = (struct gl_texture *)img->inner;
|
||||
glBlendFunc(GL_ZERO, GL_CONSTANT_ALPHA);
|
||||
glBlendColor(0, 0, 0, (GLclampf)alpha);
|
||||
GLuint fbo;
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
img->inner->texture, 0);
|
||||
inner->texture, 0);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
_gl_fill(base, (struct color){0, 0, 0, 0}, reg_op, fbo, 0, false);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
@ -1883,40 +1917,28 @@ void gl_present(backend_t *base, const region_t *region) {
|
|||
free(indices);
|
||||
}
|
||||
|
||||
/// stub for backend_operations::image_op
|
||||
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
|
||||
const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
|
||||
struct gl_image *tex = image_data;
|
||||
int *iargs = arg;
|
||||
struct backend_image *tex = image_data;
|
||||
switch (op) {
|
||||
case IMAGE_OP_INVERT_COLOR_ALL: tex->color_inverted = true; break;
|
||||
case IMAGE_OP_DIM_ALL:
|
||||
tex->dim = 1.0 - (1.0 - tex->dim) * (1.0 - *(double *)arg);
|
||||
break;
|
||||
case IMAGE_OP_APPLY_ALPHA_ALL: tex->opacity *= *(double *)arg; break;
|
||||
case IMAGE_OP_APPLY_ALPHA:
|
||||
gl_image_decouple(base, tex);
|
||||
assert(tex->inner->refcount == 1);
|
||||
gl_image_apply_alpha(base, tex, reg_op, *(double *)arg);
|
||||
break;
|
||||
case IMAGE_OP_RESIZE_TILE:
|
||||
// texture is already set to repeat, so nothing else we need to do
|
||||
tex->ewidth = iargs[0];
|
||||
tex->eheight = iargs[1];
|
||||
break;
|
||||
case IMAGE_OP_MAX_BRIGHTNESS: tex->max_brightness = *(double *)arg; break;
|
||||
case IMAGE_OP_BAKE_PROPERTIES: gl_image_bake(base, tex); break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gl_read_pixel(backend_t *base, void *image_data, int x, int y, struct color *output) {
|
||||
struct gl_image *tex = image_data;
|
||||
gl_image_decouple(base, tex);
|
||||
assert(tex->inner->refcount == 1);
|
||||
bool gl_read_pixel(backend_t *base attr_unused, void *image_data, int x, int y,
|
||||
struct color *output) {
|
||||
struct backend_image *tex = image_data;
|
||||
auto inner = (struct gl_texture *)tex->inner;
|
||||
GLfloat color[4];
|
||||
glReadPixels(x, tex->inner->y_inverted ? tex->inner->height - y : y, 1, 1,
|
||||
GL_RGBA, GL_FLOAT, color);
|
||||
glReadPixels(x, inner->y_inverted ? inner->height - y : y, 1, 1, GL_RGBA,
|
||||
GL_FLOAT, color);
|
||||
output->alpha = color[3];
|
||||
output->red = color[0];
|
||||
output->green = color[1];
|
||||
|
@ -1926,8 +1948,3 @@ bool gl_read_pixel(backend_t *base, void *image_data, int x, int y, struct color
|
|||
gl_clear_err();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool gl_is_image_transparent(backend_t *base attr_unused, void *image_data) {
|
||||
struct gl_image *img = image_data;
|
||||
return img->has_alpha;
|
||||
}
|
||||
|
|
|
@ -44,8 +44,10 @@ typedef struct {
|
|||
GLint color_loc;
|
||||
} gl_fill_shader_t;
|
||||
|
||||
/// @brief Wrapper of a binded GLX texture.
|
||||
struct gl_texture {
|
||||
int refcount;
|
||||
bool has_alpha;
|
||||
GLuint texture;
|
||||
int width, height;
|
||||
bool y_inverted;
|
||||
|
@ -55,17 +57,6 @@ struct gl_texture {
|
|||
void *user_data;
|
||||
};
|
||||
|
||||
/// @brief Wrapper of a binded GLX texture.
|
||||
typedef struct gl_image {
|
||||
struct gl_texture *inner;
|
||||
double opacity;
|
||||
double dim;
|
||||
double max_brightness;
|
||||
int ewidth, eheight;
|
||||
bool has_alpha;
|
||||
bool color_inverted;
|
||||
} gl_image_t;
|
||||
|
||||
struct gl_data {
|
||||
backend_t base;
|
||||
// If we are using proprietary NVIDIA driver
|
||||
|
@ -123,7 +114,6 @@ void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
|
|||
void gl_destroy_blur_context(backend_t *base, void *ctx);
|
||||
void gl_get_blur_size(void *blur_context, int *width, int *height);
|
||||
|
||||
bool gl_is_image_transparent(backend_t *base, void *image_data);
|
||||
void gl_fill(backend_t *base, struct color, const region_t *clip);
|
||||
|
||||
void gl_present(backend_t *base, const region_t *);
|
||||
|
|
|
@ -388,11 +388,12 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
|||
}
|
||||
|
||||
log_trace("Binding pixmap %#010x", pixmap);
|
||||
auto wd = ccalloc(1, struct gl_image);
|
||||
auto wd = ccalloc(1, struct backend_image);
|
||||
wd->max_brightness = 1;
|
||||
wd->inner = ccalloc(1, struct gl_texture);
|
||||
wd->inner->width = wd->ewidth = r->width;
|
||||
wd->inner->height = wd->eheight = r->height;
|
||||
auto inner = ccalloc(1, struct gl_texture);
|
||||
inner->width = wd->ewidth = r->width;
|
||||
inner->height = wd->eheight = r->height;
|
||||
wd->inner = (struct backend_image_inner_base *)inner;
|
||||
free(r);
|
||||
|
||||
auto fbcfg = glx_find_fbconfig(gd->display, gd->screen, fmt);
|
||||
|
@ -420,7 +421,7 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
|||
0,
|
||||
};
|
||||
|
||||
wd->inner->y_inverted = fbcfg->y_inverted;
|
||||
inner->y_inverted = fbcfg->y_inverted;
|
||||
|
||||
glxpixmap = cmalloc(struct _glx_pixmap);
|
||||
glxpixmap->pixmap = pixmap;
|
||||
|
@ -436,14 +437,14 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
|||
log_trace("GLXPixmap %#010lx", glxpixmap->glpixmap);
|
||||
|
||||
// Create texture
|
||||
wd->inner->user_data = glxpixmap;
|
||||
wd->inner->texture = gl_new_texture(GL_TEXTURE_2D);
|
||||
inner->user_data = glxpixmap;
|
||||
inner->texture = gl_new_texture(GL_TEXTURE_2D);
|
||||
inner->has_alpha = fmt.alpha_size != 0;
|
||||
wd->opacity = 1;
|
||||
wd->color_inverted = false;
|
||||
wd->dim = 0;
|
||||
wd->has_alpha = fmt.alpha_size != 0;
|
||||
wd->inner->refcount = 1;
|
||||
glBindTexture(GL_TEXTURE_2D, wd->inner->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, inner->texture);
|
||||
glXBindTexImageEXT(gd->display, glxpixmap->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
|
@ -526,10 +527,11 @@ struct backend_operations glx_ops = {
|
|||
.release_image = gl_release_image,
|
||||
.compose = gl_compose,
|
||||
.image_op = gl_image_op,
|
||||
.set_image_property = default_set_image_property,
|
||||
.read_pixel = gl_read_pixel,
|
||||
.clone_image = gl_clone,
|
||||
.clone_image = default_clone_image,
|
||||
.blur = gl_blur,
|
||||
.is_image_transparent = gl_is_image_transparent,
|
||||
.is_image_transparent = default_is_image_transparent,
|
||||
.present = glx_present,
|
||||
.buffer_age = glx_buffer_age,
|
||||
.render_shadow = default_backend_render_shadow,
|
||||
|
|
|
@ -70,41 +70,136 @@ struct _xrender_blur_context {
|
|||
int x_blur_kernel_count;
|
||||
};
|
||||
|
||||
struct _xrender_image_data {
|
||||
struct _xrender_image_data_inner {
|
||||
// struct backend_image_inner_base
|
||||
int refcount;
|
||||
bool has_alpha;
|
||||
|
||||
// Pixmap that the client window draws to,
|
||||
// it will contain the content of client window.
|
||||
xcb_pixmap_t pixmap;
|
||||
// A Picture links to the Pixmap
|
||||
xcb_render_picture_t pict;
|
||||
int width, height;
|
||||
// The effective size of the image
|
||||
int ewidth, eheight;
|
||||
bool has_alpha;
|
||||
double opacity;
|
||||
xcb_visualid_t visual;
|
||||
uint8_t depth;
|
||||
// Whether we own this image, e.g. we allocated it;
|
||||
// or not, e.g. this is a named pixmap of a X window.
|
||||
bool owned;
|
||||
};
|
||||
|
||||
static void compose_impl(struct _xrender_data *xd, const struct backend_image *img,
|
||||
int dst_x, int dst_y, const region_t *reg_paint,
|
||||
const region_t *reg_visible, xcb_render_picture_t result) {
|
||||
auto alpha_pict = xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)];
|
||||
auto inner = (struct _xrender_image_data_inner *)img->inner;
|
||||
region_t reg;
|
||||
|
||||
bool has_alpha = inner->has_alpha || img->opacity != 1;
|
||||
const auto tmpw = to_u16_checked(inner->width);
|
||||
const auto tmph = to_u16_checked(inner->height);
|
||||
const auto tmpew = to_u16_checked(img->ewidth);
|
||||
const auto tmpeh = to_u16_checked(img->eheight);
|
||||
const xcb_render_color_t dim_color = {
|
||||
.red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * img->dim)};
|
||||
|
||||
// Clip region of rendered_pict might be set during rendering, clear it to
|
||||
// make sure we get everything into the buffer
|
||||
x_clear_picture_clip_region(xd->base.c, inner->pict);
|
||||
|
||||
pixman_region32_init(®);
|
||||
pixman_region32_intersect(®, (region_t *)reg_paint, (region_t *)reg_visible);
|
||||
x_set_picture_clip_region(xd->base.c, result, 0, 0, ®);
|
||||
if ((img->color_inverted || img->dim != 0) && has_alpha) {
|
||||
// Apply image properties using a temporary image, because the source
|
||||
// image is transparent. Otherwise the properties can be applied directly
|
||||
// on the target image.
|
||||
auto tmp_pict =
|
||||
x_create_picture_with_visual(xd->base.c, xd->base.root, inner->width,
|
||||
inner->height, inner->visual, 0, NULL);
|
||||
|
||||
// Set clip region translated to source coordinate
|
||||
x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-dst_x),
|
||||
to_i16_checked(-dst_y), ®);
|
||||
// Copy source -> tmp
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
if (img->color_inverted) {
|
||||
if (inner->has_alpha) {
|
||||
auto tmp_pict2 = x_create_picture_with_visual(
|
||||
xd->base.c, xd->base.root, tmpw, tmph, inner->visual,
|
||||
0, NULL);
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC,
|
||||
tmp_pict, XCB_NONE, tmp_pict2, 0, 0,
|
||||
0, 0, 0, 0, tmpw, tmph);
|
||||
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, tmp_pict,
|
||||
0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
xcb_render_composite(
|
||||
xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE, tmp_pict2,
|
||||
XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
xcb_render_free_picture(xd->base.c, tmp_pict2);
|
||||
} else {
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, tmp_pict,
|
||||
0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
}
|
||||
}
|
||||
if (img->dim != 0) {
|
||||
// Dim the actually content of window
|
||||
xcb_rectangle_t rect = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = tmpw,
|
||||
.height = tmph,
|
||||
};
|
||||
|
||||
xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER,
|
||||
tmp_pict, dim_color, 1, &rect);
|
||||
}
|
||||
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, tmp_pict,
|
||||
alpha_pict, result, 0, 0, 0, 0, to_i16_checked(dst_x),
|
||||
to_i16_checked(dst_y), tmpew, tmpeh);
|
||||
xcb_render_free_picture(xd->base.c, tmp_pict);
|
||||
} else {
|
||||
uint8_t op = (has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
|
||||
|
||||
xcb_render_composite(xd->base.c, op, inner->pict, alpha_pict, result, 0,
|
||||
0, 0, 0, to_i16_checked(dst_x),
|
||||
to_i16_checked(dst_y), tmpew, tmpeh);
|
||||
if (img->dim != 0 || img->color_inverted) {
|
||||
// Apply properties, if we reach here, then has_alpha == false
|
||||
assert(!has_alpha);
|
||||
if (img->color_inverted) {
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, result, 0,
|
||||
0, 0, 0, to_i16_checked(dst_x),
|
||||
to_i16_checked(dst_y), tmpew, tmpeh);
|
||||
}
|
||||
|
||||
if (img->dim != 0) {
|
||||
// Dim the actually content of window
|
||||
xcb_rectangle_t rect = {
|
||||
.x = to_i16_checked(dst_x),
|
||||
.y = to_i16_checked(dst_y),
|
||||
.width = tmpew,
|
||||
.height = tmpeh,
|
||||
};
|
||||
|
||||
xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER,
|
||||
result, dim_color, 1, &rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
pixman_region32_fini(®);
|
||||
}
|
||||
|
||||
static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
|
||||
const region_t *reg_paint, const region_t *reg_visible) {
|
||||
struct _xrender_data *xd = (void *)base;
|
||||
struct _xrender_image_data *img = img_data;
|
||||
uint8_t op = (img->has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
|
||||
auto alpha_pict = xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)];
|
||||
region_t reg;
|
||||
pixman_region32_init(®);
|
||||
pixman_region32_intersect(®, (region_t *)reg_paint, (region_t *)reg_visible);
|
||||
|
||||
// Clip region of rendered_pict might be set during rendering, clear it to make
|
||||
// sure we get everything into the buffer
|
||||
x_clear_picture_clip_region(base->c, img->pict);
|
||||
|
||||
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, ®);
|
||||
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[2], 0, 0, 0, 0,
|
||||
to_i16_checked(dst_x), to_i16_checked(dst_y),
|
||||
to_u16_checked(img->ewidth), to_u16_checked(img->eheight));
|
||||
pixman_region32_fini(®);
|
||||
return compose_impl(xd, img_data, dst_x, dst_y, reg_paint, reg_visible, xd->back[2]);
|
||||
}
|
||||
|
||||
static void fill(backend_t *base, struct color c, const region_t *clip) {
|
||||
|
@ -255,31 +350,42 @@ bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool
|
|||
return NULL;
|
||||
}
|
||||
|
||||
auto img = ccalloc(1, struct _xrender_image_data);
|
||||
img->depth = (uint8_t)fmt.visual_depth;
|
||||
img->width = img->ewidth = r->width;
|
||||
img->height = img->eheight = r->height;
|
||||
img->pixmap = pixmap;
|
||||
img->opacity = 1;
|
||||
img->has_alpha = fmt.alpha_size != 0;
|
||||
img->pict =
|
||||
auto img = ccalloc(1, struct backend_image);
|
||||
auto inner = ccalloc(1, struct _xrender_image_data_inner);
|
||||
inner->depth = (uint8_t)fmt.visual_depth;
|
||||
inner->width = img->ewidth = r->width;
|
||||
inner->height = img->eheight = r->height;
|
||||
inner->pixmap = pixmap;
|
||||
inner->has_alpha = fmt.alpha_size != 0;
|
||||
inner->pict =
|
||||
x_create_picture_with_visual_and_pixmap(base->c, fmt.visual, pixmap, 0, NULL);
|
||||
img->owned = owned;
|
||||
img->visual = fmt.visual;
|
||||
inner->owned = owned;
|
||||
inner->visual = fmt.visual;
|
||||
inner->refcount = 1;
|
||||
|
||||
img->inner = (struct backend_image_inner_base *)inner;
|
||||
img->opacity = 1;
|
||||
free(r);
|
||||
|
||||
if (img->pict == XCB_NONE) {
|
||||
if (inner->pict == XCB_NONE) {
|
||||
free(inner);
|
||||
free(img);
|
||||
return NULL;
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
static void release_image_inner(backend_t *base, struct _xrender_image_data_inner *inner) {
|
||||
xcb_render_free_picture(base->c, inner->pict);
|
||||
if (inner->owned) {
|
||||
xcb_free_pixmap(base->c, inner->pixmap);
|
||||
}
|
||||
free(inner);
|
||||
}
|
||||
static void release_image(backend_t *base, void *image) {
|
||||
struct _xrender_image_data *img = image;
|
||||
xcb_render_free_picture(base->c, img->pict);
|
||||
if (img->owned) {
|
||||
xcb_free_pixmap(base->c, img->pixmap);
|
||||
struct backend_image *img = image;
|
||||
img->inner->refcount--;
|
||||
if (img->inner->refcount == 0) {
|
||||
release_image_inner(base, (void *)img->inner);
|
||||
}
|
||||
free(img);
|
||||
}
|
||||
|
@ -375,71 +481,99 @@ static int buffer_age(backend_t *backend_data) {
|
|||
return xd->buffer_age[xd->curr_back];
|
||||
}
|
||||
|
||||
static bool is_image_transparent(backend_t *bd attr_unused, void *image) {
|
||||
struct _xrender_image_data *img = image;
|
||||
return img->has_alpha;
|
||||
static struct _xrender_image_data_inner *
|
||||
new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) {
|
||||
auto new_inner = ccalloc(1, struct _xrender_image_data_inner);
|
||||
new_inner->pixmap = x_create_pixmap(base->c, depth, base->root, w, h);
|
||||
if (new_inner->pixmap == XCB_NONE) {
|
||||
log_error("Failed to create pixmap for copy");
|
||||
free(new_inner);
|
||||
return NULL;
|
||||
}
|
||||
new_inner->pict = x_create_picture_with_visual_and_pixmap(
|
||||
base->c, visual, new_inner->pixmap, 0, NULL);
|
||||
if (new_inner->pict == XCB_NONE) {
|
||||
log_error("Failed to create picture for copy");
|
||||
xcb_free_pixmap(base->c, new_inner->pixmap);
|
||||
free(new_inner);
|
||||
return NULL;
|
||||
}
|
||||
new_inner->width = w;
|
||||
new_inner->height = h;
|
||||
new_inner->visual = visual;
|
||||
new_inner->depth = depth;
|
||||
new_inner->refcount = 1;
|
||||
new_inner->owned = true;
|
||||
return new_inner;
|
||||
}
|
||||
|
||||
static bool decouple_image(backend_t *base, struct backend_image *img, const region_t *reg) {
|
||||
if (img->inner->refcount == 1) {
|
||||
return true;
|
||||
}
|
||||
auto inner = (struct _xrender_image_data_inner *)img->inner;
|
||||
auto inner2 =
|
||||
new_inner(base, inner->width, inner->height, inner->visual, inner->depth);
|
||||
if (!inner2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
x_set_picture_clip_region(base->c, inner->pict, 0, 0, reg);
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE,
|
||||
inner2->pict, 0, 0, 0, 0, 0, 0, to_u16_checked(inner->width),
|
||||
to_u16_checked(inner->height));
|
||||
|
||||
img->inner = (struct backend_image_inner_base *)inner2;
|
||||
inner->refcount--;
|
||||
return true;
|
||||
}
|
||||
static bool bake_image(backend_t *base, struct backend_image *img, const region_t *reg) {
|
||||
struct _xrender_data *xd = (void *)base;
|
||||
struct _xrender_image_data_inner *inner = (void *)img->inner;
|
||||
assert(inner->visual != XCB_NONE);
|
||||
|
||||
if (!img->color_inverted && img->opacity == 1 && img->dim == 0) {
|
||||
// Nothing to bake
|
||||
return true;
|
||||
}
|
||||
|
||||
log_trace("xrender: copying %#010x visual %#x", inner->pixmap, inner->visual);
|
||||
auto inner2 =
|
||||
new_inner(base, inner->width, inner->height, inner->visual, inner->depth);
|
||||
if (!inner2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inner2->has_alpha = (inner->has_alpha || img->opacity != 1);
|
||||
compose_impl(xd, img, 0, 0, reg, NULL, inner2->pict);
|
||||
|
||||
img->opacity = 1;
|
||||
img->inner = (struct backend_image_inner_base *)inner2;
|
||||
img->dim = 0;
|
||||
img->color_inverted = false;
|
||||
inner->refcount--;
|
||||
if (inner->refcount == 0) {
|
||||
release_image_inner(base, inner);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool image_op(backend_t *base, enum image_operations op, void *image,
|
||||
const region_t *reg_op, const region_t *reg_visible, void *arg) {
|
||||
struct _xrender_data *xd = (void *)base;
|
||||
struct _xrender_image_data *img = image;
|
||||
struct backend_image *img = image;
|
||||
region_t reg;
|
||||
double *dargs = arg;
|
||||
int *iargs = arg;
|
||||
if (op == IMAGE_OP_APPLY_ALPHA_ALL) {
|
||||
img->opacity *= dargs[0];
|
||||
img->has_alpha = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
pixman_region32_init(®);
|
||||
pixman_region32_intersect(®, (region_t *)reg_op, (region_t *)reg_visible);
|
||||
|
||||
const auto tmpw = to_u16_checked(img->width);
|
||||
const auto tmph = to_u16_checked(img->height);
|
||||
switch (op) {
|
||||
case IMAGE_OP_INVERT_COLOR_ALL:
|
||||
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg_visible);
|
||||
if (img->has_alpha) {
|
||||
auto tmp_pict =
|
||||
x_create_picture_with_visual(base->c, base->root, img->width,
|
||||
img->height, img->visual, 0, NULL);
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, img->pict,
|
||||
XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
case IMAGE_OP_BAKE_PROPERTIES: return bake_image(base, img, ®);
|
||||
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, img->pict, 0, 0,
|
||||
0, 0, 0, 0, tmpw, tmph);
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
tmp_pict, XCB_NONE, img->pict, 0, 0, 0, 0, 0,
|
||||
0, tmpw, tmph);
|
||||
xcb_render_free_picture(base->c, tmp_pict);
|
||||
} else {
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, img->pict, 0, 0,
|
||||
0, 0, 0, 0, tmpw, tmph);
|
||||
}
|
||||
break;
|
||||
case IMAGE_OP_DIM_ALL:
|
||||
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg_visible);
|
||||
|
||||
xcb_render_color_t color = {
|
||||
.red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * dargs[0])};
|
||||
|
||||
// Dim the actually content of window
|
||||
xcb_rectangle_t rect = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = tmpw,
|
||||
.height = tmph,
|
||||
};
|
||||
|
||||
xcb_render_fill_rectangles(base->c, XCB_RENDER_PICT_OP_OVER, img->pict,
|
||||
color, 1, &rect);
|
||||
break;
|
||||
case IMAGE_OP_APPLY_ALPHA:
|
||||
assert(reg_op);
|
||||
pixman_region32_intersect(®, (region_t *)reg_op, (region_t *)reg_visible);
|
||||
|
||||
if (!pixman_region32_not_empty(®)) {
|
||||
break;
|
||||
}
|
||||
|
@ -448,58 +582,25 @@ static bool image_op(backend_t *base, enum image_operations op, void *image,
|
|||
break;
|
||||
}
|
||||
|
||||
if (!decouple_image(base, img, reg_visible)) {
|
||||
pixman_region32_fini(®);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto inner = (struct _xrender_image_data_inner *)img->inner;
|
||||
auto alpha_pict = xd->alpha_pict[(int)((1 - dargs[0]) * MAX_ALPHA)];
|
||||
x_set_picture_clip_region(base->c, img->pict, 0, 0, ®);
|
||||
x_set_picture_clip_region(base->c, inner->pict, 0, 0, ®);
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_OUT_REVERSE, alpha_pict,
|
||||
XCB_NONE, img->pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
img->has_alpha = true;
|
||||
XCB_NONE, inner->pict, 0, 0, 0, 0, 0, 0,
|
||||
to_u16_checked(inner->width),
|
||||
to_u16_checked(inner->height));
|
||||
inner->has_alpha = true;
|
||||
break;
|
||||
case IMAGE_OP_RESIZE_TILE:
|
||||
img->ewidth = iargs[0];
|
||||
img->eheight = iargs[1];
|
||||
break;
|
||||
case IMAGE_OP_APPLY_ALPHA_ALL: assert(false);
|
||||
case IMAGE_OP_MAX_BRIGHTNESS: assert(false);
|
||||
}
|
||||
pixman_region32_fini(®);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(yshui): use copy-on-write
|
||||
static void *clone_image(backend_t *base, const void *image, const region_t *reg) {
|
||||
const struct _xrender_image_data *img = image;
|
||||
struct _xrender_data *xd = (void *)base;
|
||||
auto new_img = ccalloc(1, struct _xrender_image_data);
|
||||
assert(img->visual != XCB_NONE);
|
||||
log_trace("xrender: copying %#010x visual %#x", img->pixmap, img->visual);
|
||||
*new_img = *img;
|
||||
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg);
|
||||
new_img->pixmap =
|
||||
x_create_pixmap(base->c, img->depth, base->root, img->width, img->height);
|
||||
new_img->opacity = 1;
|
||||
new_img->owned = true;
|
||||
if (new_img->pixmap == XCB_NONE) {
|
||||
log_error("Failed to create pixmap for copy");
|
||||
free(new_img);
|
||||
return NULL;
|
||||
}
|
||||
new_img->pict = x_create_picture_with_visual_and_pixmap(base->c, img->visual,
|
||||
new_img->pixmap, 0, NULL);
|
||||
if (new_img->pict == XCB_NONE) {
|
||||
log_error("Failed to create picture for copy");
|
||||
xcb_free_pixmap(base->c, new_img->pixmap);
|
||||
free(new_img);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xcb_render_picture_t alpha_pict =
|
||||
img->opacity == 1 ? XCB_NONE : xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)];
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, img->pict, alpha_pict,
|
||||
new_img->pict, 0, 0, 0, 0, 0, 0, to_u16_checked(img->width),
|
||||
to_u16_checked(img->height));
|
||||
return new_img;
|
||||
}
|
||||
|
||||
static void *
|
||||
create_blur_context(backend_t *base attr_unused, enum blur_method method, void *args) {
|
||||
auto ret = ccalloc(1, struct _xrender_blur_context);
|
||||
|
@ -562,9 +663,10 @@ static void get_blur_size(void *blur_context, int *width, int *height) {
|
|||
static bool
|
||||
read_pixel(backend_t *backend_data, void *image_data, int x, int y, struct color *output) {
|
||||
auto xd = (struct _xrender_data *)backend_data;
|
||||
auto img = (struct _xrender_image_data *)image_data;
|
||||
auto img = (struct backend_image *)image_data;
|
||||
auto inner = (struct _xrender_image_data_inner *)img->inner;
|
||||
|
||||
auto r = XCB_AWAIT(xcb_get_image, xd->base.c, XCB_IMAGE_FORMAT_XY_PIXMAP, img->pixmap,
|
||||
auto r = XCB_AWAIT(xcb_get_image, xd->base.c, XCB_IMAGE_FORMAT_XY_PIXMAP, inner->pixmap,
|
||||
to_i16_checked(x), to_i16_checked(y), 1, 1, (uint32_t)-1L);
|
||||
|
||||
if (!r) {
|
||||
|
@ -673,13 +775,14 @@ struct backend_operations xrender_ops = {
|
|||
.render_shadow = default_backend_render_shadow,
|
||||
//.prepare_win = prepare_win,
|
||||
//.release_win = release_win,
|
||||
.is_image_transparent = is_image_transparent,
|
||||
.is_image_transparent = default_is_image_transparent,
|
||||
.buffer_age = buffer_age,
|
||||
.max_buffer_age = 2,
|
||||
|
||||
.image_op = image_op,
|
||||
.read_pixel = read_pixel,
|
||||
.clone_image = clone_image,
|
||||
.clone_image = default_clone_image,
|
||||
.set_image_property = default_set_image_property,
|
||||
.create_blur_context = create_blur_context,
|
||||
.destroy_blur_context = destroy_blur_context,
|
||||
.get_blur_size = get_blur_size,
|
||||
|
|
|
@ -893,9 +893,9 @@ void root_damaged(session_t *ps) {
|
|||
if (pixmap != XCB_NONE) {
|
||||
ps->root_image = ps->backend_data->ops->bind_pixmap(
|
||||
ps->backend_data, pixmap, x_get_visual_info(ps->c, ps->vis), false);
|
||||
ps->backend_data->ops->image_op(
|
||||
ps->backend_data, IMAGE_OP_RESIZE_TILE, ps->root_image, NULL,
|
||||
NULL, (int[]){ps->root_width, ps->root_height});
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_EFFECTIVE_SIZE,
|
||||
ps->root_image, (int[]){ps->root_width, ps->root_height});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue