Merge pull request #638 from yshui/backend-image-api-change

Tweaking the image_op API interface.
This commit is contained in:
yshui
2021-06-20 07:05:04 +01:00
committed by GitHub
13 changed files with 430 additions and 321 deletions

View File

@@ -18,4 +18,4 @@ CheckOptions:
- key: readability-magic-numbers.IgnoredIntegerValues
value: 4;8;16;24;32;1;2;3;4096;65536;
- key: readability-magic-numbers.IgnoredFloatingPointValues
value: 255.0;
value: 255.0;1.0;

View File

@@ -8,6 +8,6 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: git fetch --depth=1 origin ${{ github.event.pull_request.base.sha }}
- uses: yshui/git-clang-format-lint@v1.10
- uses: yshui/git-clang-format-lint@v1.11
with:
base: ${{ github.event.pull_request.base.sha }}

View File

@@ -9,6 +9,6 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 2
- uses: yshui/git-clang-format-lint@v1.10
- uses: yshui/git-clang-format-lint@v1.11
with:
base: ${{ github.event.ref }}~1

View File

@@ -306,11 +306,11 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
ps->backend_data, w->shadow_image, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
} else {
auto new_img = ps->backend_data->ops->copy(
auto new_img = ps->backend_data->ops->clone_image(
ps->backend_data, w->shadow_image, &reg_visible);
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_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, &reg_shadow, &reg_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,
&reg_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
@@ -360,21 +360,20 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
pixman_region32_intersect(&reg_visible_local, &reg_visible_local,
&reg_bound_local);
auto new_img = ps->backend_data->ops->copy(
auto new_img = ps->backend_data->ops->clone_image(
ps->backend_data, w->win_image, &reg_visible_local);
if (w->invert_color) {
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_INVERT_COLOR_ALL, new_img,
NULL, &reg_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,
&reg_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(&reg_frame);
}
if (w->opacity != 1) {
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_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, &reg_paint_in_bound,

View File

@@ -32,23 +32,33 @@ 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,
// 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 +213,19 @@ 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.
*
* @param backend_data backend data
* @param op the operation to perform
@@ -214,13 +236,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.
*
* @param backend_data backend_data
* @param image_data an image data structure previously returned by the
@@ -232,9 +255,11 @@ struct backend_operations {
bool (*read_pixel)(backend_t *backend_data, void *image_data, int x, int y,
struct color *output);
/// Create another instance of the `image_data`. All `image_op` calls on the
/// returned image should not affect the original image
void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_visible);
/// Create another instance of the `image_data`. All `image_op` and
/// `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);
/// Create a blur context that can be used to call `blur`
void *(*create_blur_context)(backend_t *base, enum blur_method, void *args);

View File

@@ -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;

View File

@@ -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);

View File

@@ -121,8 +121,14 @@ bool dummy_image_op(struct backend_base *base, enum image_operations op attr_unu
return true;
}
void *dummy_image_copy(struct backend_base *base, const void *image,
const region_t *reg_visible attr_unused) {
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;
dummy_check_image(base, img);
(*img->refcount)++;
@@ -159,7 +165,8 @@ struct backend_operations dummy_ops = {
.max_buffer_age = 5,
.image_op = dummy_image_op,
.copy = dummy_image_copy,
.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,

View File

@@ -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_copy(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,80 +1746,49 @@ 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_DRAW_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
new_tex->texture, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// clang-format off
GLint coord[] = {
// top left
0, 0, // vertex coord
0, 0, // texture coord
// top right
img->inner->width, 0, // vertex coord
img->inner->width, 0, // texture coord
// bottom right
img->inner->width, img->inner->height,
img->inner->width, img->inner->height,
// bottom left
0, img->inner->height,
0, img->inner->height,
};
// clang-format on
_gl_compose(base, img, fbo, coord, (GLuint[]){0, 1, 2, 2, 3, 0}, 1);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
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->refcount--;
img->inner = new_tex;
// Clear lazy operation flags
img->color_inverted = false;
img->dim = 0;
img->opacity = 1;
gl_check_err();
img->inner = (struct backend_image_inner_base *)new_tex;
inner->refcount--;
}
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 +1848,27 @@ 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;
}
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 +1878,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;
}

View File

@@ -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
@@ -115,7 +106,7 @@ bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
void gl_release_image(backend_t *base, void *image_data);
void *gl_copy(backend_t *base, const void *image_data, const region_t *reg_visible);
void *gl_clone(backend_t *base, const void *image_data, const region_t *reg_visible);
bool gl_blur(backend_t *base, double opacity, void *, const region_t *reg_blur,
const region_t *reg_visible);
@@ -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 *);

View File

@@ -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,
.copy = gl_copy,
.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,

View File

@@ -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(&reg);
pixman_region32_intersect(&reg, (region_t *)reg_paint, (region_t *)reg_visible);
x_set_picture_clip_region(xd->base.c, result, 0, 0, &reg);
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), &reg);
// 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(&reg);
}
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(&reg);
pixman_region32_intersect(&reg, (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, &reg);
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(&reg);
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,67 @@ 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 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(&reg);
pixman_region32_intersect(&reg, (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);
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(&reg, (region_t *)reg_op, (region_t *)reg_visible);
if (!pixman_region32_not_empty(&reg)) {
break;
}
@@ -448,58 +550,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(&reg);
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, &reg);
x_set_picture_clip_region(base->c, inner->pict, 0, 0, &reg);
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(&reg);
return true;
}
// TODO(yshui): use copy-on-write
static void *copy(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 +631,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 +743,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,
.copy = copy,
.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,

View File

@@ -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});
}
}