Skip to content

Commit

Permalink
backend: give images a type
Browse files Browse the repository at this point in the history
It's quite confusing what should be passed into what because too many
things are `void *`. So give images a type to make things a bit clearer.
Because of C's limited type system, we lose the ability to annotate them
as nonnull or const, well you win some you lose some.

Also while doing this I noticed error handling around this is a bit
lacking.

Signed-off-by: Yuxuan Shui <[email protected]>
  • Loading branch information
yshui committed Feb 6, 2024
1 parent bbc657e commit fbaeeee
Show file tree
Hide file tree
Showing 15 changed files with 208 additions and 183 deletions.
28 changes: 14 additions & 14 deletions src/backend/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,10 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
ps->backend_data->ops->prepare(ps->backend_data, &reg_paint);
}

if (ps->root_image) {
if (ps->root_image.p != NULL) {
ps->backend_data->ops->compose(ps->backend_data, ps->root_image,
(coord_t){0}, NULL, (coord_t){0},
&reg_paint, &reg_visible);
(coord_t){0}, IMAGE_HANDLE_NONE,
(coord_t){0}, &reg_paint, &reg_visible);

Check warning on line 214 in src/backend/backend.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend.c#L213-L214

Added lines #L213 - L214 were not covered by tests
} else {
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
&reg_paint);
Expand All @@ -234,7 +234,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
auto reg_bound_no_corner =
win_get_bounding_shape_global_without_corners_by_val(w);

if (!w->mask_image && (w->bounding_shaped || w->corner_radius != 0)) {
if (w->mask_image.p == NULL && (w->bounding_shaped || w->corner_radius != 0)) {
win_bind_mask(ps->backend_data, w);
}

Expand Down Expand Up @@ -370,18 +370,18 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
&reg_visible);
}

assert(w->shadow_image);
assert(w->shadow_image.p != NULL);
ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_OPACITY, w->shadow_image,
&w->opacity);
coord_t shadow_coord = {.x = w->g.x + w->shadow_dx,
.y = w->g.y + w->shadow_dy};

auto inverted_mask = NULL;
image_handle inverted_mask = IMAGE_HANDLE_NONE;
if (!ps->o.wintype_option[w->window_type].full_shadow) {
pixman_region32_subtract(&reg_shadow, &reg_shadow,
&reg_bound_no_corner);
if (w->mask_image) {
if (w->mask_image.p != NULL) {
inverted_mask = w->mask_image;
ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_INVERTED,
Expand All @@ -391,7 +391,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
ps->backend_data->ops->compose(
ps->backend_data, w->shadow_image, shadow_coord,
inverted_mask, window_coord, &reg_shadow, &reg_visible);
if (inverted_mask) {
if (inverted_mask.p != NULL) {
ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_INVERTED,
inverted_mask, (bool[]){false});
Expand Down Expand Up @@ -457,9 +457,9 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {

// Draw window on target
if (w->frame_opacity == 1) {
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
window_coord, NULL, window_coord,
&reg_paint_in_bound, &reg_visible);
ps->backend_data->ops->compose(
ps->backend_data, w->win_image, window_coord, IMAGE_HANDLE_NONE,
window_coord, &reg_paint_in_bound, &reg_visible);
} else {
// For window image processing, we don't have to limit the process
// region to damage for correctness. (see <damager-note> for
Expand Down Expand Up @@ -497,9 +497,9 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, &reg_frame,
&reg_visible_local, (double[]){w->frame_opacity});
pixman_region32_fini(&reg_frame);
ps->backend_data->ops->compose(ps->backend_data, new_img,
window_coord, NULL, window_coord,
&reg_paint_in_bound, &reg_visible);
ps->backend_data->ops->compose(

Check warning on line 500 in src/backend/backend.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend.c#L500

Added line #L500 was not covered by tests
ps->backend_data, new_img, window_coord, IMAGE_HANDLE_NONE,
window_coord, &reg_paint_in_bound, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local);
Expand Down
86 changes: 55 additions & 31 deletions src/backend/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ struct dual_kawase_blur_args {
int strength;
};

typedef struct image_handle {
void *p;
} image_handle;

static const image_handle IMAGE_HANDLE_NONE = {NULL};

struct backend_operations {
// =========== Initialization ===========

Expand Down Expand Up @@ -167,26 +173,27 @@ struct backend_operations {
* Paint the content of an image onto the rendering buffer.
*
* @param backend_data the backend data
* @param image_data the image to paint
* @param image the image to paint, cannot be IMAGE_HANDLE_NONE
* @param dst_x, dst_y the top left corner of the image in the target
* @param mask the mask image, the top left of the mask is aligned with
* the top left of the image
* the top left of the image. optional, can be
* IMAGE_HANDLE_NONE.
* @param reg_paint the clip region, in target coordinates
* @param reg_visible the visible region, in target coordinates
*/
void (*compose)(backend_t *backend_data, void *image_data, coord_t image_dst,
void *mask, coord_t mask_dst, const region_t *reg_paint,
void (*compose)(backend_t *backend_data, image_handle image, coord_t image_dst,
image_handle mask, coord_t mask_dst, const region_t *reg_paint,
const region_t *reg_visible);

/// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);

/// Blur a given region of the rendering buffer.
///
/// The blur is limited by `mask`. `mask_dst` specifies the top left corner of the
/// mask is.
bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx, void *mask,
coord_t mask_dst, const region_t *reg_blur,
/// The blur can limited by `mask`. `mask_dst` specifies the top left corner of
/// the mask is. `mask` can be IMAGE_HANDLE_NONE.
bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx,
image_handle mask, coord_t mask_dst, const region_t *reg_blur,
const region_t *reg_visible) attr_nonnull(1, 3, 6, 7);

/// Update part of the back buffer with the rendering buffer, then present the
Expand All @@ -202,13 +209,15 @@ struct backend_operations {
* Bind a X pixmap to the backend's internal image data structure.
*
* @param backend_data backend data
* @param pixmap X pixmap to bind
* @param fmt information of the pixmap's visual
* @param owned whether the ownership of the pixmap is transferred to the backend
* @return backend internal data structure bound with this pixmap
* @param pixmap X pixmap to bind
* @param fmt information of the pixmap's visual
* @param owned whether the ownership of the pixmap is transferred to the
* backend.
* @return backend specific image handle for the pixmap. may be
* IMAGE_HANDLE_NONE.
*/
void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned);
image_handle (*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned);

/// Create a shadow context for rendering shadows with radius `radius`.
/// Default implementation: default_create_shadow_context
Expand All @@ -224,17 +233,23 @@ struct backend_operations {
/// shadow context is created.
/// Default implementation: default_render_shadow
///
/// @return the shadow image, may be IMAGE_HANDLE_NONE.
///
/// Required.
void *(*render_shadow)(backend_t *backend_data, int width, int height,
struct backend_shadow_context *ctx, struct color color);
image_handle (*render_shadow)(backend_t *backend_data, int width, int height,
struct backend_shadow_context *ctx, struct color color);

/// Create a shadow by blurring a mask. `size` is the size of the blur. The
/// backend can use whichever blur method is the fastest. The shadow produced
/// shoule be consistent with `render_shadow`.
///
/// @param mask the input mask, must not be IMAGE_HANDLE_NONE.
/// @return the shadow image, may be IMAGE_HANDLE_NONE.
///
/// Optional.
void *(*shadow_from_mask)(backend_t *backend_data, void *mask,
struct backend_shadow_context *ctx, struct color color);
image_handle (*shadow_from_mask)(backend_t *backend_data, image_handle mask,
struct backend_shadow_context *ctx,
struct color color);

/// Create a mask image from region `reg`. This region can be used to create
/// shadow, or used as a mask for composing. When used as a mask, it should mask
Expand All @@ -245,13 +260,18 @@ struct backend_operations {
/// and outside of the mask. Corner radius should exclude the corners from the
/// mask. Corner radius should be applied before the inversion.
///
/// @return the mask image, may be IMAGE_HANDLE_NONE.
///
/// Required.
void *(*make_mask)(backend_t *backend_data, geometry_t size, const region_t *reg);
image_handle (*make_mask)(backend_t *backend_data, geometry_t size,
const region_t *reg);

// ============ Resource management ===========

/// Free resources associated with an image data structure
void (*release_image)(backend_t *backend_data, void *img_data) attr_nonnull(1, 2);
///
/// @param image the image to be released, cannot be IMAGE_HANDLE_NONE.
void (*release_image)(backend_t *backend_data, image_handle image) attr_nonnull(1);

/// Create a shader object from a shader source.
///
Expand All @@ -276,8 +296,10 @@ struct backend_operations {
/// This function is needed because some backend might change the content of the
/// window (e.g. when using a custom shader with the glx backend), so only the
/// backend knows if an image is transparent.
bool (*is_image_transparent)(backend_t *backend_data, void *image_data)
attr_nonnull(1, 2);
///
/// @param image the image to be checked, must not be IMAGE_HANDLE_NONE.
bool (*is_image_transparent)(backend_t *backend_data, image_handle image)
attr_nonnull(1);

/// Get the age of the buffer content we are currently rendering on top
/// of. The buffer that has just been `present`ed has a buffer age of 1.
Expand Down Expand Up @@ -311,35 +333,37 @@ struct backend_operations {
*
* @param backend_data backend data
* @param prop the property to change
* @param image_data an image data structure returned by the backend
* @param image an image handle, cannot be IMAGE_HANDLE_NONE.
* @param args property value
* @return whether the operation is successful
* @return whether the operation is successful
*/
bool (*set_image_property)(backend_t *backend_data, enum image_properties prop,
void *image_data, void *args);
image_handle image, void *args);

/**
* Manipulate an image. Image properties are untouched.
*
* @param backend_data backend data
* @param op the operation to perform
* @param image_data an image data structure returned by the backend
* @param image an image, cannot be IMAGE_HANDLE_NONE.
* @param reg_op the clip region, define the part of the image to be
* operated on.
* @param reg_visible define the part of the image that will eventually
* be visible on target. this is a hint to the backend
* for optimization purposes.
* @param args extra arguments, operation specific
* @return whether the operation is successful
* @return whether the operation is successful
*/
bool (*image_op)(backend_t *backend_data, enum image_operations op, void *image_data,
bool (*image_op)(backend_t *backend_data, enum image_operations op, image_handle image,
const region_t *reg_op, const region_t *reg_visible, void *args);

/// 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);
/// original image.
///
/// @param image the image to be cloned, must not be IMAGE_HANDLE_NONE.
image_handle (*clone_image)(backend_t *base, image_handle image,
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);
Expand Down
30 changes: 15 additions & 15 deletions src/backend/backend_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ bool build_shadow(struct x_connection *c, double opacity, const int width,
return false;
}

void *default_render_shadow(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color) {
image_handle default_render_shadow(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color) {
const conv *kernel = (void *)sctx;
xcb_render_picture_t shadow_pixel =
solid_picture(backend_data->c, true, 1, color.red, color.green, color.blue);
Expand All @@ -304,28 +304,28 @@ void *default_render_shadow(backend_t *backend_data, int width, int height,
if (!build_shadow(backend_data->c, color.alpha, width, height, kernel,
shadow_pixel, &shadow, &pict)) {
x_free_picture(backend_data->c, shadow_pixel);
return NULL;
return IMAGE_HANDLE_NONE;

Check warning on line 307 in src/backend/backend_common.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend_common.c#L307

Added line #L307 was not covered by tests
}

auto visual = x_get_visual_for_standard(backend_data->c, XCB_PICT_STANDARD_ARGB_32);
void *ret = backend_data->ops->bind_pixmap(
auto ret = backend_data->ops->bind_pixmap(
backend_data, shadow, x_get_visual_info(backend_data->c, visual), true);
x_free_picture(backend_data->c, pict);
x_free_picture(backend_data->c, shadow_pixel);
return ret;
}

/// Implement render_shadow with shadow_from_mask
void *
image_handle
backend_render_shadow_from_mask(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color) {
region_t reg;
pixman_region32_init_rect(&reg, 0, 0, (unsigned int)width, (unsigned int)height);
void *mask = backend_data->ops->make_mask(
auto mask = backend_data->ops->make_mask(

Check warning on line 324 in src/backend/backend_common.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend_common.c#L324

Added line #L324 was not covered by tests
backend_data, (geometry_t){.width = width, .height = height}, &reg);
pixman_region32_fini(&reg);

void *shadow = backend_data->ops->shadow_from_mask(backend_data, mask, sctx, color);
auto shadow = backend_data->ops->shadow_from_mask(backend_data, mask, sctx, color);

Check warning on line 328 in src/backend/backend_common.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend_common.c#L328

Added line #L328 was not covered by tests
backend_data->ops->release_image(backend_data, mask);
return shadow;
}
Expand Down Expand Up @@ -458,17 +458,17 @@ 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) {
image_handle default_clone_image(backend_t *base attr_unused, image_handle image_data,

Check warning on line 461 in src/backend/backend_common.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend_common.c#L461

Added line #L461 was not covered by tests
const region_t *reg_visible attr_unused) {
auto new_img = ccalloc(1, struct backend_image);
*new_img = *(struct backend_image *)image_data;
*new_img = *(struct backend_image *)image_data.p;

Check warning on line 464 in src/backend/backend_common.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend_common.c#L464

Added line #L464 was not covered by tests
new_img->inner->refcount++;
return new_img;
return (image_handle){.p = new_img};

Check warning on line 466 in src/backend/backend_common.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend_common.c#L466

Added line #L466 was not covered by tests
}

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;
image_handle image_data, void *arg) {
struct backend_image *tex = image_data.p;

Check warning on line 471 in src/backend/backend_common.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend_common.c#L471

Added line #L471 was not covered by tests
int *iargs = arg;
bool *bargs = arg;
double *dargs = arg;
Expand All @@ -490,8 +490,8 @@ bool default_set_image_property(backend_t *base attr_unused, enum image_properti
return true;
}

bool default_is_image_transparent(backend_t *base attr_unused, void *image_data) {
struct backend_image *img = image_data;
bool default_is_image_transparent(backend_t *base attr_unused, image_handle image_data) {
struct backend_image *img = image_data.p;

Check warning on line 494 in src/backend/backend_common.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend_common.c#L493-L494

Added lines #L493 - L494 were not covered by tests
return img->opacity < 1 || img->inner->has_alpha;
}

Expand Down
13 changes: 7 additions & 6 deletions src/backend/backend_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ solid_picture(struct x_connection *, bool argb, double a, double r, double g, do
xcb_image_t *make_shadow(struct x_connection *c, const conv *kernel, double opacity,
int width, int height);

void *default_render_shadow(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color);
image_handle default_render_shadow(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color);

/// Implement `render_shadow` with `shadow_from_mask`.
void *
image_handle
backend_render_shadow_from_mask(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color);
struct backend_shadow_context *
Expand All @@ -72,8 +72,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);
image_handle
default_clone_image(backend_t *base, image_handle image_data, const region_t *reg);
bool default_is_image_transparent(backend_t *base attr_unused, image_handle image_data);
bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
void *image_data, void *arg);
image_handle image_data, void *arg);
struct backend_image *default_new_backend_image(int w, int h);
Loading

0 comments on commit fbaeeee

Please sign in to comment.