[cairo] Meta surface update and new ps surface

Kristian Høgsberg krh at bitplanet.net
Wed Jun 22 22:55:09 PDT 2005


Carl Worth wrote:
> On Fri, 17 Jun 2005 15:17:58 -0400, Kristian Høgsberg wrote:
> 
>>Using the meta surface I posted a few weeks back, I've been working on a 
>>better postscript backend.  I'm attaching a patch with the meta surface 
>>and the new postscript backend.  The meta surface hasn't changed much 
>>from what I posted earlier; I've updated it to support the new path 
>>clipping and fixed a couple of bugs.
> 
> 
> Kristian,
> 
> You're doing fantastic work here. Keep it up!
> 
> I haven't looked at the postscript-related piece yet, (I'll tackle
> that in a separate message), but here's some review of
> cairo-meta-surface.c, (and much of this already communicated in IRC).

I've committed the part of the patch that rearranged the font subsetting
code, and I'm attaching an updated version of the meta surface part of
the patch.  Once we've landed the meta surface, I'll post an updated
version of the postscript part.

>>+static void
>>+_meta_pattern_init_copy (cairo_pattern_t *dest, cairo_pattern_t *src)
>>+{
>>+    if (src)
>>+	_cairo_pattern_init_copy (dest, src);
>>+    else
>>+	dest->ref_count = 0;
>>+}
>>+
>>+static void
>>+_meta_pattern_fini (cairo_pattern_t *pattern)
>>+{
>>+    if (pattern->ref_count > 0)
>>+	_cairo_pattern_fini (pattern);
>>+}
>>+
>>+static cairo_pattern_t *
>>+_meta_pattern_get (cairo_pattern_t *pattern)
>>+{
>>+    if (pattern->ref_count > 0)
>>+	return pattern;
>>+    else
>>+	return NULL;
>>+}
> 
> 
> This allows the meta-surface to treat NULL as a legitimate pattern. I
> think that makes sense, but this should happen in
> _cairo_pattern_init_copy instead, (and the result should be
> communicated with pattern->status == CAIRO_STATUS_NULL_POINTER rather
> than returning NULL). So the three functions above should disappear I
> think.

Thinking a bit about this approach, I'm not sure I like it.  You're
suggesting making _cairo_pattern_init_copy (&pattern, NULL) initialize
'pattern' to an error object (pattern.status ==
CAIRO_STATUS_NULL_POINTER) but the cases I'm trying to handle are
situations were a NULL pattern is entirely legal.  For example, when
passing a NULL mask pattern to surface->backend->composite.  I can think
of two other ideas that I like better: the one I'm using in the new
patch is to make _cairo_pattern_init_copy do nothing if 'other' is NULL,
and for convenience return NULL in that case, otherwise return
'pattern'.  I'm using it like this:

      command->pattern_pointer =
          _cairo_pattern_init_copy (&command->pattern.base, pattern);

which is a little awkward, but command->pattern_pointer is easy to use
when replaying, and _cairo_pattern_fini is changed to do nothing when
given a NULL pointer so this works pretty well too.

The other idea is to make a new pattern type, CAIRO_PATTERN_NULL, which
means that the pattern was initialized from a NULL pointer.
_cairo_pattern_fini can easily be changed to do nothing for this type of
pattern, but when replaying I'll have to do something like

      command->pattern.base.type == CAIRO_PATTERN_NULL ?
          NULL : &cairo->command.base

when calling the surface functions.

>>+    command->type = CAIRO_COMMAND_SET_CLIP_REGION;
>>+    command->region = region;
> 
> 
> Oops. The region should be copied here.

Oops indeed.  Fixed.

>>+    if (path) {
>>+	status = _cairo_path_fixed_init_copy (&command->path, path);
>>+	if (status) {
>>+	    free (command);
>>+	    return status;
>>+	}
>>+	command->path_pointer = &command->path;
>>+    } else {
>>+	command->path_pointer = NULL;
>>+    }
> 
> 
> Here's another place where allowing NULL as a legitimate object value
> could simplify things. It's certainly not a prerequisite for this
> patch, but in general, allowing NULL to "init_copy" would work well
> for any object that uses status-based error handling. And it is
> consistent with allowing NULL to "reference" and "destroy" which I
> recently applied to most objects in the implementation.

Again, I don't like initializing 'path' to an error object for something
that's valid use.  Not sure how to fix this one, none of my two
suggestions above apply here.  I think the current solution works well
enough, though, and it's only this one place in the file.

>>+static cairo_int_status_t
>>+_cairo_meta_surface_get_extents (void		   *abstract_surface,
>>+				 cairo_rectangle_t *rectangle)
>>+{
>>+    cairo_meta_surface_t *meta = abstract_surface;
>>+
>>+    /* Currently this is used for getting the extents of the surface
>>+     * before calling cairo_paint().  This is the only this that
>>+     * requires the meta surface to have an explicit size.  If paint
>>+     * was just a backend function, this would not be necessary. */
> 
> 
> This seems like a valid argument, and I think I'd like to see
> surface->backend->paint land before the current patch. I was really
> hoping to see cairo_meta_surface_t land as a size-less surface.

Do you want to block this patch on surface->backend->paint?  It's a
fairly simple change to the meta surface if we decide to go with paint
as a backend functions; I don't see a problem in committing the meta
surface first.

>>+    command = malloc (sizeof (cairo_command_fill_path_t));
>>+    if (command == NULL)
>>+	return CAIRO_STATUS_NO_MEMORY;
>>+
>>+    command->type = CAIRO_COMMAND_FILL_PATH;
>>+    command->operator = operator;
>>+    _meta_pattern_init_copy (&command->pattern.base, pattern);
>>+    status = _cairo_path_fixed_init_copy (&command->path, path);
> 
> 
> The previous functions all copied the arguments before allocating the
> command. All of the functions should be consistent one way or the
> other.

Fixed - now command is allocated first thing and then the arguments are
copied in order of appearance.

>>+static const cairo_surface_backend_t cairo_meta_surface_backend = {
>>+    _cairo_meta_surface_create_similar,
>>+    _cairo_meta_surface_finish,
>>+    NULL, /* acquire_source_image */
>>+    NULL, /* release_source_image */
>>+    NULL, /* acquire_dest_image */
>>+    NULL, /* release_dest_image */
>>+    NULL, /* clone_similar */
> 
> 
> I don't know if it would be useful, but it occurs to me that the
> acquire functions could be implemented in terms of replaying against
> the image backend. No action needed here though, just thinking out
> loud.

I don't think you want the meta surface to record image fallbacks
though.  It should record the highest level drawing commands sent to it
and do fallbacks as appropriate when replaying, determined by the
capabilities of the surface it is replayed against.

>>+cairo_int_status_t
>>+_cairo_meta_surface_replay (cairo_surface_t			*surface,
>>+			    const cairo_surface_backend_t	*backend,
>>+			    void				*closure)
>>+{
> 
> 
> Couldn't closure just as well be of type cairo_surface_t* rather than
> void?

I've changed the prototype to:

      cairo_int_status_t
      _cairo_meta_surface_replay (cairo_surface_t *surface,
                                  cairo_surface_t *target)

which should be much more obvious.  In the ps surface, I'm replaying the
meta surface against a few specialized backends -- that'll will have to
change to look something like this:

	font_subset_surface_t surface;

	/* initialize font_subset_surface_t fields */
	surface->subsets = NULL;
	_cairo_surface_init (&surface->backend, &font_subset_backend);
	_cairo_meta_surface_replay (meta_surface, &surface);

	/* do stuff with surface->subsets, then free them */
	_cairo_surface_fini (&surface);
	
which is a little more work, but a cleaner approach than passing in a
rogue backend and a void pointer.

>>+	case CAIRO_COMMAND_COMPOSITE:
>>+	    if (backend->composite == NULL)
>>+		break;
> 
> 
> Just giving up here doesn't seem like the right thing.
> 
> Don't we really want to call into the same logic as
> cairo_surface_composite?
> 
> The only problem with doing that directly right now is that
> cairo_surface_composite currently accepts a single "cairo_surface_t
> *dst" which it uses both to lookup dst->backend->composite and as the
> destination pointer to pass to that function. Here, however, we have
> backend and closure as separate parameters.
> 
> So, something would have to change a bit, but it does seem to me that
> we would want to call into the same logic.

With the changes to _cairo_meta_surface_replay I described above, it's
now pretty easy to use the _cairo_surface_* functions.  The bailing out
on backend->composite == NULL above isn't about giving up, though, the
issue is that for determining font subsets I only care about
surface->backend->show_glyphs.  I wrote the check to avoid write a bunch
of 'return CAIRO_STATUS_SUCCESS;' stubs, but of course, that's not going
to work for replaying against our standard surface types.

However, we'll need a bigger rearrangement of the code, since we also
need to be able to fallback from fill_path to composite_trapezoids, i.e.
we need to do the tessellation step during the replay.  For clipping
it's even worse, since we need to maintain our entire clipping state
during replay.

>>+typedef enum {
>>+    CAIRO_COMMAND_COMPOSITE,
>>+    CAIRO_COMMAND_FILL_RECTANGLES,
>>+    CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS,
>>+    CAIRO_COMMAND_COPY_PAGE,
>>+    CAIRO_COMMAND_SHOW_PAGE,
>>+    CAIRO_COMMAND_SET_CLIP_REGION,
>>+    CAIRO_COMMAND_INTERSECT_CLIP_PATH,
>>+    CAIRO_COMMAND_SHOW_GLYPHS,
>>+    CAIRO_COMMAND_FILL_PATH
>>+} cairo_command_type_t;
> 
> 
> Seeing those names so close together points out some
> inconsistencies. We should probably rename "show_glyphs" to
> "composite_glyphs" throughout the backend interface, (though that can
> be after this patch). And "fill_rectangles" as "composite_rectangles"
> as well?

Yeah, I've wanted to rename show_glyphs to composite_glyphs for a while. 
  I think fill_rectangles is alright, though, since it only accepts a
solid color source.  On the other hand, it does take an operator
argument and does composite that solid color.  Dunno...

Kristian

-------------- next part --------------
? make-patch.sh
? doc/public/tmpl/cairo-image.sgml
? doc/public/tmpl/cairo-win3.sgml
? src/cairo-meta-surface-private.h
? src/cairo-meta-surface.c
? test/icecream-0.6-1.i386.rpm
? test/meta-ps-surface.c
? test/meta-surface.c
? test/meta-surface.png
? test/pdf-clip.ps
Index: src/Makefile.am
===================================================================
RCS file: /cvs/cairo/cairo/src/Makefile.am,v
retrieving revision 1.52
diff -u -p -r1.52 Makefile.am
--- src/Makefile.am	21 Jun 2005 22:38:51 -0000	1.52
+++ src/Makefile.am	23 Jun 2005 05:49:57 -0000
@@ -111,6 +111,8 @@ libcairo_la_SOURCES =				\
 	cairo-output-stream.c			\
 	cairo-wideint.c				\
 	cairo-wideint.h				\
+	cairo-meta-surface.c			\
+	cairo-meta-surface.h			\
 	$(libcairo_atsui_sources)		\
 	$(libcairo_ft_sources)			\
 	$(libcairo_ps_sources)			\
Index: src/cairo-pattern.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-pattern.c,v
retrieving revision 1.46
diff -u -p -r1.46 cairo-pattern.c
--- src/cairo-pattern.c	20 Jun 2005 18:09:51 -0000	1.46
+++ src/cairo-pattern.c	23 Jun 2005 05:49:58 -0000
@@ -143,10 +143,13 @@ _cairo_gradient_pattern_init_copy (cairo
     }
 }
 
-void
+cairo_pattern_t *
 _cairo_pattern_init_copy (cairo_pattern_t	*pattern,
 			  const cairo_pattern_t *other)
 {
+    if (other == NULL)
+	return NULL;
+
     switch (other->type) {
     case CAIRO_PATTERN_SOLID: {
 	cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern;
@@ -171,11 +174,16 @@ _cairo_pattern_init_copy (cairo_pattern_
     }
     
     pattern->ref_count = 1;
+
+    return pattern;
 }
 
 void
 _cairo_pattern_fini (cairo_pattern_t *pattern)
 {
+    if (pattern == NULL)
+	return;
+
     switch (pattern->type) {
     case CAIRO_PATTERN_SOLID:
 	break;
Index: src/cairoint.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairoint.h,v
retrieving revision 1.157
diff -u -p -r1.157 cairoint.h
--- src/cairoint.h	20 Jun 2005 18:09:51 -0000	1.157
+++ src/cairoint.h	23 Jun 2005 05:49:59 -0000
@@ -1713,7 +1713,7 @@ _cairo_slope_counter_clockwise (cairo_sl
 
 /* cairo_pattern.c */
 
-cairo_private void
+cairo_private cairo_pattern_t *
 _cairo_pattern_init_copy (cairo_pattern_t	*pattern,
 			  const cairo_pattern_t *other);
 
--- /dev/null	2005-06-22 14:57:06.180889176 -0400
+++ src/cairo-meta-surface.c	2005-06-23 01:24:03.000000000 -0400
@@ -0,0 +1,599 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Kristian Høgsberg <krh at redhat.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-meta-surface-private.h"
+
+/* 
+ * Notes: 
+ *
+ * Can't use cairo_surface_* calls since we often don't want
+ * fallbacks.  For example, when determining the font subsets or the
+ * fallback areas.  Hmm... but maybe those passes could be integrated
+ * into the delegation wrappers and the ps output pass, respectively.
+ *
+ * Don't want to mark a valid NULL pattern as a error object, which is
+ * what we do if we set pattern->status = CAIRO_STATUS_NULL_POINTER.
+ * We could make a CAIRO_PATTERN_TYPE_NULL alternatively.  Btw. what
+ * about a CAIRO_PATTERN_TYPE_ERROR for pattern->status !=
+ * CAIRO_STATUS_SUCCESS cases?
+ */
+
+static const cairo_surface_backend_t cairo_meta_surface_backend;
+
+cairo_surface_t *
+_cairo_meta_surface_create (double width, double height)
+{
+    cairo_meta_surface_t *meta;
+
+    meta = malloc (sizeof (cairo_meta_surface_t));
+    if (meta == NULL)
+	return NULL;
+
+    meta->width = width;
+    meta->height = height;
+    _cairo_surface_init (&meta->base, &cairo_meta_surface_backend);
+    _cairo_array_init (&meta->commands, sizeof (cairo_command_t *));
+
+    return &meta->base;
+}
+
+static cairo_surface_t *
+_cairo_meta_surface_create_similar (void		*abstract_surface,
+				    cairo_format_t	format,
+				    int			width,
+				    int			height)
+{
+    return _cairo_meta_surface_create (width, height);
+}
+
+static cairo_status_t
+_cairo_meta_surface_finish (void *abstract_surface)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_t *command;
+    cairo_command_t **elements;
+    int i, num_elements;
+
+    num_elements = meta->commands.num_elements;
+    elements = (cairo_command_t **) meta->commands.elements;
+    for (i = 0; i < num_elements; i++) {
+	command = elements[i];
+	switch (command->type) {
+	case CAIRO_COMMAND_COMPOSITE:
+	    _cairo_pattern_fini (command->composite.src_pattern_pointer);
+	    _cairo_pattern_fini (command->composite.mask_pattern_pointer);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_FILL_RECTANGLES:
+	    free (command->fill_rectangles.rects);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS:
+	    _cairo_pattern_fini (command->composite_trapezoids.pattern_pointer);
+	    free (command->composite_trapezoids.traps);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_SET_CLIP_REGION:
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_INTERSECT_CLIP_PATH:
+	    if (command->intersect_clip_path.path_pointer)
+		_cairo_path_fixed_fini (&command->intersect_clip_path.path);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_SHOW_GLYPHS:
+	    cairo_scaled_font_destroy (command->show_glyphs.scaled_font);
+	    _cairo_pattern_fini (command->show_glyphs.pattern_pointer);
+	    free (command->show_glyphs.glyphs);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_FILL_PATH:
+	    _cairo_pattern_fini (command->fill_path.pattern_pointer);
+	    _cairo_path_fixed_fini (&command->fill_path.path);
+	    free (command);
+	    break;
+
+	default:
+	    ASSERT_NOT_REACHED;
+	}
+    }
+
+    _cairo_array_fini (&meta->commands);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_composite (cairo_operator_t	operator,
+			       cairo_pattern_t	*src_pattern,
+			       cairo_pattern_t	*mask_pattern,
+			       void		*abstract_surface,
+			       int		src_x,
+			       int		src_y,
+			       int		mask_x,
+			       int		mask_y,
+			       int		dst_x,
+			       int		dst_y,
+			       unsigned int	width,
+			       unsigned int	height)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_composite_t *command;
+
+    command = malloc (sizeof (cairo_command_composite_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_COMPOSITE;
+    command->operator = operator;
+    command->src_pattern_pointer = 
+	_cairo_pattern_init_copy (&command->src_pattern.base, src_pattern);
+    command->mask_pattern_pointer =
+	_cairo_pattern_init_copy (&command->mask_pattern.base, mask_pattern);
+    command->src_x = src_x;
+    command->src_y = src_y;
+    command->mask_x = mask_x;
+    command->mask_y = mask_y;
+    command->dst_x = dst_x;
+    command->dst_y = dst_y;
+    command->width = width;
+    command->height = height;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	_cairo_pattern_fini (command->src_pattern_pointer);
+	_cairo_pattern_fini (command->mask_pattern_pointer);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_fill_rectangles (void			*abstract_surface,
+				     cairo_operator_t		operator,
+				     const cairo_color_t	*color,
+				     cairo_rectangle_t		*rects,
+				     int			num_rects)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_fill_rectangles_t *command;
+
+    command = malloc (sizeof (cairo_command_fill_rectangles_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_FILL_RECTANGLES;
+    command->operator = operator;
+    command->color = *color;
+
+    command->rects = malloc (sizeof (cairo_rectangle_t) * num_rects);
+    if (command->rects == NULL) {
+	free (command);
+        return CAIRO_STATUS_NO_MEMORY;
+    }
+    memcpy (command->rects, rects, sizeof (cairo_rectangle_t) * num_rects);
+
+    command->num_rects = num_rects;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	free (command->rects);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_composite_trapezoids (cairo_operator_t	operator,
+					  cairo_pattern_t	*pattern,
+					  void			*abstract_surface,
+					  int			x_src,
+					  int			y_src,
+					  int			x_dst,
+					  int			y_dst,
+					  unsigned int		width,
+					  unsigned int		height,
+					  cairo_trapezoid_t	*traps,
+					  int			num_traps)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_composite_trapezoids_t *command;
+
+    command = malloc (sizeof (cairo_command_composite_trapezoids_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS;
+    command->operator = operator;
+    command->pattern_pointer = 
+	_cairo_pattern_init_copy (&command->pattern.base, pattern);
+    command->x_src = x_src;
+    command->y_src = y_src;
+    command->x_dst = x_dst;
+    command->y_dst = y_dst;
+    command->width = width;
+    command->height = height;
+
+    command->traps = malloc (sizeof (cairo_trapezoid_t) * num_traps);
+    if (command->traps == NULL) {
+	_cairo_pattern_fini (command->pattern_pointer);
+	free (command);
+        return CAIRO_STATUS_NO_MEMORY;
+    }
+    memcpy (command->traps, traps, sizeof (cairo_trapezoid_t) * num_traps);
+
+    command->num_traps = num_traps;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	_cairo_pattern_fini (command->pattern_pointer);
+	free (command->traps);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_set_clip_region (void	       *abstract_surface,
+				     pixman_region16_t *region)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_set_clip_region_t *command;
+
+    command = malloc (sizeof (cairo_command_set_clip_region_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_SET_CLIP_REGION;
+
+    if (region) {
+	command->region = pixman_region_create ();
+	pixman_region_copy (command->region, region);
+    } else {
+	command->region = NULL;
+    }
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	if (command->region)
+	    pixman_region_destroy (command->region);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_intersect_clip_path (void		    *dst,
+					 cairo_path_fixed_t *path,
+					 cairo_fill_rule_t   fill_rule,
+					 double		     tolerance)
+{
+    cairo_meta_surface_t *meta = dst;
+    cairo_command_intersect_clip_path_t *command;
+    cairo_status_t status;
+
+    command = malloc (sizeof (cairo_command_intersect_clip_path_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_INTERSECT_CLIP_PATH;
+
+    if (path) {
+	status = _cairo_path_fixed_init_copy (&command->path, path);
+	if (status) {
+	    free (command);
+	    return status;
+	}
+	command->path_pointer = &command->path;
+    } else {
+	command->path_pointer = NULL;
+    }
+    command->fill_rule = fill_rule;
+    command->tolerance = tolerance;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	if (path)
+	    _cairo_path_fixed_fini (&command->path);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_get_extents (void		   *abstract_surface,
+				 cairo_rectangle_t *rectangle)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+
+    /* Currently this is used for getting the extents of the surface
+     * before calling cairo_paint().  This is the only this that
+     * requires the meta surface to have an explicit size.  If paint
+     * was just a backend function, this would not be necessary. */
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width = meta->width;
+    rectangle->height = meta->height;
+    
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_show_glyphs (cairo_scaled_font_t	*scaled_font,
+				 cairo_operator_t	operator,
+				 cairo_pattern_t	*pattern,
+				 void			*abstract_surface,
+				 int			source_x,
+				 int			source_y,
+				 int			dest_x,
+				 int			dest_y,
+				 unsigned int		width,
+				 unsigned int		height,
+				 const cairo_glyph_t	*glyphs,
+				 int			num_glyphs)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_show_glyphs_t *command;
+
+    command = malloc (sizeof (cairo_command_show_glyphs_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_SHOW_GLYPHS;
+    command->scaled_font = scaled_font;
+    cairo_scaled_font_reference (scaled_font);
+    command->operator = operator;
+    command->pattern_pointer =
+	_cairo_pattern_init_copy (&command->pattern.base, pattern);
+    command->source_x = source_x;
+    command->source_y = source_y;
+    command->dest_x = dest_x;
+    command->dest_y = dest_y;
+    command->width = width;
+    command->height = height;
+
+    command->glyphs = malloc (sizeof (cairo_glyph_t) * num_glyphs);
+    if (command->glyphs == NULL) {
+	_cairo_pattern_fini (command->pattern_pointer);
+	free (command);
+        return CAIRO_STATUS_NO_MEMORY;
+    }
+    memcpy (command->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+
+    command->num_glyphs = num_glyphs;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	_cairo_pattern_fini (command->pattern_pointer);
+	free (command->glyphs);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_fill_path (cairo_operator_t	   operator,
+			       cairo_pattern_t	  *pattern,
+			       void		  *abstract_surface,
+			       cairo_path_fixed_t *path,
+			       cairo_fill_rule_t   fill_rule,
+			       double		   tolerance)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_fill_path_t *command;
+    cairo_status_t status;
+
+    command = malloc (sizeof (cairo_command_fill_path_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_FILL_PATH;
+    command->operator = operator;
+    command->pattern_pointer = 
+	_cairo_pattern_init_copy (&command->pattern.base, pattern);
+    status = _cairo_path_fixed_init_copy (&command->path, path);
+    if (status) {
+	_cairo_pattern_fini (command->pattern_pointer);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }	
+    command->fill_rule = fill_rule;
+    command->tolerance = tolerance;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	_cairo_path_fixed_fini (&command->path);
+	_cairo_pattern_fini (command->pattern_pointer);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_meta_surface_backend = {
+    _cairo_meta_surface_create_similar,
+    _cairo_meta_surface_finish,
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* acquire_dest_image */
+    NULL, /* release_dest_image */
+    NULL, /* clone_similar */
+    _cairo_meta_surface_composite,
+    _cairo_meta_surface_fill_rectangles,
+    _cairo_meta_surface_composite_trapezoids,
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_meta_surface_set_clip_region,
+    _cairo_meta_surface_intersect_clip_path,
+    _cairo_meta_surface_get_extents,
+    _cairo_meta_surface_show_glyphs,
+    _cairo_meta_surface_fill_path,
+};
+
+cairo_int_status_t
+_cairo_meta_surface_replay (cairo_surface_t *surface,
+			    cairo_surface_t *target)
+{
+    cairo_meta_surface_t *meta;
+    cairo_command_t *command, **elements;
+    int i, num_elements;
+    cairo_int_status_t status;
+
+    meta = (cairo_meta_surface_t *) surface;
+    status = CAIRO_STATUS_SUCCESS;
+
+    num_elements = meta->commands.num_elements;
+    elements = (cairo_command_t **) meta->commands.elements;
+    for (i = 0; i < num_elements; i++) {
+	command = elements[i];
+	switch (command->type) {
+	case CAIRO_COMMAND_COMPOSITE:
+	    if (target->backend->composite == NULL)
+		break;
+	    status = target->backend->composite
+		(command->composite.operator,
+		 command->composite.src_pattern_pointer,
+		 command->composite.mask_pattern_pointer,
+		 target,
+		 command->composite.src_x,
+		 command->composite.src_y,
+		 command->composite.mask_x,
+		 command->composite.mask_y,
+		 command->composite.dst_x,
+		 command->composite.dst_y,
+		 command->composite.width,
+		 command->composite.height);
+	    break;
+
+	case CAIRO_COMMAND_FILL_RECTANGLES:
+	    if (target->backend->fill_rectangles == NULL)
+		break;
+	    status = target->backend->fill_rectangles
+		(target,
+		 command->fill_rectangles.operator,
+		 &command->fill_rectangles.color,
+		 command->fill_rectangles.rects,
+		 command->fill_rectangles.num_rects);
+	    break;
+
+	case CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS:
+	    if (target->backend->composite_trapezoids == NULL)
+		break;
+	    status = target->backend->composite_trapezoids
+		(command->composite_trapezoids.operator,
+		 command->composite_trapezoids.pattern_pointer,
+		 target,
+		 command->composite_trapezoids.x_src,
+		 command->composite_trapezoids.y_src,
+		 command->composite_trapezoids.x_dst,
+		 command->composite_trapezoids.y_dst,
+		 command->composite_trapezoids.width,
+		 command->composite_trapezoids.height,
+		 command->composite_trapezoids.traps,
+		 command->composite_trapezoids.num_traps);
+	    break;
+
+	case CAIRO_COMMAND_SET_CLIP_REGION:
+	    if (target->backend->set_clip_region == NULL)
+		break;
+	    status = target->backend->set_clip_region
+		(target,
+		 command->set_clip_region.region);
+	    break;
+
+	case CAIRO_COMMAND_INTERSECT_CLIP_PATH:
+	    if (target->backend->intersect_clip_path == NULL)
+		break;
+	    status = target->backend->intersect_clip_path
+		(target,
+		 command->intersect_clip_path.path_pointer,
+		 command->intersect_clip_path.fill_rule,
+		 command->intersect_clip_path.tolerance);
+	    break;
+
+	case CAIRO_COMMAND_SHOW_GLYPHS:
+	    if (target->backend->show_glyphs == NULL)
+		break;
+	    status = target->backend->show_glyphs
+		(command->show_glyphs.scaled_font,
+		 command->show_glyphs.operator,
+		 command->show_glyphs.pattern_pointer,
+		 target,
+		 command->show_glyphs.source_x,
+		 command->show_glyphs.source_y,
+		 command->show_glyphs.dest_x,
+		 command->show_glyphs.dest_y,
+		 command->show_glyphs.width,
+		 command->show_glyphs.height,
+		 command->show_glyphs.glyphs,
+		 command->show_glyphs.num_glyphs);
+	    break;
+
+	case CAIRO_COMMAND_FILL_PATH:
+	    if (target->backend->fill_path == NULL)
+		break;
+	    status = target->backend->fill_path
+		(command->fill_path.operator,
+		 command->fill_path.pattern_pointer,
+		 target,
+		 &command->fill_path.path,
+		 command->fill_path.fill_rule,
+		 command->fill_path.tolerance);
+	    break;
+
+	default:
+	    ASSERT_NOT_REACHED;
+	}
+
+	if (status)
+	    break;
+    }
+
+    return status;
+}
--- /dev/null	2005-06-22 14:57:06.180889176 -0400
+++ src/cairo-meta-surface-private.h	2005-06-23 01:24:31.000000000 -0400
@@ -0,0 +1,155 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Kristian Høgsberg <krh at redhat.com>
+ */
+
+#ifndef CAIRO_META_SURFACE_H
+#define CAIRO_META_SURFACE_H
+
+#include "cairoint.h"
+#include "cairo-path-fixed-private.h"
+
+typedef enum {
+    CAIRO_COMMAND_COMPOSITE,
+    CAIRO_COMMAND_FILL_RECTANGLES,
+    CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS,
+    CAIRO_COMMAND_SET_CLIP_REGION,
+    CAIRO_COMMAND_INTERSECT_CLIP_PATH,
+    CAIRO_COMMAND_SHOW_GLYPHS,
+    CAIRO_COMMAND_FILL_PATH
+} cairo_command_type_t;
+
+typedef struct _cairo_command_composite {
+    cairo_command_type_t	type;
+    cairo_operator_t		operator;
+    cairo_pattern_union_t	src_pattern;
+    cairo_pattern_t	       *src_pattern_pointer;
+    cairo_pattern_union_t	mask_pattern;
+    cairo_pattern_t	       *mask_pattern_pointer;
+    int				src_x;
+    int				src_y;
+    int				mask_x;
+    int				mask_y;
+    int				dst_x;
+    int				dst_y;
+    unsigned int		width;
+    unsigned int		height;
+} cairo_command_composite_t;
+
+typedef struct _cairo_command_fill_rectangles {
+    cairo_command_type_t	type;
+    cairo_operator_t		operator;
+    cairo_color_t		color;
+    cairo_rectangle_t	       *rects;
+    int				num_rects;
+} cairo_command_fill_rectangles_t;
+
+typedef struct _cairo_command_composite_trapezoids {
+    cairo_command_type_t	type;
+    cairo_operator_t		operator;
+    cairo_pattern_union_t	pattern;
+    cairo_pattern_t	       *pattern_pointer;
+    int				x_src;
+    int				y_src;
+    int				x_dst;
+    int				y_dst;
+    unsigned int		width;
+    unsigned int		height;
+    cairo_trapezoid_t	       *traps;
+    int				num_traps;
+} cairo_command_composite_trapezoids_t;
+
+typedef struct _cairo_command_set_clip_region {
+    cairo_command_type_t	type;
+    pixman_region16_t	       *region;
+} cairo_command_set_clip_region_t;
+
+typedef struct _cairo_command_intersect_clip_path {
+    cairo_command_type_t	type;
+    cairo_path_fixed_t	       *path_pointer;
+    cairo_path_fixed_t		path;
+    cairo_fill_rule_t		fill_rule;
+    double			tolerance;
+} cairo_command_intersect_clip_path_t;
+
+typedef struct _cairo_command_show_glyphs {
+    cairo_command_type_t	type;
+    cairo_scaled_font_t	       *scaled_font;
+    cairo_operator_t		operator;
+    cairo_pattern_union_t	pattern;
+    cairo_pattern_t	       *pattern_pointer;
+    int				source_x;
+    int				source_y;
+    int				dest_x;
+    int				dest_y;
+    unsigned int		width;
+    unsigned int		height;
+    cairo_glyph_t		*glyphs;
+    int				num_glyphs;
+} cairo_command_show_glyphs_t;
+
+typedef struct _cairo_command_fill_path {
+    cairo_command_type_t	type;
+    cairo_operator_t		operator;
+    cairo_pattern_union_t	pattern;
+    cairo_pattern_t	       *pattern_pointer;
+    cairo_path_fixed_t		path;
+    cairo_fill_rule_t		fill_rule;
+    double			tolerance;
+} cairo_command_fill_path_t;
+
+typedef union _cairo_command {
+    cairo_command_type_t			type;
+    cairo_command_composite_t			composite;	
+    cairo_command_fill_rectangles_t		fill_rectangles;
+    cairo_command_composite_trapezoids_t	composite_trapezoids;
+    cairo_command_set_clip_region_t		set_clip_region;
+    cairo_command_intersect_clip_path_t		intersect_clip_path;
+    cairo_command_show_glyphs_t			show_glyphs;
+    cairo_command_fill_path_t			fill_path;
+} cairo_command_t;
+
+typedef struct _cairo_meta_surface {
+    cairo_surface_t base;
+    double width, height;
+    cairo_array_t commands;
+} cairo_meta_surface_t;
+
+cairo_surface_t *
+_cairo_meta_surface_create (double width, double height);
+
+cairo_int_status_t
+_cairo_meta_surface_replay (cairo_surface_t *surface,
+			    cairo_surface_t *target);
+
+#endif /* CAIRO_META_SURFACE_H */


More information about the cairo mailing list