diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp index aa720ad8e89..cb62dc5bd0a 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp @@ -2168,7 +2168,152 @@ _cairo_d2d_flush(void *surface) return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_d2d_copy_surface(cairo_d2d_surface_t *dst, + cairo_d2d_surface_t *src, + cairo_point_int_t *translation, + cairo_region_t *region) +{ + RefPtr dstSurface; + dst->surface->QueryInterface(&dstSurface); + RefPtr srcSurface; + src->surface->QueryInterface(&srcSurface); + DXGI_SURFACE_DESC srcDesc, dstDesc; + srcSurface->GetDesc(&srcDesc); + dstSurface->GetDesc(&dstDesc); + + cairo_rectangle_int_t clip_rect; + clip_rect.x = 0; + clip_rect.y = 0; + clip_rect.width = dstDesc.Width; + clip_rect.height = dstDesc.Height; + + cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS; + + _cairo_d2d_flush(dst); + ID3D10Resource *srcResource = src->surface; + if (src->surface.get() == dst->surface.get()) { + // Self-copy + srcResource = _cairo_d2d_get_buffer_texture(dst); + D3D10Factory::Device()->CopyResource(srcResource, src->surface); + } else { + // Need to flush the source too if it's a different surface. + _cairo_d2d_flush(src); + } + + // One copy for each rectangle in the final clipping region. + for (int i = 0; i < cairo_region_num_rectangles(region); i++) { + D3D10_BOX rect; + cairo_rectangle_int_t area_to_copy; + + cairo_region_get_rectangle(region, i, &area_to_copy); + + cairo_rectangle_int_t transformed_rect = { area_to_copy.x + translation->x, + area_to_copy.y + translation->y, + area_to_copy.width, area_to_copy.height }; + cairo_rectangle_int_t surface_rect = { 0, 0, srcDesc.Width, srcDesc.Height }; + + + if (!_cairo_rectangle_contains(&surface_rect, &transformed_rect)) { + /* We cannot do any sort of extend, in the future a little bit of extra code could + * allow us to support EXTEND_NONE. + */ + rv = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + rect.front = 0; + rect.back = 1; + rect.left = transformed_rect.x; + rect.top = transformed_rect.y; + rect.right = transformed_rect.x + transformed_rect.width; + rect.bottom = transformed_rect.y + transformed_rect.height; + + D3D10Factory::Device()->CopySubresourceRegion(dst->surface, + 0, + area_to_copy.x, + area_to_copy.y, + 0, + srcResource, + 0, + &rect); + } + + return rv; +} + +/** + * This function will text if we can use GPU mem cpy to execute an operation with + * a surface pattern. If box is NULL it will operate on the entire dst surface. + */ +static cairo_int_status_t +_cairo_d2d_try_copy(cairo_d2d_surface_t *dst, + cairo_surface_t *src, + cairo_box_t *box, + const cairo_matrix_t *matrix, + cairo_clip_t *clip, + cairo_operator_t op) +{ + if (op != CAIRO_OPERATOR_SOURCE && + !(op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_point_int_t translation; + if ((box && !box_is_integer(box)) || + !_cairo_matrix_is_integer_translation(matrix, &translation.x, &translation.y)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* For now we do only D2D sources */ + if (src->type != CAIRO_SURFACE_TYPE_D2D) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_rectangle_int_t rect; + if (box) { + _cairo_box_round_to_rectangle(box, &rect); + } else { + rect.x = rect.y = 0; + rect.width = dst->rt->GetPixelSize().width; + rect.height = dst->rt->GetPixelSize().height; + } + + cairo_d2d_surface_t *d2dsrc = reinterpret_cast(src); + + /* Region we need to clip this operation to */ + cairo_region_t *clipping_region = NULL; + cairo_region_t *region; + if (clip) { + _cairo_clip_get_region(clip, &clipping_region); + + if (!clipping_region) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + region = cairo_region_copy(clipping_region); + + cairo_region_intersect_rectangle(region, &rect); + + if (cairo_region_is_empty(region)) { + // Nothing to do. + return CAIRO_INT_STATUS_SUCCESS; + } + } else { + region = cairo_region_create_rectangle(&rect); + // Areas outside of the surface do not matter. + cairo_rectangle_int_t surface_rect = { 0, 0, + dst->rt->GetPixelSize().width, + dst->rt->GetPixelSize().height }; + cairo_region_intersect_rectangle(region, &surface_rect); + } + + cairo_int_status_t rv = _cairo_d2d_copy_surface(dst, d2dsrc, &translation, region); + + cairo_region_destroy(region); + + return rv; +} static cairo_int_status_t _cairo_d2d_paint(void *surface, @@ -2185,6 +2330,17 @@ _cairo_d2d_paint(void *surface, return _cairo_d2d_clear(d2dsurf, clip); } + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); + + status = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface, + NULL, &source->matrix, clip, op); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + return status; + } + } _begin_draw_state(d2dsurf); status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); @@ -2380,6 +2536,19 @@ _cairo_d2d_fill(void *surface, cairo_int_status_t status; cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_box_t box; + bool is_box = _cairo_path_fixed_is_box(path, &box); + + if (is_box && source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); + cairo_int_status_t rv = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface, + &box, &source->matrix, clip, op); + + if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { + return rv; + } + } op = _cairo_d2d_simplify_operator(op, source); @@ -2406,8 +2575,6 @@ _cairo_d2d_fill(void *surface, d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); } - cairo_box_t box; - if (op == CAIRO_OPERATOR_CLEAR) { if (_cairo_path_fixed_is_box(path, &box)) { return _cairo_d2d_clear_box (d2dsurf, clip, &box); @@ -2416,7 +2583,7 @@ _cairo_d2d_fill(void *surface, } } - if (_cairo_path_fixed_is_box(path, &box)) { + if (is_box) { float x1 = _cairo_fixed_to_float(box.p1.x); float y1 = _cairo_fixed_to_float(box.p1.y); float x2 = _cairo_fixed_to_float(box.p2.x); diff --git a/gfx/cairo/cairo/src/cairo-rectangle.c b/gfx/cairo/cairo/src/cairo-rectangle.c index e887c75134a..40a8bc95c4b 100644 --- a/gfx/cairo/cairo/src/cairo-rectangle.c +++ b/gfx/cairo/cairo/src/cairo-rectangle.c @@ -94,6 +94,26 @@ _cairo_boxes_get_extents (const cairo_box_t *boxes, } } +/* This function will return 'true' if the containing_rectangle contains the + * contained_rectangle, and false otherwise. + */ +cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle) +{ + if (containing_rectangle->x > contained_rectangle->x || + containing_rectangle->y > contained_rectangle->y) + return FALSE; + + if (containing_rectangle->x + containing_rectangle->width < + contained_rectangle->x + contained_rectangle->width || + containing_rectangle->y + containing_rectangle->height < + contained_rectangle->y + contained_rectangle->height) + return FALSE; + + return TRUE; +} + /* XXX We currently have a confusing mix of boxes and rectangles as * exemplified by this function. A #cairo_box_t is a rectangular area * represented by the coordinates of the upper left and lower right diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h index 28861d73ef4..63612e63ae3 100644 --- a/gfx/cairo/cairo/src/cairoint.h +++ b/gfx/cairo/cairo/src/cairoint.h @@ -271,6 +271,10 @@ cairo_private void _cairo_box_from_rectangle (cairo_box_t *box, const cairo_rectangle_int_t *rectangle); +cairo_private cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle); + cairo_private void _cairo_box_round_to_rectangle (const cairo_box_t *box, cairo_rectangle_int_t *rectangle);