Merge pull request #638 from yshui/backend-image-api-change
Tweaking the image_op API interface.
This commit is contained in:
@@ -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;
|
||||
|
||||
2
.github/workflows/coding-style-pr.yml
vendored
2
.github/workflows/coding-style-pr.yml
vendored
@@ -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 }}
|
||||
|
||||
2
.github/workflows/coding-style.yml
vendored
2
.github/workflows/coding-style.yml
vendored
@@ -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
|
||||
|
||||
@@ -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, ®_shadow, ®_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, ®_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
|
||||
@@ -360,21 +360,20 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
pixman_region32_intersect(®_visible_local, ®_visible_local,
|
||||
®_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, ®_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,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);
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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(®);
|
||||
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);
|
||||
|
||||
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 +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(®);
|
||||
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 *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,
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user