[cairo] New surface: cairo_combined_surface_t

Kouhei Sutou kou at cozmixng.org
Mon Jun 5 06:15:08 PDT 2006


Hi,

I made an example for cairo_combined_surface_t that is a
GTK+ widget named GtkTransparent. The widget helps us to
make custom shape widget. The widget automatically set mask
whose shape is same as we draw by expose-event.

An example code that makes a circle shape widget:

  statci gboolean
  expose_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
  {
     cairo_t *cr;
     cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 1.0);
     cairo_new_path (cr);
     cairo_move_to (cr, 50, 50);
     cairo_arc (cr, 50.0, 50.0, 50, 0.0, 2 * M_PI);
     cairo_fill (cr);
     cairo_destroy (cr);
     return FALSE;
  }

  int
  main (int argc, char **argv)
  {
    ..
    transparent = gtk_transparent_new ();
    g_signal_connect (trnasparent, "expose-event",
                      G_CALLBACK (expose_cb), NULL);
    ..
  }


To try example, type the following command:
  % cc `pkg-config gtk+-2.0 --cflags --libs` -o example example.c gtktransparent.c

I attached four files:
  * gtktransparent.c: an implementation custom shape widget.
  * gtktransparent.h: an interface of custom shape widget.
  * example.c: an example code to use GtkTransparent widget.
  * cairo-combined-surface.diff: second version patch of
    cairo_combined_surface_t.

Thanks,
--
kou

In <a799fada0605311820r4173033bm3074cd8599d8369d at mail.gmail.com>
  "[cairo] New surface: cairo_combined_surface_t" on Thu, 1 Jun 2006 10:20:52 +0900,
  "Kouhei Sutou" <kou at cozmixng.org> wrote:

> --
> Even if we want to write same contents to different serfaces, we must make
> cairo_t for each cairo_surface_t. For example, if we wants to output PS and PDF
> with same contents, we write the following code:
> 
> static void
> render(cairo_surface_t *surface)
> {
>   cairo_t *cr;
> 
>   cr = cairo_create(surface);
>   ...
>   cairo_rectangle(cr, ...);
>   cairo_fill(cr);
>   ...
>   cairo_show_page(cr);
>   ...
>   cairo_surface_finish(cairo_get_target(cr));
> }
> 
> static void
> output(void)
> {
>   cairo_surface_t *ps_surface, *pdf_surface;
>   ...
>   render(ps_surface);
>   render(pdf_surface);
> }
> 
> There is some problems:
> * If render() loads a image each time, the image is loaded twice.
> * We must define a function to render. (Yes, we can use "copy and paste" but
> this is a bad manner.)
> * ...
> 
> To solve those problems, I suggeste that cairo provides a surface can combine
> some surfaces and show those surfaces as a surface to cairo_t. And then, we can
> write the above code as the following:
> 
> static void
> output(void)
> {
>   cairo_t *cr;
>   cairo_surface_t *ps_surface, *pdf_surface, *combined_surface;
> 
>   ...
>   combined_surface = cairo_combined_surface(ps_surface, pdf_surface, NULL);
> 
>   cr = cairo_create(combined_surface);
>   ...
>   cairo_rectangle(cr, ...);
>   cairo_fill(cr);
>   ...
>   cairo_show_page(cr);
>   ...
>   cairo_surface_finish(cairo_get_target(cr));
> }
> --
-------------- next part --------------
/* GtkTransparent
 * Copyright (C) 2006 Kouhei Sutou <kou at cozmixng.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <cairo-combined.h>

#include "gtktransparent.h"

#define GTK_TRANSPARENT_GET_PRIVATE(o)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_TRANSPARENT, GtkTransparentPrivate))

struct _GtkTransparentPrivate
{
  GdkBitmap *mask;
};

static void gtk_transparent_finalize     (GObject *object);
static gboolean gtk_transparent_expose_event (GtkWidget        *widget,
                                              GdkEventExpose   *event);
static gboolean expose_event_cb (GtkWidget        *widget,
                                 GdkEventExpose   *event,
                                 gpointer          data);

G_DEFINE_TYPE (GtkTransparent, gtk_transparent, GTK_TYPE_DRAWING_AREA)

static void
gtk_transparent_class_init (GtkTransparentClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);

  gobject_class->finalize = gtk_transparent_finalize;

  widget_class->expose_event = gtk_transparent_expose_event;

  g_type_class_add_private (class, sizeof (GtkTransparentPrivate));
}

static void
gtk_transparent_init (GtkTransparent *transparent)
{
  GtkTransparentPrivate *priv;

  priv = transparent->priv = GTK_TRANSPARENT_GET_PRIVATE (transparent);

  priv->mask = NULL;

  g_signal_connect (transparent, "expose_event",
                    G_CALLBACK (expose_event_cb), NULL);
}

static void
gtk_transparent_free_mask (GtkTransparent *transparent)
{
  if (transparent->priv->mask) {
    g_object_unref (transparent->priv->mask);
    transparent->priv->mask = NULL;
  }
}

static void
gtk_transparent_reset_mask (GtkTransparent *transparent)
{
  gtk_transparent_free_mask (transparent);
  gtk_widget_shape_combine_mask (GTK_WIDGET (transparent), NULL, 0, 0);
}


static void
gtk_transparent_finalize (GObject *object)
{
  GtkTransparent *transparent = GTK_TRANSPARENT (object);

  gtk_transparent_free_mask (transparent);

  G_OBJECT_CLASS (gtk_transparent_parent_class)->finalize (object);
}

GtkWidget*
gtk_transparent_new (void)
{
  return g_object_new (GTK_TYPE_TRANSPARENT, NULL);
}

static gboolean
expose_event_cb (GtkWidget      *widget,
                 GdkEventExpose *event,
                 gpointer        data)
{
  GtkTransparent *transparent;

  g_return_val_if_fail (GTK_IS_TRANSPARENT (widget), FALSE);
  transparent = GTK_TRANSPARENT (widget);

  gtk_transparent_free_mask (transparent);

  return FALSE;
}

static gboolean
gtk_transparent_expose_event (GtkWidget      *widget,
                              GdkEventExpose *event)
{
  GtkTransparent *transparent;

  g_return_val_if_fail (GTK_IS_TRANSPARENT (widget), FALSE);
  transparent = GTK_TRANSPARENT (widget);

  if (transparent->priv->mask)
    {
      gtk_widget_shape_combine_mask (widget, transparent->priv->mask, 0, 0);
      gtk_transparent_free_mask (transparent);
      return TRUE;
    }
  else
    {
      gtk_transparent_reset_mask (transparent);
      return FALSE;
    }
}

cairo_t *
gtk_transparent_cairo_create (GtkTransparent *transparent)
{
  GtkWidget *widget;
  gint width, height;
  cairo_t *cr, *drawable_cr, *mask_cr;
  cairo_surface_t *drawable_surface, *mask_surface, *combined_surface;

  g_return_val_if_fail (GTK_IS_TRANSPARENT (transparent), NULL);

  widget = GTK_WIDGET (transparent);
  g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);

  width = widget->allocation.width;
  height = widget->allocation.height;

  if (!transparent->priv->mask)
    {
      gchar *data;

      data = g_malloc0 (width * height);
      transparent->priv->mask =
        gdk_bitmap_create_from_data (NULL, data, width, height);
      g_free (data);
    }

  drawable_cr = gdk_cairo_create (GDK_DRAWABLE (widget->window));
  drawable_surface = cairo_get_target (drawable_cr);
  mask_cr = gdk_cairo_create (GDK_DRAWABLE (transparent->priv->mask));
  mask_surface = cairo_get_target (mask_cr);
  combined_surface = cairo_combined_surface_create (drawable_surface,
                                                    mask_surface,
                                                    NULL);
  cairo_destroy (drawable_cr);
  cairo_destroy (mask_cr);
  cr = cairo_create (combined_surface);
  cairo_surface_destroy (combined_surface);
  return cr;
}
-------------- next part --------------
/* GtkTransparent
 * Copyright (C) 2006 Kouhei Sutou <kou at cozmixng.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#ifndef __GTK_TRANSPARENT_H__
#define __GTK_TRANSPARENT_H__

#include <gtk/gtk.h>

G_BEGIN_DECLS

#define GTK_TYPE_TRANSPARENT                  (gtk_transparent_get_type ())
#define GTK_TRANSPARENT(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TRANSPARENT, GtkTransparent))
#define GTK_TRANSPARENT_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TRANSPARENT, GtkTransparentClass))
#define GTK_IS_TRANSPARENT(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TRANSPARENT))
#define GTK_IS_TRANSPARENT_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TRANSPARENT))
#define GTK_TRANSPARENT_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TRANSPARENT, GtkTransparentClass))

typedef struct _GtkTransparent          GtkTransparent;
typedef struct _GtkTransparentClass     GtkTransparentClass;
typedef struct _GtkTransparentPrivate   GtkTransparentPrivate;

struct _GtkTransparent
{
  GtkDrawingArea parent_instance;

  GtkTransparentPrivate *priv;
};

struct _GtkTransparentClass
{
  GtkDrawingAreaClass parent_class;

  /* Padding for future expansion */
  void (*_gtk_reserved1) (void);
  void (*_gtk_reserved2) (void);
  void (*_gtk_reserved3) (void);
  void (*_gtk_reserved4) (void);
  void (*_gtk_reserved5) (void);
  void (*_gtk_reserved6) (void);
  void (*_gtk_reserved7) (void);
};

GType        gtk_transparent_get_type          (void) G_GNUC_CONST;
GtkWidget   *gtk_transparent_new               (void);
cairo_t     *gtk_transparent_cairo_create      (GtkTransparent *transparent);

G_END_DECLS

#endif /* __GTK_TRANSPARENT_H__ */
-------------- next part --------------
#include <math.h>
#include "gtktransparent.h"

static void
draw_rectangle (cairo_t *cr)
{
    cairo_new_path (cr);
    cairo_move_to (cr, 0.0, 0.0);
    cairo_rectangle (cr, 0.0, 0.0, 100.0, 100.0);
    cairo_stroke (cr);
}

static void
draw_arc (cairo_t *cr)
{
    cairo_new_path (cr);
    cairo_move_to (cr, 50, 50);
    cairo_arc (cr, 50.0, 50.0, g_random_double_range (0.0, 50.0),
               0.0, 2 * M_PI);
    cairo_fill (cr);
}

static gboolean
darea_expose_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
    cairo_t *cr;

    cr = gdk_cairo_create (GDK_DRAWABLE (widget->window));
    cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 1.0);
    draw_rectangle (cr);
    cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0);
    draw_arc (cr);
    cairo_destroy (cr);
    return FALSE;
}

static gboolean
transparent_expose_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
    cairo_t *cr;

    cr = gtk_transparent_cairo_create (GTK_TRANSPARENT (widget));
    cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 1.0);
    draw_rectangle (cr);
    cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 1.0);
    draw_arc (cr);
    cairo_destroy (cr);
    return FALSE;
}

static gboolean
refresh (gpointer data)
{
    GtkWidget *layout = data;
    gtk_widget_queue_draw (layout);
    return TRUE;
}


int
main (int argc, char **argv)
{
    GtkWidget *window, *layout, *darea1, *darea2, *transparent;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    layout = gtk_layout_new (NULL, NULL);
    gtk_container_add (GTK_CONTAINER (window), layout);

    gtk_widget_set_size_request (layout, 200, 200);

    darea1 = gtk_drawing_area_new ();
    gtk_widget_set_size_request (darea1, 100, 100);
    g_signal_connect(darea1, "expose-event",
                     G_CALLBACK (darea_expose_cb), NULL);

    darea2 = gtk_drawing_area_new ();
    gtk_widget_set_size_request (darea2, 100, 100);
    g_signal_connect(darea2, "expose-event",
                     G_CALLBACK (darea_expose_cb), NULL);

    transparent = gtk_transparent_new ();
    gtk_widget_set_size_request (transparent, 100, 100);
    g_signal_connect(transparent, "expose-event",
                     G_CALLBACK (transparent_expose_cb), NULL);

    gtk_layout_put (GTK_LAYOUT (layout), darea1, 0, 0);
    gtk_layout_put (GTK_LAYOUT (layout), transparent, 50, 50);
    gtk_layout_put (GTK_LAYOUT (layout), darea2, 100, 100);

    gtk_widget_show_all (window);

    g_timeout_add (1000, refresh, layout);

    gtk_main ();

    return 0;
}
-------------- next part --------------
diff --git a/configure.in b/configure.in
index 1335e4c..5a5e922 100644
--- a/configure.in
+++ b/configure.in
@@ -709,6 +709,15 @@ fi
 
 dnl ===========================================================================
 
+CAIRO_BACKEND_ENABLE(combined, Combined, no, [])
+AM_CONDITIONAL(CAIRO_HAS_COMBINED_SURFACE, test "x$use_combined" = "xyes")
+if test "x$use_combined" = "xyes"; then
+  COMBINED_SURFACE_FEATURE="#define CAIRO_HAS_COMBINED_SURFACE 1"
+fi
+AC_SUBST(COMBINED_SURFACE_FEATURE)
+
+dnl ===========================================================================
+
 AC_ARG_ENABLE(test-surfaces,
   AS_HELP_STRING([--enable-test-surfaces],
 		 [Add backends for more test suite coverage (no additional public functionality)]),
@@ -749,6 +758,7 @@ echo "  SVG:        $use_svg"
 echo "  glitz:      $use_glitz"
 echo "  BeOS:       $use_beos"
 echo "  DirectFB:   $use_directfb"
+echo "  Combined:   $use_combined"
 echo ""
 echo "the following font backends:"
 echo "  FreeType:   $use_freetype"
diff --git a/src/Makefile.am b/src/Makefile.am
index 42ae71f..996fdb4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -100,6 +100,12 @@ cairo_win32_api_headers += \
 	$(srcdir)/cairo-ps.h
 endif
 
+if CAIRO_HAS_COMBINED_SURFACE
+libcairo_combined_headers = cairo-combined.h
+libcairo_combined_sources = cairo-combined-surface.c
+endif
+
+
 # These names match automake style variable definition conventions so
 # without these lines, automake will complain during the handling of 
 # the libcairo_la_LIBADD below.  (The INCLUDES is an autoconf only
@@ -122,7 +128,8 @@ cairoinclude_HEADERS =			\
 	$(libcairo_beos_headers)	\
 	$(libcairo_xcb_headers)		\
 	$(libcairo_xlib_headers)	\
-	$(libcairo_directfb_headers)
+	$(libcairo_directfb_headers)	\
+	$(libcairo_combined_headers)
 
 lib_LTLIBRARIES = libcairo.la
 
@@ -195,6 +202,7 @@ libcairo_la_SOURCES =				\
 	$(libcairo_glitz_sources)		\
 	$(libcairo_win32_sources)		\
 	$(libcairo_directfb_sources)		\
+	$(libcairo_combined_sources)		\
 	cairoint.h
 
 libcairo_la_LDFLAGS = -version-info @VERSION_INFO@ -no-undefined $(export_symbols)
diff --git a/src/cairo-combined-surface.c b/src/cairo-combined-surface.c
new file mode 100644
index 0000000..ab21621
--- /dev/null
+++ b/src/cairo-combined-surface.c
@@ -0,0 +1,685 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright ? 2006 Kouhei Sutou <kou at cozmixng.org>
+ *
+ * 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 University of Southern
+ * California.
+ *
+ * Contributor(s):
+ */
+
+#include "cairoint.h"
+#include "cairo-combined.h"
+
+typedef struct _cairo_combined_surface
+{
+    cairo_surface_t base;
+
+    cairo_array_t surfaces;
+} cairo_combined_surface_t;
+
+static const cairo_surface_backend_t cairo_combined_surface_backend;
+
+
+static cairo_surface_t *
+_cairo_combined_surface_create_array (cairo_array_t *surfaces)
+{
+    int i, n;
+    cairo_combined_surface_t *surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_content_t content = CAIRO_CONTENT_COLOR;
+
+    n = _cairo_array_num_elements (surfaces);
+    child_surfaces = _cairo_array_index (surfaces, 0);
+    for (i = 0; i < n; i++) {
+        child_surface = child_surfaces[i];
+        if (child_surface->status) {
+            _cairo_error (child_surface->status);
+            return (cairo_surface_t*) &_cairo_surface_nil;
+        }
+        content |= cairo_surface_get_content (child_surface);
+    }
+
+    surface = malloc (sizeof (cairo_combined_surface_t));
+    if (surface == NULL) {
+	_cairo_error (CAIRO_STATUS_NO_MEMORY);
+	return (cairo_surface_t*) &_cairo_surface_nil;
+    }
+
+    _cairo_surface_init (&surface->base, &cairo_combined_surface_backend,
+			 content);
+
+    _cairo_array_init (&surface->surfaces, sizeof (cairo_surface_t *));
+
+    child_surfaces = _cairo_array_index (surfaces, 0);
+    for (i = 0; i < n; i++) {
+        child_surface = child_surfaces[i];
+        cairo_surface_reference (child_surface);
+        _cairo_array_append (&surface->surfaces, &child_surface);
+    }
+
+    return &surface->base;
+}
+
+/**
+ * cairo_combined_surface_create:
+ * @child_surface1: a surface to combined
+ * @...: other surfaces to combined. NULL terminate.
+ *
+ * Creates a combined surface with the specified surfaces.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+cairo_surface_t *
+cairo_combined_surface_create (cairo_surface_t *child_surface1, ...)
+{
+    va_list ap;
+    cairo_array_t surfaces;
+    cairo_surface_t *surface;
+    cairo_surface_t *child_surface_n;
+
+    _cairo_array_init (&surfaces, sizeof (cairo_surface_t *));
+
+    va_start (ap, child_surface1);
+    for (child_surface_n = child_surface1;
+         child_surface_n;
+         child_surface_n = (cairo_surface_t *) va_arg (ap, cairo_surface_t *)) {
+        _cairo_array_append (&surfaces, &child_surface_n);
+    }
+    va_end (ap);
+
+    surface = _cairo_combined_surface_create_array (&surfaces);
+    _cairo_array_fini (&surfaces);
+    return surface;
+}
+
+/**
+ * cairo_combined_surface_create2:
+ * @n_surface: number of surfaces.
+ * @child_surfaces: surfaces to combined.
+ *
+ * Creates a combined surface with the specified surfaces.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+cairo_surface_t *
+cairo_combined_surface_create2 (int		  n_surfaces,
+                                cairo_surface_t	**child_surfaces)
+{
+    cairo_array_t surfaces;
+    cairo_surface_t *surface;
+
+    if (n_surfaces < 1)
+	return (cairo_surface_t *) &_cairo_surface_nil;
+
+    _cairo_array_init (&surfaces, sizeof (cairo_surface_t *));
+    _cairo_array_append_multiple (&surfaces, child_surfaces, n_surfaces);
+    surface = _cairo_combined_surface_create_array (&surfaces);
+    _cairo_array_fini (&surfaces);
+    return surface;
+}
+
+
+
+static cairo_surface_t *
+_cairo_combined_surface_create_similar (void            *abstract_surface,
+                                        cairo_content_t	 content,
+                                        int		 width,
+                                        int		 height)
+{
+    cairo_combined_surface_t *other_surface = abstract_surface;
+
+    if (((cairo_surface_t *)other_surface)->status)
+	return (cairo_surface_t *) &_cairo_surface_nil;
+
+    if (! CAIRO_CONTENT_VALID (content)) {
+	_cairo_error (CAIRO_STATUS_INVALID_CONTENT);
+	return (cairo_surface_t *) &_cairo_surface_nil;
+    }
+
+    return _cairo_combined_surface_create_array (&other_surface->surfaces);
+}
+
+static cairo_status_t
+_cairo_combined_surface_finish (void *abstract_surface)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; i < n; i++) {
+        child_surface = child_surfaces[i];
+        cairo_surface_finish (child_surface);
+        cairo_surface_destroy (child_surface);
+    }
+    _cairo_array_fini (&surface->surfaces);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_combined_surface_clone_similar (void		*abstract_surface,
+                                       cairo_surface_t	*src,
+                                       cairo_surface_t	**clone_out)
+{
+    cairo_combined_surface_t *surface = abstract_surface;
+
+    if (src->backend == surface->base.backend) {
+	*clone_out = cairo_surface_reference (src);
+
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_composite (cairo_operator_t	 op,
+                                   cairo_pattern_t	*src_pattern,
+                                   cairo_pattern_t	*mask_pattern,
+                                   void			*abstract_dst,
+                                   int			 src_x,
+                                   int			 src_y,
+                                   int			 mask_x,
+                                   int			 mask_y,
+                                   int			 fdst_x,
+                                   int			 dst_y,
+                                   unsigned int		 width,
+                                   unsigned int		 height)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_dst;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_composite (op, src_pattern, mask_pattern,
+                                           child_surface, src_x, src_y,
+                                           mask_x, mask_y, fdst_x, dst_y,
+                                           width, height);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_fill_rectangles (void				*abstract_surface,
+                                         cairo_operator_t		 op,
+                                         const cairo_color_t		*color,
+                                         cairo_rectangle_fixed_t	*rects,
+                                         int				 num_rects)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_fill_rectangles (child_surface, op, color,
+                                                 rects, num_rects);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_composite_trapezoids (cairo_operator_t	 op,
+                                              cairo_pattern_t	*pattern,
+                                              void		*abstract_dst,
+                                              cairo_antialias_t	 antialias,
+                                              int		 src_x,
+                                              int		 src_y,
+                                              int		 dst_x,
+                                              int		 dst_y,
+                                              unsigned int	 width,
+                                              unsigned int	 height,
+                                              cairo_trapezoid_t	*traps,
+                                              int		 num_traps)
+{
+    int i, n;
+    cairo_combined_surface_t *dst = abstract_dst;
+    cairo_surface_t *child_dst;
+    cairo_surface_t **child_dsts;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&dst->surfaces);
+    child_dsts = _cairo_array_index (&dst->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_dst = child_dsts[i];
+        status = _cairo_surface_composite_trapezoids (op, pattern,
+                                                      child_dst, antialias,
+                                                      src_x, src_y,
+                                                      dst_x, dst_y,
+                                                      width, height,
+                                                      traps, num_traps);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_copy_page (void *abstract_surface)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_copy_page (child_surface);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_show_page (void *abstract_surface)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_show_page (child_surface);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_set_clip_region (void			*abstract_surface,
+                                         pixman_region16_t	*region)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+    unsigned int serial;
+
+    serial = _cairo_surface_get_current_clip_serial(&surface->base);
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_set_clip_region (child_surface, region, serial);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_intersect_clip_path (void		*abstract_surface,
+                                             cairo_path_fixed_t	*path,
+                                             cairo_fill_rule_t	 fill_rule,
+                                             double		 tolerance,
+                                             cairo_antialias_t	 antialias)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_intersect_clip_path (child_surface, path,
+                                                     fill_rule, tolerance,
+                                                     antialias);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_get_extents (void			*abstract_surface,
+                                     cairo_rectangle_fixed_t	*rectangle)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    if (n < 1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    status = _cairo_surface_get_extents (child_surfaces[0], rectangle);
+
+    for (i = 1; !status && i < n; i++) {
+        cairo_rectangle_fixed_t tmp_rectangle;
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_get_extents (child_surface, &tmp_rectangle);
+        if (tmp_rectangle.x != rectangle->x ||
+            tmp_rectangle.y != rectangle->y ||
+            tmp_rectangle.width != rectangle->width ||
+            tmp_rectangle.height != rectangle->height)
+            status = CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_old_show_glyphs (cairo_scaled_font_t	*font,
+                                         cairo_operator_t	 op,
+                                         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)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_old_show_glyphs (font, op, pattern,
+                                                 child_surface,
+                                                 source_x, source_y,
+                                                 dest_x, dest_y,
+                                                 width, height,
+                                                 glyphs, num_glyphs);
+    }
+
+    return status;
+}
+
+static void
+_cairo_combined_surface_get_font_options (void			*abstract_surface,
+                                          cairo_font_options_t	*options)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_bool_t got = FALSE;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; i < n; i++) {
+        child_surface = child_surfaces[i];
+        if (!child_surface->finished &&
+            child_surface->backend->get_font_options) {
+            child_surface->backend->get_font_options (child_surface, options);
+            got = TRUE;
+            break;
+        }
+    }
+
+    if (!got)
+        _cairo_font_options_init_default (options);
+}
+
+static cairo_status_t
+_cairo_combined_surface_flush (void *abstract_surface)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        cairo_surface_flush (child_surface);
+        status = cairo_surface_status (child_surface);
+    }
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_combined_surface_mark_dirty_rectangle (void	*abstract_surface,
+                                              int	 x,
+                                              int	 y,
+                                              int	 width,
+                                              int	 height)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        cairo_surface_mark_dirty_rectangle (child_surface, x, y, width, height);
+        status = cairo_surface_status (child_surface);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_paint (void		*abstract_surface,
+                               cairo_operator_t	 op,
+                               cairo_pattern_t	*source)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_paint (child_surface, op, source);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_mask (void		*abstract_surface,
+                              cairo_operator_t	 op,
+                              cairo_pattern_t	*source,
+                              cairo_pattern_t	*mask)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_mask (child_surface, op, source, mask);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_stroke (void			*abstract_surface,
+                                cairo_operator_t	 op,
+                                cairo_pattern_t		*source,
+                                cairo_path_fixed_t	*path,
+                                cairo_stroke_style_t	*style,
+                                cairo_matrix_t		*ctm,
+                                cairo_matrix_t		*ctm_inverse,
+                                double			 tolerance,
+                                cairo_antialias_t	 antialias)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        child_surface->clip = surface->base.clip;
+        status = _cairo_surface_stroke (child_surface, op, source, path, style,
+                                        ctm, ctm_inverse, tolerance, antialias);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_fill (void			*abstract_surface,
+                              cairo_operator_t		 op,
+                              cairo_pattern_t		*source,
+                              cairo_path_fixed_t	*path,
+                              cairo_fill_rule_t		 fill_rule,
+                              double			 tolerance,
+                              cairo_antialias_t		 antialias)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        if (!status) {
+            child_surface = child_surfaces[i];
+            child_surface->clip = surface->base.clip;
+            status = _cairo_surface_fill (child_surface, op, source, path,
+                                          fill_rule, tolerance, antialias);
+        }
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_combined_surface_show_glyphs (void		*abstract_surface,
+                                     cairo_operator_t	 op,
+                                     cairo_pattern_t	 *source,
+                                     const cairo_glyph_t *glyphs,
+                                     int		  num_glyphs,
+                                     cairo_scaled_font_t *scaled_font)
+{
+    int i, n;
+    cairo_combined_surface_t *surface = abstract_surface;
+    cairo_surface_t *child_surface;
+    cairo_surface_t **child_surfaces;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    n = _cairo_array_num_elements (&surface->surfaces);
+    child_surfaces = _cairo_array_index (&surface->surfaces, 0);
+    for (i = 0; !status && i < n; i++) {
+        child_surface = child_surfaces[i];
+        status = _cairo_surface_show_glyphs (child_surface, op, source,
+                                             glyphs, num_glyphs, scaled_font);
+    }
+
+    return status;
+}
+
+static const cairo_surface_backend_t cairo_combined_surface_backend = {
+    CAIRO_SURFACE_TYPE_COMBINED,
+    _cairo_combined_surface_create_similar,
+    _cairo_combined_surface_finish,
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* acquire_dest_image */
+    NULL, /* release_dest_image */
+    _cairo_combined_surface_clone_similar,
+    _cairo_combined_surface_composite,
+    _cairo_combined_surface_fill_rectangles,
+    _cairo_combined_surface_composite_trapezoids,
+    _cairo_combined_surface_copy_page,
+    _cairo_combined_surface_show_page,
+    _cairo_combined_surface_set_clip_region,
+    _cairo_combined_surface_intersect_clip_path,
+    _cairo_combined_surface_get_extents,
+    _cairo_combined_surface_old_show_glyphs,
+    _cairo_combined_surface_get_font_options,
+    _cairo_combined_surface_flush,
+    _cairo_combined_surface_mark_dirty_rectangle,
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+    _cairo_combined_surface_paint,
+    _cairo_combined_surface_mask,
+    _cairo_combined_surface_stroke,
+    _cairo_combined_surface_fill,
+    _cairo_combined_surface_show_glyphs,
+    NULL, /* snapshot */
+};
diff --git a/src/cairo-combined.h b/src/cairo-combined.h
new file mode 100644
index 0000000..6766927
--- /dev/null
+++ b/src/cairo-combined.h
@@ -0,0 +1,57 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright ? 2006 Kouhei Sutou <kou at cozmixng.org>
+ *
+ * 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 University of Southern
+ * California.
+ *
+ * Contributor(s):
+ */
+
+#ifndef CAIRO_COMBINED_H
+#define CAIRO_COMBINED_H
+
+#include <cairo.h>
+
+#if CAIRO_HAS_COMBINED_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_combined_surface_create (cairo_surface_t *surface, ...);
+
+cairo_public cairo_surface_t *
+cairo_combined_surface_create2 (int n_surface, cairo_surface_t **child_surfaces);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_COMBINED_SURFACE */
+# error Cairo was not compiled with support for the combined backend
+#endif /* CAIRO_HAS_COMBINED_SURFACE */
+
+#endif /* CAIRO_COMBINED_H */
diff --git a/src/cairo-features.h.in b/src/cairo-features.h.in
index 4b845bf..2608e97 100644
--- a/src/cairo-features.h.in
+++ b/src/cairo-features.h.in
@@ -75,6 +75,10 @@ #define CAIRO_VERSION_STRING "@CAIRO_VER
 
 @DIRECTFB_SURFACE_FEATURE@
 
+ at COMBINED_SURFACE_FEATURE@
+
+ at COMBINED_SURFACE_FEATURE@
+
 @FT_FONT_FEATURE@
 
 @WIN32_FONT_FEATURE@
diff --git a/src/cairo.h b/src/cairo.h
index 980ecc0..abd1c23 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1260,7 +1260,8 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_WIN32,
     CAIRO_SURFACE_TYPE_BEOS,
     CAIRO_SURFACE_TYPE_DIRECTFB,
-    CAIRO_SURFACE_TYPE_SVG
+    CAIRO_SURFACE_TYPE_SVG,
+    CAIRO_SURFACE_TYPE_COMBINED
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t


More information about the cairo mailing list