1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
|
#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, 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,
});
}
// Rotate the source size into destination coordinates
struct wlr_box src_box_transformed;
wlr_box_transform(&src_box_transformed, &src_box, options->transform,
buffer->buffer->width, buffer->buffer->height);
if (options->transform != WL_OUTPUT_TRANSFORM_NORMAL ||
src_box_transformed.width != dst_box.width ||
src_box_transformed.height != dst_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_y = src_box.width;
break;
case WL_OUTPUT_TRANSFORM_180:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
tr_cos = -1;
tr_sin = 0;
tr_x = src_box.width;
tr_y = src_box.height;
break;
case WL_OUTPUT_TRANSFORM_270:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
tr_cos = 0;
tr_sin = -1;
tr_x = src_box.height;
break;
}
// Pixman transforms are generally the opposite of what you expect because they
// apply to the coordinate system rather than the image. The comments here
// refer to what happens to the image, so all the code between
// pixman_transform_init_identity() and pixman_image_set_transform() is probably
// best read backwards. Also this means translations are in the opposite
// direction, imagine them as moving the origin around rather than moving the
// image.
//
// Beware that this doesn't work quite the same as wp_viewporter: We apply crop
// before transform and scale, whereas it defines crop in post-transform-scale
// coordinates. But this only applies to internal wlroots code - the viewporter
// extension code makes sure that to clients everything works as it should.
struct pixman_transform transform;
pixman_transform_init_identity(&transform);
// Apply scaling to get to the dst_box size. Because the scaling is applied last
// it depends on the whether the rotation swapped width and height, which is why
// we use src_box_transformed instead of src_box.
pixman_transform_scale(&transform, NULL,
pixman_double_to_fixed(src_box_transformed.width / (double)dst_box.width),
pixman_double_to_fixed(src_box_transformed.height / (double)dst_box.height));
// pixman rotates about the origin which again leaves everything outside of the
// viewport. 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));
// Apply the rotation
pixman_transform_rotate(&transform, NULL,
pixman_int_to_fixed(tr_cos), pixman_int_to_fixed(tr_sin));
// Apply flip before rotation
if (options->transform >= WL_OUTPUT_TRANSFORM_FLIPPED) {
// The flip leaves everything left of the Y axis which is outside the
// viewport. So translate everything back into the viewport.
pixman_transform_translate(&transform, NULL,
-pixman_int_to_fixed(src_box.width), pixman_int_to_fixed(0));
// Flip by applying a scale of -1 to the X axis
pixman_transform_scale(&transform, NULL,
pixman_int_to_fixed(-1), pixman_int_to_fixed(1));
}
// 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));
pixman_image_set_transform(texture->image, &transform);
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;
}
// Now composite the result onto the pass buffer. We specify a source origin of 0,0
// because the x,y part of source crop is already done using the transform. The
// width,height part of source crop is done here by the width and height we pass:
// because of the scaling, cropping at the end by dst_box.{width,height} is
// equivalent to if we cropped at the start by src_box.{width,height}.
pixman_image_composite32(op, texture->image, mask, buffer->image,
0, 0, // source x,y
0, 0, // mask x,y
dst_box.x, dst_box.y, // dest x,y
dst_box.width, dst_box.height // composite width,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, 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;
}
|