diff --git a/src/pixman-renderer.c b/src/pixman-renderer.c index 48cd37b6..be3b3e58 100644 --- a/src/pixman-renderer.c +++ b/src/pixman-renderer.c @@ -211,16 +211,114 @@ region_intersect_only_translation(pixman_region32_t *result_global, pixman_region32_intersect(result_global, result_global, global); } +static void +composite_whole(pixman_op_t op, + pixman_image_t *src, + pixman_image_t *mask, + pixman_image_t *dest, + const pixman_transform_t *transform, + pixman_filter_t filter) +{ + int32_t dest_width; + int32_t dest_height; + + dest_width = pixman_image_get_width(dest); + dest_height = pixman_image_get_height(dest); + + pixman_image_set_transform(src, transform); + pixman_image_set_filter(src, filter, NULL, 0); + + pixman_image_composite32(op, src, mask, dest, + 0, 0, /* src_x, src_y */ + 0, 0, /* mask_x, mask_y */ + 0, 0, /* dest_x, dest_y */ + dest_width, dest_height); +} + +static void +composite_clipped(pixman_image_t *src, + pixman_image_t *mask, + pixman_image_t *dest, + const pixman_transform_t *transform, + pixman_filter_t filter, + pixman_region32_t *src_clip) +{ + int n_box; + pixman_box32_t *boxes; + int32_t dest_width; + int32_t dest_height; + int src_stride; + int bitspp; + pixman_format_code_t src_format; + void *src_data; + int i; + + /* Hardcoded to use PIXMAN_OP_OVER, because sampling outside of + * a Pixman image produces (0,0,0,0) instead of discarding the + * fragment. + */ + + dest_width = pixman_image_get_width(dest); + dest_height = pixman_image_get_height(dest); + src_format = pixman_image_get_format(src); + src_stride = pixman_image_get_stride(src); + bitspp = PIXMAN_FORMAT_BPP(src_format); + src_data = pixman_image_get_data(src); + + assert(src_format); + + /* This would be massive overdraw, except when n_box is 1. */ + boxes = pixman_region32_rectangles(src_clip, &n_box); + for (i = 0; i < n_box; i++) { + uint8_t *ptr = src_data; + pixman_image_t *boximg; + pixman_transform_t adj = *transform; + + ptr += boxes[i].y1 * src_stride; + ptr += boxes[i].x1 * bitspp / 8; + boximg = pixman_image_create_bits_no_clear(src_format, + boxes[i].x2 - boxes[i].x1, + boxes[i].y2 - boxes[i].y1, + (uint32_t *)ptr, src_stride); + + pixman_transform_translate(&adj, NULL, + pixman_int_to_fixed(-boxes[i].x1), + pixman_int_to_fixed(-boxes[i].y1)); + pixman_image_set_transform(boximg, &adj); + + pixman_image_set_filter(boximg, filter, NULL, 0); + pixman_image_composite32(PIXMAN_OP_OVER, boximg, mask, dest, + 0, 0, /* src_x, src_y */ + 0, 0, /* mask_x, mask_y */ + 0, 0, /* dest_x, dest_y */ + dest_width, dest_height); + + pixman_image_unref(boximg); + } + + if (n_box > 1) { + static bool warned = false; + + if (!warned) + weston_log("Pixman-renderer warning: %dx overdraw\n", + n_box); + warned = true; + } +} + /** Paint an intersected region * * \param ev The view to be painted. * \param output The output being painted. * \param repaint_output The region to be painted in output coordinates. + * \param source_clip The region of the source image to use, in source image + * coordinates. If NULL, use the whole source image. * \param pixman_op Compositing operator, either SRC or OVER. */ static void repaint_region(struct weston_view *ev, struct weston_output *output, pixman_region32_t *repaint_output, + pixman_region32_t *source_clip, pixman_op_t pixman_op) { struct pixman_renderer *pr = @@ -229,6 +327,7 @@ repaint_region(struct weston_view *ev, struct weston_output *output, struct pixman_output_state *po = get_output_state(output); struct weston_buffer_viewport *vp = &ev->surface->buffer_viewport; pixman_transform_t transform; + pixman_filter_t filter; pixman_image_t *mask_image; pixman_color_t mask = { 0, }; @@ -236,12 +335,11 @@ repaint_region(struct weston_view *ev, struct weston_output *output, pixman_image_set_clip_region32(po->shadow_image, repaint_output); pixman_renderer_compute_transform(&transform, ev, output); - pixman_image_set_transform(ps->image, &transform); if (ev->transform.enabled || output->current_scale != vp->buffer.scale) - pixman_image_set_filter(ps->image, PIXMAN_FILTER_BILINEAR, NULL, 0); + filter = PIXMAN_FILTER_BILINEAR; else - pixman_image_set_filter(ps->image, PIXMAN_FILTER_NEAREST, NULL, 0); + filter = PIXMAN_FILTER_NEAREST; if (ps->buffer_ref.buffer) wl_shm_buffer_begin_access(ps->buffer_ref.buffer->shm_buffer); @@ -253,15 +351,12 @@ repaint_region(struct weston_view *ev, struct weston_output *output, mask_image = NULL; } - pixman_image_composite32(pixman_op, - ps->image, /* src */ - mask_image, /* mask */ - po->shadow_image, /* dest */ - 0, 0, /* src_x, src_y */ - 0, 0, /* mask_x, mask_y */ - 0, 0, /* dest_x, dest_y */ - pixman_image_get_width (po->shadow_image), /* width */ - pixman_image_get_height (po->shadow_image) /* height */); + if (source_clip) + composite_clipped(ps->image, mask_image, po->shadow_image, + &transform, filter, source_clip); + else + composite_whole(pixman_op, ps->image, mask_image, + po->shadow_image, &transform, filter); if (mask_image) pixman_image_unref(mask_image); @@ -312,7 +407,7 @@ draw_view_translated(struct weston_view *view, struct weston_output *output, view); region_global_to_output(output, &repaint_output); - repaint_region(view, output, &repaint_output, + repaint_region(view, output, &repaint_output, NULL, PIXMAN_OP_SRC); } } @@ -323,7 +418,7 @@ draw_view_translated(struct weston_view *view, struct weston_output *output, &surface_blend, view); region_global_to_output(output, &repaint_output); - repaint_region(view, output, &repaint_output, + repaint_region(view, output, &repaint_output, NULL, PIXMAN_OP_OVER); } @@ -331,6 +426,38 @@ draw_view_translated(struct weston_view *view, struct weston_output *output, pixman_region32_fini(&repaint_output); } +static void +draw_view_source_clipped(struct weston_view *view, + struct weston_output *output, + pixman_region32_t *repaint_global) +{ + struct weston_surface *surface = view->surface; + pixman_region32_t surf_region; + pixman_region32_t buffer_region; + pixman_region32_t repaint_output; + + /* Do not bother separating the opaque region from non-opaque. + * Source clipping requires PIXMAN_OP_OVER in all cases, so painting + * opaque separately has no benefit. + */ + + pixman_region32_init_rect(&surf_region, 0, 0, + surface->width, surface->height); + pixman_region32_init(&buffer_region); + weston_surface_to_buffer_region(surface, &surf_region, &buffer_region); + + pixman_region32_init(&repaint_output); + pixman_region32_copy(&repaint_output, repaint_global); + region_global_to_output(output, &repaint_output); + + repaint_region(view, output, &repaint_output, &buffer_region, + PIXMAN_OP_OVER); + + pixman_region32_fini(&repaint_output); + pixman_region32_fini(&buffer_region); + pixman_region32_fini(&surf_region); +} + static void draw_view(struct weston_view *ev, struct weston_output *output, pixman_region32_t *damage) /* in global coordinates */ @@ -357,12 +484,22 @@ draw_view(struct weston_view *ev, struct weston_output *output, zoom_logged = 1; } - /* TODO: Implement repaint_region_complex() using pixman_composite_trapezoids() */ if (view_transformation_is_translation(ev)) { + /* The simple case: The surface regions opaque, non-opaque, + * etc. are convertible to global coordinate space. + * There is no need to use a source clip region. + * It is possible to paint opaque region as PIXMAN_OP_SRC. + */ draw_view_translated(ev, output, &repaint); } else { - region_global_to_output(output, &repaint); - repaint_region(ev, output, &repaint, PIXMAN_OP_OVER); + /* The complex case: the view transformation does not allow + * converting opaque etc. regions into global coordinate space. + * Therefore we need source clipping to avoid sampling from + * unwanted source image areas, unless the source image is + * to be used whole. Source clipping does not work with + * PIXMAN_OP_SRC. + */ + draw_view_source_clipped(ev, output, &repaint); } out: