Beispiel #1
0
/**
 * cairo_xcb_surface_set_size:
 * @surface: a #cairo_surface_t for the XCB backend
 * @width: the new width of the surface
 * @height: the new height of the surface
 *
 * Informs cairo of the new size of the XCB drawable underlying the
 * surface. For a surface created for a window (rather than a pixmap),
 * this function must be called each time the size of the window
 * changes. (For a subwindow, you are normally resizing the window
 * yourself, but for a toplevel window, it is necessary to listen for
 * ConfigureNotify events.)
 *
 * A pixmap can never change size, so it is never necessary to call
 * this function on a surface created for a pixmap.
 *
 * If cairo_surface_flush() wasn't called, some pending operations
 * might be discarded.
 **/
void
cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface,
			    int              width,
			    int              height)
{
    cairo_xcb_surface_t *surface;

    if (unlikely (abstract_surface->status))
	return;
    if (unlikely (abstract_surface->finished)) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
	return;
    }


    if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
	return;
    }

    if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_INVALID_SIZE));
	return;
    }

    surface = (cairo_xcb_surface_t *) abstract_surface;

    _drawable_changed(surface);
    surface->width  = width;
    surface->height = height;
}
Beispiel #2
0
void
cairo_tee_surface_add (cairo_surface_t *abstract_surface,
		       cairo_surface_t *target)
{
    cairo_tee_surface_t *surface;
    cairo_surface_wrapper_t slave;
    cairo_status_t status;

    if (unlikely (abstract_surface->status))
	return;

    if (abstract_surface->backend != &cairo_tee_surface_backend) {
	status = _cairo_surface_set_error (abstract_surface,
					   _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
	return;
    }

    if (unlikely (target->status)) {
	status = _cairo_surface_set_error (abstract_surface, target->status);
	return;
    }

    surface = (cairo_tee_surface_t *) abstract_surface;

    _cairo_surface_wrapper_init (&slave, target);
    status = _cairo_array_append (&surface->slaves, &slave);
    if (unlikely (status)) {
	_cairo_surface_wrapper_fini (&slave);
	status = _cairo_surface_set_error (&surface->base, status);
    }
}
Beispiel #3
0
void
cairo_gl_surface_set_size (cairo_surface_t *abstract_surface,
			   int              width,
			   int              height)
{
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;

    if (unlikely (abstract_surface->status))
	return;
    if (unlikely (abstract_surface->finished)) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
        return;
    }

    if (! _cairo_surface_is_gl (abstract_surface) ||
        _cairo_gl_surface_is_texture (surface)) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
	return;
    }

    if (surface->width != width || surface->height != height) {
	surface->needs_update = TRUE;
	surface->width = width;
	surface->height = height;
    }
}
Beispiel #4
0
/**
 * cairo_xcb_surface_set_size:
 * @surface: a #cairo_surface_t for the XCB backend
 * @width: the new width of the surface
 * @height: the new height of the surface
 *
 * Informs cairo of the new size of the XCB drawable underlying the
 * surface. For a surface created for a window (rather than a pixmap),
 * this function must be called each time the size of the window
 * changes. (For a subwindow, you are normally resizing the window
 * yourself, but for a toplevel window, it is necessary to listen for
 * ConfigureNotify events.)
 *
 * A pixmap can never change size, so it is never necessary to call
 * this function on a surface created for a pixmap.
 **/
void
cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface,
			    int              width,
			    int              height)
{
    cairo_xcb_surface_t *surface;
    cairo_status_t status_ignored;

    if (unlikely (abstract_surface->status))
	return;

    if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) {
	status_ignored = _cairo_surface_set_error (abstract_surface,
						   CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
	return;
    }

    if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) {
	status_ignored = _cairo_surface_set_error (abstract_surface,
						   CAIRO_STATUS_INVALID_SIZE);
	return;
    }

    surface = (cairo_xcb_surface_t *) abstract_surface;
    surface->width  = width;
    surface->height = height;
}
Beispiel #5
0
/**
 * cairo_xcb_surface_set_drawable:
 * @surface: a #cairo_surface_t for the XCB backend
 * @drawable: the new drawable of the surface
 * @width: the new width of the surface
 * @height: the new height of the surface
 *
 * Informs cairo of the new drawable and size of the XCB drawable underlying the
 * surface.
 *
 * If cairo_surface_flush() wasn't called, some pending operations
 * might be discarded.
 **/
void
cairo_xcb_surface_set_drawable (cairo_surface_t *abstract_surface,
				xcb_drawable_t  drawable,
				int             width,
				int             height)
{
    cairo_xcb_surface_t *surface;

    if (unlikely (abstract_surface->status))
	return;
    if (unlikely (abstract_surface->finished)) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
	return;
    }


    if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
	return;
    }

    if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_INVALID_SIZE));
	return;
    }

    surface = (cairo_xcb_surface_t *) abstract_surface;

    /* XXX: and what about this case? */
    if (surface->owns_pixmap)
	    return;

    _drawable_changed (surface);

    if (surface->drawable != drawable) {
	    cairo_status_t status;
	    status = _cairo_xcb_connection_acquire (surface->connection);
	    if (unlikely (status))
		    return;

	    if (surface->picture != XCB_NONE) {
		    _cairo_xcb_connection_render_free_picture (surface->connection,
							       surface->picture);
		    surface->picture = XCB_NONE;
	    }

	    _cairo_xcb_connection_release (surface->connection);

	    surface->drawable = drawable;
    }
    surface->width  = width;
    surface->height = height;
}
static void
_cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface)
{
    cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface;
    cairo_image_surface_t *image;
    cairo_image_surface_t *clone;
    void *extra;
    cairo_status_t status;

    /* We need to make an image copy of the original surface since the
     * snapshot may exceed the lifetime of the original device, i.e.
     * when we later need to use the snapshot the data may have already
     * been lost.
     */

    status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra);
    if (unlikely (status)) {
	snapshot->target = _cairo_surface_create_in_error (status);
	status = _cairo_surface_set_error (surface, status);
	return;
    }

    clone = (cairo_image_surface_t *)
	_cairo_image_surface_create_with_pixman_format (NULL,
							image->pixman_format,
							image->width,
							image->height,
							0);
    if (likely (clone->base.status == CAIRO_STATUS_SUCCESS)) {
	if (clone->stride == image->stride) {
	    memcpy (clone->data, image->data, image->stride * image->height);
	} else {
	    pixman_image_composite32 (PIXMAN_OP_SRC,
				      image->pixman_image, NULL, clone->pixman_image,
				      0, 0,
				      0, 0,
				      0, 0,
				      image->width, image->height);
	}
	clone->base.is_clear = FALSE;

	snapshot->clone = &clone->base;
    } else {
	snapshot->clone = &clone->base;
	status = _cairo_surface_set_error (surface, clone->base.status);
    }

    _cairo_surface_release_source_image (snapshot->target, image, extra);
    snapshot->target = snapshot->clone;
    snapshot->base.type = snapshot->target->type;
}
static void
_cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface)
{
    cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface;
    cairo_image_surface_t *image;
    cairo_surface_t *clone;
    void *extra;
    cairo_status_t status;

    TRACE ((stderr, "%s: target=%d\n",
	    __FUNCTION__, snapshot->target->unique_id));

    /* We need to make an image copy of the original surface since the
     * snapshot may exceed the lifetime of the original device, i.e.
     * when we later need to use the snapshot the data may have already
     * been lost.
     */

    CAIRO_MUTEX_LOCK (snapshot->mutex);

    if (snapshot->target->backend->snapshot != NULL) {
	clone = snapshot->target->backend->snapshot (snapshot->target);
	if (clone != NULL) {
	    assert (clone->status || ! _cairo_surface_is_snapshot (clone));
	    goto done;
	}
    }

    /* XXX copy to a similar surface, leave acquisition till later?
     * We should probably leave such decisions to the backend in case we
     * rely upon devices/connections like Xlib.
    */
    status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra);
    if (unlikely (status)) {
	snapshot->target = _cairo_surface_create_in_error (status);
	status = _cairo_surface_set_error (surface, status);
	goto unlock;
    }
    clone = image->base.backend->snapshot (&image->base);
    _cairo_surface_release_source_image (snapshot->target, image, extra);

done:
    status = _cairo_surface_set_error (surface, clone->status);
    snapshot->target = snapshot->clone = clone;
    snapshot->base.type = clone->type;
unlock:
    CAIRO_MUTEX_UNLOCK (snapshot->mutex);
}
Beispiel #8
0
void
cairo_tee_surface_remove (cairo_surface_t *abstract_surface,
			  cairo_surface_t *target)
{
    cairo_tee_surface_t *surface;
    cairo_surface_wrapper_t *slaves;
    int n, num_slaves;
    cairo_status_t status;

    if (unlikely (abstract_surface->status))
	return;
    if (unlikely (abstract_surface->finished)) {
	status = _cairo_surface_set_error (abstract_surface,
					   _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
	return;
    }

    if (abstract_surface->backend != &cairo_tee_surface_backend) {
	status = _cairo_surface_set_error (abstract_surface,
					   _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
	return;
    }

    surface = (cairo_tee_surface_t *) abstract_surface;
    if (target == surface->master.target) {
	status = _cairo_surface_set_error (abstract_surface,
					   _cairo_error (CAIRO_STATUS_INVALID_INDEX));
	return;
    }

    num_slaves = _cairo_array_num_elements (&surface->slaves);
    slaves = _cairo_array_index (&surface->slaves, 0);
    for (n = 0; n < num_slaves; n++) {
	if (slaves[n].target == target)
	    break;
    }

    if (n == num_slaves) {
	status = _cairo_surface_set_error (abstract_surface,
					   _cairo_error (CAIRO_STATUS_INVALID_INDEX));
	return;
    }

    _cairo_surface_wrapper_fini (&slaves[n]);
    for (n++; n < num_slaves; n++)
	slaves[n-1] = slaves[n];
    surface->slaves.num_elements--; /* XXX: cairo_array_remove()? */
}
cairo_status_t
_cairo_paginated_surface_set_size (cairo_surface_t	*surface,
				   int			 width,
				   int			 height)
{
    cairo_paginated_surface_t *paginated_surface;
    cairo_status_t status;
    cairo_rectangle_t recording_extents;

    assert (_cairo_surface_is_paginated (surface));

    paginated_surface = (cairo_paginated_surface_t *) surface;

    recording_extents.x = 0;
    recording_extents.y = 0;
    recording_extents.width = width;
    recording_extents.height = height;

    cairo_surface_destroy (paginated_surface->recording_surface);
    paginated_surface->recording_surface = cairo_recording_surface_create (paginated_surface->content,
									   &recording_extents);
    status = paginated_surface->recording_surface->status;
    if (unlikely (status))
	return (cairo_status_t)_cairo_surface_set_error (surface, (cairo_int_status_t)status);

    return (cairo_status_t)CAIRO_STATUS_SUCCESS;
}
/**
 * cairo_recording_surface_ink_extents:
 * @surface: a #cairo_recording_surface_t
 * @x0: the x-coordinate of the top-left of the ink bounding box
 * @y0: the y-coordinate of the top-left of the ink bounding box
 * @width: the width of the ink bounding box
 * @height: the height of the ink bounding box
 *
 * Measures the extents of the operations stored within the recording-surface.
 * This is useful to compute the required size of an image surface (or
 * equivalent) into which to replay the full sequence of drawing operations.
 *
 * Since: 1.10
 **/
void
cairo_recording_surface_ink_extents (cairo_surface_t *surface,
				     double *x0,
				     double *y0,
				     double *width,
				     double *height)
{
    cairo_status_t status;
    cairo_box_t bbox;

    memset (&bbox, 0, sizeof (bbox));

    if (! _cairo_surface_is_recording (surface)) {
	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
	goto DONE;
    }

    status = _recording_surface_get_ink_bbox ((cairo_recording_surface_t *) surface,
					 &bbox,
					 NULL);
    if (unlikely (status))
	status = _cairo_surface_set_error (surface, status);

DONE:
    if (x0)
	*x0 = _cairo_fixed_to_double (bbox.p1.x);
    if (y0)
	*y0 = _cairo_fixed_to_double (bbox.p1.y);
    if (width)
	*width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x);
    if (height)
	*height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y);
}
Beispiel #11
0
/* This does the necessary fixup when a surface's drawable or size changed. */
static void
_drawable_changed (cairo_xcb_surface_t *surface)
{
    _cairo_surface_set_error (&surface->base,
	    _cairo_surface_begin_modification (&surface->base));
    _cairo_boxes_clear (&surface->fallback_damage);
    cairo_surface_destroy (&surface->fallback->base);

    surface->deferred_clear = FALSE;
    surface->fallback = NULL;
}
static cairo_status_t
_start_page (cairo_paginated_surface_t *surface)
{
    if (surface->target->status)
	return surface->target->status;

    if (! surface->backend->start_page)
	return CAIRO_STATUS_SUCCESS;

    return _cairo_surface_set_error (surface->target,
	                        surface->backend->start_page (surface->target));
}
Beispiel #13
0
void
cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface)
{
    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;

    if (unlikely (abstract_surface->status))
	return;
    if (unlikely (abstract_surface->finished)) {
	_cairo_surface_set_error (abstract_surface,
				  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
        return;
    }

    if (! _cairo_surface_is_gl (abstract_surface)) {
	_cairo_surface_set_error (abstract_surface,
				  CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
	return;
    }

    if (! _cairo_gl_surface_is_texture (surface)) {
	cairo_gl_context_t *ctx;
        cairo_status_t status;

        status = _cairo_gl_context_acquire (surface->base.device, &ctx);
        if (unlikely (status))
            return;

	/* For swapping on EGL, at least, we need a valid context/target. */
	_cairo_gl_context_set_destination (ctx, surface);
	/* And in any case we should flush any pending operations. */
	_cairo_gl_composite_flush (ctx);

	ctx->swap_buffers (ctx, surface);

        status = _cairo_gl_context_release (ctx, status);
        if (status)
            status = _cairo_surface_set_error (abstract_surface, status);
    }
}
/**
 * cairo_xcb_surface_set_size:
 * @surface: a #cairo_surface_t for the XCB backend
 * @width: the new width of the surface
 * @height: the new height of the surface
 *
 * Informs cairo of the new size of the XCB drawable underlying the
 * surface. For a surface created for a window (rather than a pixmap),
 * this function must be called each time the size of the window
 * changes. (For a subwindow, you are normally resizing the window
 * yourself, but for a toplevel window, it is necessary to listen for
 * ConfigureNotify events.)
 *
 * A pixmap can never change size, so it is never necessary to call
 * this function on a surface created for a pixmap.
 **/
void
cairo_xcb_surface_set_size (cairo_surface_t *surface,
			    int              width,
			    int              height)
{
    cairo_xcb_surface_t *xcb_surface = (cairo_xcb_surface_t *)surface;

    if (! _cairo_surface_is_xcb (surface)) {
	_cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
	return;
    }

    xcb_surface->width = width;
    xcb_surface->height = height;
}
cairo_status_t
_cairo_paginated_surface_set_size (cairo_surface_t	*surface,
				   int			 width,
				   int			 height)
{
    cairo_paginated_surface_t *paginated_surface;
    cairo_status_t status;

    assert (_cairo_surface_is_paginated (surface));

    paginated_surface = (cairo_paginated_surface_t *) surface;

    paginated_surface->width = width;
    paginated_surface->height = height;

    cairo_surface_destroy (paginated_surface->meta);
    paginated_surface->meta = _cairo_meta_surface_create (paginated_surface->content,
							  width, height);
    status = cairo_surface_status (paginated_surface->meta);
    if (status)
	return _cairo_surface_set_error (surface, status);

    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_paint_page (cairo_paginated_surface_t *surface)
{
    cairo_surface_t *analysis;
    cairo_status_t status;
    cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback;

    if (surface->target->status)
	return surface->target->status;

    analysis = _cairo_analysis_surface_create (surface->target,
					       surface->width, surface->height);
    if (analysis->status)
	return _cairo_surface_set_error (surface->target, analysis->status);

    surface->backend->set_paginated_mode (surface->target,
	                                  CAIRO_PAGINATED_MODE_ANALYZE);
    status = _cairo_meta_surface_replay_and_create_regions (surface->meta,
	                                                    analysis);
    if (status || analysis->status) {
	if (status == CAIRO_STATUS_SUCCESS)
	    status = analysis->status;
	goto FAIL;
    }

     if (surface->backend->set_bounding_box) {
	 cairo_box_t bbox;

	 _cairo_analysis_surface_get_bounding_box (analysis, &bbox);
	 status = surface->backend->set_bounding_box (surface->target, &bbox);
	 if (status)
	     goto FAIL;
     }

    if (surface->backend->set_fallback_images_required) {
	cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis);

	status = surface->backend->set_fallback_images_required (surface->target,
								 has_fallbacks);
	if (status)
	    goto FAIL;
    }

    /* Finer grained fallbacks are currently only supported for some
     * surface types */
    switch (surface->target->type) {
        case CAIRO_SURFACE_TYPE_PDF:
        case CAIRO_SURFACE_TYPE_PS:
        case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
            has_supported = _cairo_analysis_surface_has_supported (analysis);
            has_page_fallback = FALSE;
            has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis);
            break;

	case CAIRO_SURFACE_TYPE_IMAGE:
	case CAIRO_SURFACE_TYPE_XLIB:
	case CAIRO_SURFACE_TYPE_XCB:
	case CAIRO_SURFACE_TYPE_GLITZ:
	case CAIRO_SURFACE_TYPE_QUARTZ:
	case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE:
	case CAIRO_SURFACE_TYPE_WIN32:
	case CAIRO_SURFACE_TYPE_BEOS:
	case CAIRO_SURFACE_TYPE_DIRECTFB:
	case CAIRO_SURFACE_TYPE_SVG:
	case CAIRO_SURFACE_TYPE_OS2:
        default:
            if (_cairo_analysis_surface_has_unsupported (analysis)) {
                has_supported = FALSE;
                has_page_fallback = TRUE;
            } else {
                has_supported = TRUE;
                has_page_fallback = FALSE;
            }
            has_finegrained_fallback = FALSE;
            break;
    }

    if (has_supported) {
	surface->backend->set_paginated_mode (surface->target,
		                              CAIRO_PAGINATED_MODE_RENDER);

	status = _cairo_meta_surface_replay_region (surface->meta,
						    surface->target,
						    CAIRO_META_REGION_NATIVE);
	assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
	if (status)
	    goto FAIL;
    }

    if (has_page_fallback) {
	cairo_box_int_t box;

	surface->backend->set_paginated_mode (surface->target,
		                              CAIRO_PAGINATED_MODE_FALLBACK);

	box.p1.x = 0;
	box.p1.y = 0;
	box.p2.x = surface->width;
	box.p2.y = surface->height;
	status = _paint_fallback_image (surface, &box);
	if (status)
	    goto FAIL;
    }

    if (has_finegrained_fallback) {
        cairo_region_t *region;
        cairo_box_int_t *boxes;
        int num_boxes, i;

	surface->backend->set_paginated_mode (surface->target,
		                              CAIRO_PAGINATED_MODE_FALLBACK);

    /* Reset clip region before drawing the fall back images */
	status = _cairo_surface_intersect_clip_path (surface->target,
						     NULL,
						     CAIRO_FILL_RULE_WINDING,
						     CAIRO_GSTATE_TOLERANCE_DEFAULT,
						     CAIRO_ANTIALIAS_DEFAULT);
	if (status)
	    goto FAIL;

	region = _cairo_analysis_surface_get_unsupported (analysis);
	status = _cairo_region_get_boxes (region, &num_boxes, &boxes);
	if (status)
	    goto FAIL;
	for (i = 0; i < num_boxes; i++) {
	    status = _paint_fallback_image (surface, &boxes[i]);
	    if (status) {
                _cairo_region_boxes_fini (region, boxes);
		goto FAIL;
            }
	}
        _cairo_region_boxes_fini (region, boxes);
    }

  FAIL:
    cairo_surface_destroy (analysis);

    return _cairo_surface_set_error (surface->target, status);
}
static cairo_status_t
_cairo_meta_surface_replay_internal (cairo_surface_t	     *surface,
				     cairo_surface_t	     *target,
				     cairo_meta_replay_type_t type,
				     cairo_meta_region_type_t region)
{
    cairo_meta_surface_t *meta;
    cairo_command_t *command, **elements;
    int i, num_elements;
    cairo_int_status_t status, status2;
    cairo_clip_t clip, *old_clip;
    cairo_bool_t has_device_transform = _cairo_surface_has_device_transform (target);
    cairo_matrix_t *device_transform = &target->device_transform;
    cairo_path_fixed_t path_copy, *dev_path;

    if (surface->status)
	return surface->status;

    if (target->status)
	return _cairo_surface_set_error (surface, target->status);

    meta = (cairo_meta_surface_t *) surface;
    status = CAIRO_STATUS_SUCCESS;

    _cairo_clip_init (&clip, target);
    old_clip = _cairo_surface_get_clip (target);

    num_elements = meta->commands.num_elements;
    elements = _cairo_array_index (&meta->commands, 0);
    for (i = meta->replay_start_idx; i < num_elements; i++) {
	command = elements[i];

	if (type == CAIRO_META_REPLAY && region != CAIRO_META_REGION_ALL) {
	    if (command->header.region != region)
		continue;
        }

	/* For all commands except intersect_clip_path, we have to
	 * ensure the current clip gets set on the surface. */
	if (command->header.type != CAIRO_COMMAND_INTERSECT_CLIP_PATH) {
	    status = _cairo_surface_set_clip (target, &clip);
	    if (status)
		break;
	}

	dev_path = _cairo_command_get_path (command);
	if (dev_path && has_device_transform) {
	    status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
	    if (status)
		break;
	    _cairo_path_fixed_transform (&path_copy, device_transform);
	    dev_path = &path_copy;
	}

	switch (command->header.type) {
	case CAIRO_COMMAND_PAINT:
	    status = _cairo_surface_paint (target,
					   command->paint.op,
					   &command->paint.source.base);
	    break;
	case CAIRO_COMMAND_MASK:
	    status = _cairo_surface_mask (target,
					  command->mask.op,
					  &command->mask.source.base,
					  &command->mask.mask.base);
	    break;
	case CAIRO_COMMAND_STROKE:
	{
	    cairo_matrix_t dev_ctm = command->stroke.ctm;
	    cairo_matrix_t dev_ctm_inverse = command->stroke.ctm_inverse;

	    if (has_device_transform) {
		cairo_matrix_multiply (&dev_ctm, &dev_ctm, device_transform);
		cairo_matrix_multiply (&dev_ctm_inverse,
				       &target->device_transform_inverse,
				       &dev_ctm_inverse);
	    }

	    status = _cairo_surface_stroke (target,
					    command->stroke.op,
					    &command->stroke.source.base,
					    dev_path,
					    &command->stroke.style,
					    &dev_ctm,
					    &dev_ctm_inverse,
					    command->stroke.tolerance,
					    command->stroke.antialias);
	    break;
	}
	case CAIRO_COMMAND_FILL:
	{
	    cairo_command_t *stroke_command;

	    if (type != CAIRO_META_CREATE_REGIONS)
		stroke_command = (i < num_elements - 1) ? elements[i + 1] : NULL;
	    else
		stroke_command = NULL;

	    if (stroke_command != NULL &&
		type == CAIRO_META_REPLAY && region != CAIRO_META_REGION_ALL)
	    {
		if (stroke_command->header.region != region)
		    stroke_command = NULL;
	    }
	    if (stroke_command != NULL &&
		stroke_command->header.type == CAIRO_COMMAND_STROKE &&
		_cairo_path_fixed_is_equal (dev_path, _cairo_command_get_path (stroke_command))) {
		cairo_matrix_t dev_ctm;
		cairo_matrix_t dev_ctm_inverse;

		dev_ctm = stroke_command->stroke.ctm;
		dev_ctm_inverse = stroke_command->stroke.ctm_inverse;

		if (has_device_transform) {
		    cairo_matrix_multiply (&dev_ctm, &dev_ctm, device_transform);
		    cairo_matrix_multiply (&dev_ctm_inverse,
					   &surface->device_transform_inverse,
					   &dev_ctm_inverse);
		}

		status = _cairo_surface_fill_stroke (target,
						     command->fill.op,
						     &command->fill.source.base,
						     command->fill.fill_rule,
						     command->fill.tolerance,
						     command->fill.antialias,
						     dev_path,
						     stroke_command->stroke.op,
						     &stroke_command->stroke.source.base,
						     &stroke_command->stroke.style,
						     &dev_ctm,
						     &dev_ctm_inverse,
						     stroke_command->stroke.tolerance,
						     stroke_command->stroke.antialias);
		i++;
	    } else
		status = _cairo_surface_fill (target,
					      command->fill.op,
					      &command->fill.source.base,
					      dev_path,
					      command->fill.fill_rule,
					      command->fill.tolerance,
					      command->fill.antialias);
	    break;
	}
	case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
	{
	    cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs;
	    cairo_glyph_t *dev_glyphs;
	    int i, num_glyphs = command->show_text_glyphs.num_glyphs;

            /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed
	     * to modify the glyph array that's passed in.  We must always
	     * copy the array before handing it to the backend.
	     */
	    dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
	    if (dev_glyphs == NULL) {
		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
		break;
	    }

	    if (has_device_transform) {
		for (i = 0; i < num_glyphs; i++) {
		    dev_glyphs[i] = glyphs[i];
		    cairo_matrix_transform_point (device_transform,
						  &dev_glyphs[i].x,
						  &dev_glyphs[i].y);
		}
	    } else {
		memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
	    }

	    status = _cairo_surface_show_text_glyphs	(target,
							 command->show_text_glyphs.op,
							 &command->show_text_glyphs.source.base,
							 command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
							 dev_glyphs, num_glyphs,
							 command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
							 command->show_text_glyphs.cluster_flags,
							 command->show_text_glyphs.scaled_font);

	    free (dev_glyphs);
	    break;
	}
	case CAIRO_COMMAND_INTERSECT_CLIP_PATH:
	    /* XXX Meta surface clipping is broken and requires some
	     * cairo-gstate.c rewriting.  Work around it for now. */
	    if (dev_path == NULL)
		_cairo_clip_reset (&clip);
	    else
		status = _cairo_clip_clip (&clip, dev_path,
					   command->intersect_clip_path.fill_rule,
					   command->intersect_clip_path.tolerance,
					   command->intersect_clip_path.antialias,
					   target);
	    break;
	default:
	    ASSERT_NOT_REACHED;
	}

	if (dev_path == &path_copy)
	    _cairo_path_fixed_fini (&path_copy);

	if (type == CAIRO_META_CREATE_REGIONS) {
	    if (status == CAIRO_STATUS_SUCCESS) {
		command->header.region = CAIRO_META_REGION_NATIVE;
	    } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
		command->header.region = CAIRO_META_REGION_IMAGE_FALLBACK;
		status = CAIRO_STATUS_SUCCESS;
	    }
	}

	if (status)
	    break;
    }

    _cairo_clip_reset (&clip);
    status2 = _cairo_surface_set_clip (target, old_clip);
    if (status == CAIRO_STATUS_SUCCESS)
	status = status2;

    return _cairo_surface_set_error (surface, status);
}
static cairo_int_status_t
_paint_page (cairo_paginated_surface_t *surface)
{
    cairo_surface_t *analysis;
    cairo_int_status_t status;
    cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback;

    if (unlikely (surface->target->status))
	return (cairo_int_status_t)surface->target->status;

    analysis = _cairo_analysis_surface_create (surface->target);
    if (unlikely (analysis->status))
	return (cairo_int_status_t)_cairo_surface_set_error (surface->target, (cairo_int_status_t)analysis->status);

    surface->backend->set_paginated_mode (surface->target,
	                                  CAIRO_PAGINATED_MODE_ANALYZE);
    status = (cairo_int_status_t)_cairo_recording_surface_replay_and_create_regions (surface->recording_surface,
								 analysis);
    if (status)
	goto FAIL;

    assert (analysis->status == CAIRO_STATUS_SUCCESS);

     if (surface->backend->set_bounding_box) {
	 cairo_box_t bbox;

	 _cairo_analysis_surface_get_bounding_box (analysis, &bbox);
	 status = surface->backend->set_bounding_box (surface->target, &bbox);
	 if (unlikely (status))
	     goto FAIL;
     }

    if (surface->backend->set_fallback_images_required) {
	cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis);

	status = surface->backend->set_fallback_images_required (surface->target,
								 has_fallbacks);
	if (unlikely (status))
	    goto FAIL;
    }

    /* Finer grained fallbacks are currently only supported for some
     * surface types */
    if (surface->backend->supports_fine_grained_fallbacks != NULL &&
	surface->backend->supports_fine_grained_fallbacks (surface->target))
    {
	has_supported = _cairo_analysis_surface_has_supported (analysis);
	has_page_fallback = FALSE;
	has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis);
    }
    else
    {
	if (_cairo_analysis_surface_has_unsupported (analysis)) {
	    has_supported = FALSE;
	    has_page_fallback = TRUE;
	} else {
	    has_supported = TRUE;
	    has_page_fallback = FALSE;
	}
	has_finegrained_fallback = FALSE;
    }

    if (has_supported) {
	surface->backend->set_paginated_mode (surface->target,
		                              CAIRO_PAGINATED_MODE_RENDER);

	status = (cairo_int_status_t)_cairo_recording_surface_replay_region (surface->recording_surface,
							 NULL,
							 surface->target,
							 CAIRO_RECORDING_REGION_NATIVE);
	assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
	if (unlikely (status))
	    goto FAIL;
    }

    if (has_page_fallback) {
	cairo_rectangle_int_t extents;
	cairo_bool_t is_bounded;

	surface->backend->set_paginated_mode (surface->target,
		                              CAIRO_PAGINATED_MODE_FALLBACK);

	is_bounded = _cairo_surface_get_extents (surface->target, &extents);
	if (! is_bounded) {
	    status = CAIRO_INT_STATUS_UNSUPPORTED;
	    goto FAIL;
	}

	status = _paint_fallback_image (surface, &extents);
	if (unlikely (status))
	    goto FAIL;
    }

    if (has_finegrained_fallback) {
        cairo_region_t *region;
        int num_rects, i;

	surface->backend->set_paginated_mode (surface->target,
		                              CAIRO_PAGINATED_MODE_FALLBACK);

	region = _cairo_analysis_surface_get_unsupported (analysis);

	num_rects = cairo_region_num_rectangles (region);
	for (i = 0; i < num_rects; i++) {
	    cairo_rectangle_int_t rect;

	    cairo_region_get_rectangle (region, i, &rect);
	    status = _paint_fallback_image (surface, &rect);
	    if (unlikely (status))
		goto FAIL;
	}
    }

  FAIL:
    cairo_surface_destroy (analysis);

    return _cairo_surface_set_error (surface->target, status);
}
static cairo_status_t
_cairo_recording_surface_replay_internal (cairo_surface_t	     *surface,
					  const cairo_rectangle_int_t *surface_extents,
					  cairo_surface_t	     *target,
					  cairo_recording_replay_type_t type,
					  cairo_recording_region_type_t region)
{
    cairo_recording_surface_t *recording_surface;
    cairo_command_t **elements;
    int i, num_elements;
    cairo_int_status_t status;
    cairo_surface_wrapper_t wrapper;

    if (unlikely (surface->status))
	return surface->status;

    if (unlikely (target->status))
	return target->status;

    if (unlikely (surface->finished))
	return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);

    if (surface->is_clear)
	return CAIRO_STATUS_SUCCESS;

    assert (_cairo_surface_is_recording (surface));

    _cairo_surface_wrapper_init (&wrapper, target);
    _cairo_surface_wrapper_set_extents (&wrapper, surface_extents);

    recording_surface = (cairo_recording_surface_t *) surface;
    status = CAIRO_STATUS_SUCCESS;

    num_elements = recording_surface->commands.num_elements;
    elements = _cairo_array_index (&recording_surface->commands, 0);

    for (i = recording_surface->replay_start_idx; i < num_elements; i++) {
	cairo_command_t *command = elements[i];

	if (type == CAIRO_RECORDING_REPLAY && region != CAIRO_RECORDING_REGION_ALL) {
	    if (command->header.region != region)
		continue;
        }

	switch (command->header.type) {
	case CAIRO_COMMAND_PAINT:
	    status = _cairo_surface_wrapper_paint (&wrapper,
						   command->header.op,
						   &command->paint.source.base,
						   _clip (command));
	    break;

	case CAIRO_COMMAND_MASK:
	    status = _cairo_surface_wrapper_mask (&wrapper,
						  command->header.op,
						  &command->mask.source.base,
						  &command->mask.mask.base,
						  _clip (command));
	    break;

	case CAIRO_COMMAND_STROKE:
	{
	    status = _cairo_surface_wrapper_stroke (&wrapper,
						    command->header.op,
						    &command->stroke.source.base,
						    &command->stroke.path,
						    &command->stroke.style,
						    &command->stroke.ctm,
						    &command->stroke.ctm_inverse,
						    command->stroke.tolerance,
						    command->stroke.antialias,
						    _clip (command));
	    break;
	}
	case CAIRO_COMMAND_FILL:
	{
	    cairo_command_t *stroke_command;

	    stroke_command = NULL;
	    if (type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1)
		stroke_command = elements[i + 1];

	    if (stroke_command != NULL &&
		type == CAIRO_RECORDING_REPLAY &&
		region != CAIRO_RECORDING_REGION_ALL)
	    {
		if (stroke_command->header.region != region)
		    stroke_command = NULL;
	    }

	    if (stroke_command != NULL &&
		stroke_command->header.type == CAIRO_COMMAND_STROKE &&
		_cairo_path_fixed_is_equal (&command->fill.path,
					    &stroke_command->stroke.path))
	    {
		status = _cairo_surface_wrapper_fill_stroke (&wrapper,
							     command->header.op,
							     &command->fill.source.base,
							     command->fill.fill_rule,
							     command->fill.tolerance,
							     command->fill.antialias,
							     &command->fill.path,
							     stroke_command->header.op,
							     &stroke_command->stroke.source.base,
							     &stroke_command->stroke.style,
							     &stroke_command->stroke.ctm,
							     &stroke_command->stroke.ctm_inverse,
							     stroke_command->stroke.tolerance,
							     stroke_command->stroke.antialias,
							     _clip (command));
		i++;
	    }
	    else
	    {
		status = _cairo_surface_wrapper_fill (&wrapper,
						      command->header.op,
						      &command->fill.source.base,
						      &command->fill.path,
						      command->fill.fill_rule,
						      command->fill.tolerance,
						      command->fill.antialias,
						      _clip (command));
	    }
	    break;
	}
	case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
	{
	    cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs;
	    cairo_glyph_t *glyphs_copy;
	    int num_glyphs = command->show_text_glyphs.num_glyphs;

            /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed
	     * to modify the glyph array that's passed in.  We must always
	     * copy the array before handing it to the backend.
	     */
	    glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
	    if (unlikely (glyphs_copy == NULL)) {
		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
		break;
	    }

	    memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);

	    status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
							      command->header.op,
							      &command->show_text_glyphs.source.base,
							      command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
							      glyphs_copy, num_glyphs,
							      command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
							      command->show_text_glyphs.cluster_flags,
							      command->show_text_glyphs.scaled_font,
							      _clip (command));
	    free (glyphs_copy);
	    break;
	}
	default:
	    ASSERT_NOT_REACHED;
	}

	if (type == CAIRO_RECORDING_CREATE_REGIONS) {
	    if (status == CAIRO_STATUS_SUCCESS) {
		command->header.region = CAIRO_RECORDING_REGION_NATIVE;
	    } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
		command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK;
		status = CAIRO_STATUS_SUCCESS;
	    } else {
		assert (_cairo_status_is_error (status));
	    }
	}

	if (unlikely (status))
	    break;
    }

    /* free up any caches */
    for (i = recording_surface->replay_start_idx; i < num_elements; i++) {
	cairo_command_t *command = elements[i];

	_cairo_clip_drop_cache (&command->header.clip);
    }

    _cairo_surface_wrapper_fini (&wrapper);

    return _cairo_surface_set_error (surface, status);
}
cairo_int_status_t
_cairo_recording_surface_get_path (cairo_surface_t    *surface,
				   cairo_path_fixed_t *path)
{
    cairo_recording_surface_t *recording_surface;
    cairo_command_t **elements;
    int i, num_elements;
    cairo_int_status_t status;

    if (surface->status)
	return surface->status;

    recording_surface = (cairo_recording_surface_t *) surface;
    status = CAIRO_STATUS_SUCCESS;

    num_elements = recording_surface->commands.num_elements;
    elements = _cairo_array_index (&recording_surface->commands, 0);
    for (i = recording_surface->replay_start_idx; i < num_elements; i++) {
	cairo_command_t *command = elements[i];

	switch (command->header.type) {
	case CAIRO_COMMAND_PAINT:
	case CAIRO_COMMAND_MASK:
	    status = CAIRO_INT_STATUS_UNSUPPORTED;
	    break;

	case CAIRO_COMMAND_STROKE:
	{
	    cairo_traps_t traps;

	    _cairo_traps_init (&traps);

	    /* XXX call cairo_stroke_to_path() when that is implemented */
	    status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path,
							&command->stroke.style,
							&command->stroke.ctm,
							&command->stroke.ctm_inverse,
							command->stroke.tolerance,
							&traps);

	    if (status == CAIRO_STATUS_SUCCESS)
		status = _cairo_traps_path (&traps, path);

	    _cairo_traps_fini (&traps);
	    break;
	}
	case CAIRO_COMMAND_FILL:
	{
	    status = _cairo_path_fixed_append (path,
					       &command->fill.path, CAIRO_DIRECTION_FORWARD,
					       0, 0);
	    break;
	}
	case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
	{
	    status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font,
						    command->show_text_glyphs.glyphs,
						    command->show_text_glyphs.num_glyphs,
						    path);
	    break;
	}

	default:
	    ASSERT_NOT_REACHED;
	}

	if (unlikely (status))
	    break;
    }

    return _cairo_surface_set_error (surface, status);
}