You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

238 lines
7.5 KiB

#include <assert.h>
#include <stdlib.h>
#include "render/pixman.h"
static const struct wlr_render_pass_impl render_pass_impl;
static struct wlr_pixman_render_pass *get_render_pass(struct wlr_render_pass *wlr_pass) {
assert(wlr_pass->impl == &render_pass_impl);
struct wlr_pixman_render_pass *pass = wl_container_of(wlr_pass, pass, base);
return pass;
}
static struct wlr_pixman_texture *get_texture(struct wlr_texture *wlr_texture) {
assert(wlr_texture_is_pixman(wlr_texture));
struct wlr_pixman_texture *texture = wl_container_of(wlr_texture, texture, wlr_texture);
return texture;
}
static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass);
wlr_buffer_end_data_ptr_access(pass->buffer->buffer);
wlr_buffer_unlock(pass->buffer->buffer);
free(pass);
return true;
}
static pixman_op_t get_pixman_blending(enum wlr_render_blend_mode mode) {
switch (mode) {
case WLR_RENDER_BLEND_MODE_PREMULTIPLIED:
return PIXMAN_OP_OVER;
case WLR_RENDER_BLEND_MODE_NONE:
return PIXMAN_OP_SRC;
}
abort();
}
static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
const struct wlr_render_texture_options *options) {
struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass);
struct wlr_pixman_texture *texture = get_texture(options->texture);
struct wlr_pixman_buffer *buffer = pass->buffer;
if (texture->buffer != NULL && !begin_pixman_data_ptr_access(texture->buffer,
&texture->image, WLR_BUFFER_DATA_PTR_ACCESS_READ)) {
return;
}
pixman_op_t op = get_pixman_blending(options->blend_mode);
pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip);
struct wlr_fbox src_fbox;
wlr_render_texture_options_get_src_box(options, &src_fbox);
struct wlr_box src_box = {
.x = roundf(src_fbox.x),
.y = roundf(src_fbox.y),
.width = roundf(src_fbox.width),
.height = roundf(src_fbox.height),
};
struct wlr_box dst_box;
wlr_render_texture_options_get_dst_box(options, &dst_box);
pixman_image_t *mask = NULL;
float alpha = wlr_render_texture_options_get_alpha(options);
if (alpha != 1) {
mask = pixman_image_create_solid_fill(&(struct pixman_color){
.alpha = 0xFFFF * alpha,
});
}
struct wlr_box orig_box;
wlr_box_transform(&orig_box, &dst_box, options->transform,
buffer->buffer->width, buffer->buffer->height);
if (options->transform != WL_OUTPUT_TRANSFORM_NORMAL ||
orig_box.width != src_box.width ||
orig_box.height != src_box.height) {
// Cosinus/sinus values are extact integers for enum wl_output_transform entries
int tr_cos = 1, tr_sin = 0, tr_x = 0, tr_y = 0;
switch (options->transform) {
case WL_OUTPUT_TRANSFORM_NORMAL:
case WL_OUTPUT_TRANSFORM_FLIPPED:
break;
case WL_OUTPUT_TRANSFORM_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
tr_cos = 0;
tr_sin = 1;
tr_x = buffer->buffer->height;
break;
case WL_OUTPUT_TRANSFORM_180:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
tr_cos = -1;
tr_sin = 0;
tr_x = buffer->buffer->width;
tr_y = buffer->buffer->height;
break;
case WL_OUTPUT_TRANSFORM_270:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
tr_cos = 0;
tr_sin = -1;
tr_y = buffer->buffer->width;
break;
}
switch (options->transform) {
case WL_OUTPUT_TRANSFORM_FLIPPED:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
tr_x = buffer->buffer->width - tr_x;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
tr_x = buffer->buffer->height - tr_x;
break;
case WL_OUTPUT_TRANSFORM_NORMAL:
case WL_OUTPUT_TRANSFORM_90:
case WL_OUTPUT_TRANSFORM_180:
case WL_OUTPUT_TRANSFORM_270:
break;
}
struct pixman_transform transform;
pixman_transform_init_identity(&transform);
pixman_transform_rotate(&transform, NULL,
pixman_int_to_fixed(tr_cos), pixman_int_to_fixed(tr_sin));
if (options->transform >= WL_OUTPUT_TRANSFORM_FLIPPED) {
pixman_transform_scale(&transform, NULL,
pixman_int_to_fixed(-1), pixman_int_to_fixed(1));
}
// pixman rotates about the origin, translate the result so that its new top-left
// corner is back at the origin.
pixman_transform_translate(&transform, NULL,
pixman_int_to_fixed(tr_x), pixman_int_to_fixed(tr_y));
pixman_transform_translate(&transform, NULL,
-pixman_int_to_fixed(orig_box.x), -pixman_int_to_fixed(orig_box.y));
pixman_transform_scale(&transform, NULL,
pixman_double_to_fixed(src_box.width / (double)orig_box.width),
pixman_double_to_fixed(src_box.height / (double)orig_box.height));
// Apply the translation for source crop so the origin is now at the top-left of
// the region we're actually using. Do this last so all the other transforms
// apply on top of this.
pixman_transform_translate(&transform, NULL,
pixman_int_to_fixed(src_box.x), pixman_int_to_fixed(src_box.y));
switch (options->filter_mode) {
case WLR_SCALE_FILTER_BILINEAR:
pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0);
break;
case WLR_SCALE_FILTER_NEAREST:
pixman_image_set_filter(texture->image, PIXMAN_FILTER_NEAREST, NULL, 0);
break;
}
pixman_image_set_transform(texture->image, &transform);
// We've already applied the transforms for source crop and scaling so just
// composite over the whole output and let the transform deal with everything.
pixman_image_composite32(op, texture->image, mask, buffer->image,
0, 0, 0, 0, 0, 0, buffer->buffer->width, buffer->buffer->height);
pixman_image_set_transform(texture->image, NULL);
} else {
// No transforms or crop needed, just a straight blit from the source
pixman_image_set_transform(texture->image, NULL);
pixman_image_composite32(op, texture->image, mask, buffer->image,
src_box.x, src_box.y, 0, 0, dst_box.x, dst_box.y,
src_box.width, src_box.height);
}
pixman_image_set_clip_region32(buffer->image, NULL);
if (texture->buffer != NULL) {
wlr_buffer_end_data_ptr_access(texture->buffer);
}
if (mask != NULL) {
pixman_image_unref(mask);
}
}
static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
const struct wlr_render_rect_options *options) {
struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass);
struct wlr_pixman_buffer *buffer = pass->buffer;
struct wlr_box box;
wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box);
pixman_op_t op = get_pixman_blending(options->color.a == 1 ?
WLR_RENDER_BLEND_MODE_NONE : options->blend_mode);
struct pixman_color color = {
.red = options->color.r * 0xFFFF,
.green = options->color.g * 0xFFFF,
.blue = options->color.b * 0xFFFF,
.alpha = options->color.a * 0xFFFF,
};
pixman_image_t *fill = pixman_image_create_solid_fill(&color);
pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip);
pixman_image_composite32(op, fill, NULL, buffer->image,
0, 0, 0, 0, box.x, box.y, box.width, box.height);
pixman_image_set_clip_region32(buffer->image, NULL);
pixman_image_unref(fill);
}
static const struct wlr_render_pass_impl render_pass_impl = {
.submit = render_pass_submit,
.add_texture = render_pass_add_texture,
.add_rect = render_pass_add_rect,
};
struct wlr_pixman_render_pass *begin_pixman_render_pass(
struct wlr_pixman_buffer *buffer) {
struct wlr_pixman_render_pass *pass = calloc(1, sizeof(*pass));
if (pass == NULL) {
return NULL;
}
wlr_render_pass_init(&pass->base, &render_pass_impl);
if (!begin_pixman_data_ptr_access(buffer->buffer, &buffer->image,
WLR_BUFFER_DATA_PTR_ACCESS_READ | WLR_BUFFER_DATA_PTR_ACCESS_WRITE)) {
free(pass);
return NULL;
}
wlr_buffer_lock(buffer->buffer);
pass->buffer = buffer;
return pass;
}