Skip to content

Commit

Permalink
Allow cloning sub-regions of similar surfaces.
Browse files Browse the repository at this point in the history
Previously the rule for clone_similar() was that the returned surface
had exactly the same size as the original, but only the contents within
the region of interest needed to be copied. This caused failures for very
large images in the xlib-backend (see test/large-source).

The obvious solution to allow cloning only the region of interest seemed
to be to simply set the device offset on the cloned surface. However, this
fails as a) nothing respects the device offset on the surface at that
layer in the compositing stack and b) possibly returning references to the
original source surface provides further confusion by mixing in another
source of device offset.

The second method was to add extra out parameters so that the
device offset could be returned separately and, for example, mixed into
the pattern matrix. Not as elegant, a couple of extra warts to the
interface, but it works - one less XFAIL...
  • Loading branch information
Chris Wilson committed Sep 27, 2008
1 parent 7f3a48f commit 5b97ee6
Show file tree
Hide file tree
Showing 16 changed files with 126 additions and 31 deletions.
6 changes: 6 additions & 0 deletions src/cairo-clip.c
Original file line number Diff line number Diff line change
Expand Up @@ -690,16 +690,22 @@ _cairo_clip_init_deep_copy (cairo_clip_t *clip,
}

if (other->surface) {
int dx, dy;
status = _cairo_surface_clone_similar (target, other->surface,
0,
0,
other->surface_rect.width,
other->surface_rect.height,
&dx, &dy,
&clip->surface);
if (status)
goto BAIL;

clip->surface_rect = other->surface_rect;

/* src offset was 0, so we expect an exact replica of the surface */
assert (dx == 0);
assert (dy == 0);
}

if (other->path) {
Expand Down
14 changes: 9 additions & 5 deletions src/cairo-directfb-surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,8 @@ _cairo_directfb_surface_clone_similar (void *abstract_surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out)
{
cairo_directfb_surface_t *surface = abstract_surface;
Expand All @@ -625,9 +627,10 @@ _cairo_directfb_surface_clone_similar (void *abstract_surface,
"%s( surface=%p, src=%p ).\n", __FUNCTION__, surface, src);

if (src->backend == surface->base.backend) {
cairo_surface_reference (src);
*clone_out = src;

*device_offset_x = 0;
*device_offset_y = 0;
*clone_out = cairo_surface_reference (src);

return CAIRO_STATUS_SUCCESS;
}
else if (_cairo_surface_is_image (src)) {
Expand Down Expand Up @@ -685,9 +688,10 @@ _cairo_directfb_surface_clone_similar (void *abstract_surface,
}

clone->dfbsurface->Unlock (clone->dfbsurface);


*device_offset_x = 0;
*device_offset_y = 0;
*clone_out = &clone->base;

return CAIRO_STATUS_SUCCESS;
}

Expand Down
10 changes: 10 additions & 0 deletions src/cairo-glitz-surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,8 @@ _cairo_glitz_surface_clone_similar (void *abstract_surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out)
{
cairo_glitz_surface_t *surface = abstract_surface;
Expand All @@ -522,6 +524,8 @@ _cairo_glitz_surface_clone_similar (void *abstract_surface,

if (src->backend == surface->base.backend)
{
*device_offset_x = 0;
*device_offset_y = 0;
*clone_out = cairo_surface_reference (src);

return CAIRO_STATUS_SUCCESS;
Expand Down Expand Up @@ -2244,6 +2248,7 @@ _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font,
if (!glyph_private || !glyph_private->area)
{
int glyph_width, glyph_height;
int device_offset_x, device_offset_y;

image = &scaled_glyphs[i]->surface->base;
glyph_width = scaled_glyphs[i]->surface->width;
Expand All @@ -2255,11 +2260,16 @@ _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font,
0,
glyph_width,
glyph_height,
&device_offset_x,
&device_offset_y
(cairo_surface_t **)
&clone);
if (status)
goto UNLOCK;

assert (device_offset_x = 0);
assert (device_offset_y = 0);

x_offset = scaled_glyphs[i]->surface->base.device_transform.x0;
y_offset = scaled_glyphs[i]->surface->base.device_transform.y0;
x1 = _cairo_lround (glyphs[i].x - x_offset);
Expand Down
3 changes: 3 additions & 0 deletions src/cairo-image-surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -779,11 +779,14 @@ _cairo_image_surface_clone_similar (void *abstract_surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out)
{
cairo_image_surface_t *surface = abstract_surface;

if (src->backend == surface->base.backend) {
*device_offset_x = *device_offset_y = 0;
*clone_out = cairo_surface_reference (src);

return CAIRO_STATUS_SUCCESS;
Expand Down
15 changes: 13 additions & 2 deletions src/cairo-pattern.c
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,7 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern,
pixman_gradient_stop_t pixman_stops_static[2];
pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
unsigned int i;
int device_offset_x, device_offset_y;

if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
pixman_stops = _cairo_malloc_ab (pattern->n_stops, sizeof(pixman_gradient_stop_t));
Expand Down Expand Up @@ -1396,7 +1397,10 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern,
pixman_image_unref (pixman_image);

status = _cairo_surface_clone_similar (dst, &image->base,
0, 0, width, height, out);
0, 0, width, height,
&device_offset_x,
&device_offset_y,
out);

cairo_surface_destroy (&image->base);

Expand Down Expand Up @@ -1829,7 +1833,14 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern,
}

status = _cairo_surface_clone_similar (dst, pattern->surface,
x, y, width, height, out);
x, y, width, height,
&x, &y, out);
if (status == CAIRO_STATUS_SUCCESS && (x != 0 || y != 0)) {
cairo_matrix_t m;

cairo_matrix_init_translate (&m, -x, -y);
cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m);
}
}

return status;
Expand Down
10 changes: 9 additions & 1 deletion src/cairo-quartz-surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -1580,6 +1580,8 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out)
{
cairo_quartz_surface_t *new_surface = NULL;
Expand All @@ -1598,6 +1600,8 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
*clone_out = (cairo_surface_t*)
_cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
width, height);
*device_offset_x = 0;
*device_offset_y = 0;
return CAIRO_STATUS_SUCCESS;
}

Expand All @@ -1608,6 +1612,8 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
*clone_out = (cairo_surface_t*)
_cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
qsurf->extents.width, qsurf->extents.height);
*device_offset_x = 0;
*device_offset_y = 0;
return CAIRO_STATUS_SUCCESS;
}
}
Expand Down Expand Up @@ -1646,7 +1652,9 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,

CGImageRelease (quartz_image);

FINISH:
FINISH:
*device_offset_x = src_x;
*device_offset_y = src_y;
*clone_out = (cairo_surface_t*) new_surface;

return CAIRO_STATUS_SUCCESS;
Expand Down
2 changes: 2 additions & 0 deletions src/cairo-surface-fallback-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ _cairo_surface_fallback_clone_similar (cairo_surface_t *surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out);

#endif
8 changes: 6 additions & 2 deletions src/cairo-surface-fallback.c
Original file line number Diff line number Diff line change
Expand Up @@ -1251,6 +1251,8 @@ _cairo_surface_fallback_clone_similar (cairo_surface_t *surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out)
{
cairo_status_t status;
Expand Down Expand Up @@ -1280,9 +1282,11 @@ _cairo_surface_fallback_clone_similar (cairo_surface_t *surface,
status = cairo_status (cr);
cairo_destroy (cr);

if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_STATUS_SUCCESS) {
*device_offset_x = src_x;
*device_offset_y = src_y;
*clone_out = new_surface;
else
} else
cairo_surface_destroy (new_surface);

return status;
Expand Down
24 changes: 18 additions & 6 deletions src/cairo-surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,8 @@ _cairo_surface_clone_similar (cairo_surface_t *surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out)
{
cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
Expand All @@ -1140,8 +1142,12 @@ _cairo_surface_clone_similar (cairo_surface_t *surface,
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);

if (surface->backend->clone_similar) {
status = surface->backend->clone_similar (surface, src, src_x, src_y,
width, height, clone_out);
status = surface->backend->clone_similar (surface, src,
src_x, src_y,
width, height,
device_offset_x,
device_offset_y,
clone_out);

if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
/* If we failed, try again with an image surface */
Expand All @@ -1151,6 +1157,8 @@ _cairo_surface_clone_similar (cairo_surface_t *surface,
surface->backend->clone_similar (surface, &image->base,
src_x, src_y,
width, height,
device_offset_x,
device_offset_y,
clone_out);

_cairo_surface_release_source_image (src, image, image_extra);
Expand All @@ -1161,8 +1169,12 @@ _cairo_surface_clone_similar (cairo_surface_t *surface,
/* If we're still unsupported, hit our fallback path to get a clone */
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
status =
_cairo_surface_fallback_clone_similar (surface, src, src_x, src_y,
width, height, clone_out);
_cairo_surface_fallback_clone_similar (surface, src,
src_x, src_y,
width, height,
device_offset_x,
device_offset_y,
clone_out);

/* We should never get UNSUPPORTED here, so if we have an error, bail. */
if (status)
Expand All @@ -1171,8 +1183,8 @@ _cairo_surface_clone_similar (cairo_surface_t *surface,
/* Update the clone's device_transform (which the underlying surface
* backend knows nothing about) */
if (*clone_out != src) {
(*clone_out)->device_transform = src->device_transform;
(*clone_out)->device_transform_inverse = src->device_transform_inverse;
(*clone_out)->device_transform = src->device_transform;
(*clone_out)->device_transform_inverse = src->device_transform_inverse;
}

return status;
Expand Down
2 changes: 2 additions & 0 deletions src/cairo-win32-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ _cairo_win32_surface_clone_similar (void *abstract_surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out);

static inline void
Expand Down
8 changes: 6 additions & 2 deletions src/cairo-win32-surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,8 @@ _cairo_win32_surface_clone_similar (void *abstract_surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out)
{
cairo_content_t src_content;
Expand Down Expand Up @@ -470,9 +472,11 @@ _cairo_win32_surface_clone_similar (void *abstract_surface,

_cairo_pattern_fini (&pattern.base);

if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_STATUS_SUCCESS) {
*device_offset_x = src_x;
*device_offset_y = src_y;
*clone_out = new_surface;
else
} else
cairo_surface_destroy (new_surface);

return status;
Expand Down
18 changes: 14 additions & 4 deletions src/cairo-xcb-surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,8 @@ _cairo_xcb_surface_clone_similar (void *abstract_surface,
int src_y,
int width,
int height,
int *device_offset_x,
int *device_offset_y,
cairo_surface_t **clone_out)
{
cairo_xcb_surface_t *surface = abstract_surface;
Expand All @@ -704,6 +706,8 @@ _cairo_xcb_surface_clone_similar (void *abstract_surface,
cairo_xcb_surface_t *xcb_src = (cairo_xcb_surface_t *)src;

if (_cairo_xcb_surface_same_screen(surface, xcb_src)) {
*device_offset_x = 0;
*device_offset_y = 0;
*clone_out = cairo_surface_reference (src);

return CAIRO_STATUS_SUCCESS;
Expand All @@ -716,14 +720,20 @@ _cairo_xcb_surface_clone_similar (void *abstract_surface,
return surface->base.status;

clone = (cairo_xcb_surface_t *)
_cairo_xcb_surface_create_similar (surface, content,
image_src->width, image_src->height);
_cairo_xcb_surface_create_similar (surface, content, width, height);
if (clone->base.status)
return clone->base.status;

_draw_image_surface (clone, image_src, src_x, src_y,
width, height, src_x, src_y);
status = _draw_image_surface (clone, image_src,
src_x, src_y,
width, height, x, y);
if (status) {
cairo_surface_destroy (&clone->base);
return status;
}

*device_offset_x = src_x;
*device_offset_y = src_y;
*clone_out = &clone->base;

return CAIRO_STATUS_SUCCESS;
Expand Down
Loading

0 comments on commit 5b97ee6

Please sign in to comment.