[cairo-commit] AUTHORS boilerplate/cairo-boilerplate-vg.c boilerplate/Makefile.sources boilerplate/Makefile.win32.features build/configure.ac.features build/Makefile.win32.features build/Makefile.win32.features-h configure.ac NEWS src/cairo.h src/cairo-vg.h src/cairo-vg-surface.c src/Makefile.sources src/Makefile.win32.features test/cairo-test.c test/get-clip.c util/cairo-trace

Chris Wilson ickle at kemper.freedesktop.org
Fri Jul 17 03:53:28 PDT 2009


 AUTHORS                             |    3 
 NEWS                                |    3 
 boilerplate/Makefile.sources        |    1 
 boilerplate/Makefile.win32.features |   30 
 boilerplate/cairo-boilerplate-vg.c  |  350 ++++++
 build/Makefile.win32.features       |    3 
 build/Makefile.win32.features-h     |    9 
 build/configure.ac.features         |    3 
 configure.ac                        |   43 
 src/Makefile.sources                |    3 
 src/Makefile.win32.features         |   42 
 src/cairo-vg-surface.c              | 1938 ++++++++++++++++++++++++++++++++++++
 src/cairo-vg.h                      |  103 +
 src/cairo.h                         |    4 
 test/cairo-test.c                   |    3 
 test/get-clip.c                     |    1 
 util/cairo-trace/trace.c            |   70 +
 17 files changed, 2606 insertions(+), 3 deletions(-)

New commits:
commit 52fa8760aeef38abbab0484a6978adaf4f100f90
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jul 15 16:37:25 2009 +0100

    Add OpenVG backend.
    
    Based on the work by Øyvind Kolås and Pierre Tardy -- many thanks to
    Pierre for pushing this backend for inclusion as well as testing and
    reviewing my initial patch. And many more thanks to pippin for writing the
    backend in the first place!
    
    Hacked and chopped by myself into a suitable basis for a backend. Quite a
    few issues remain open, but would seem to be ready for testing on suitable
    hardware.

diff --git a/AUTHORS b/AUTHORS
index 39a3179..5f8dd59 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -43,7 +43,7 @@ Amaury Jacquot <sxpert at esitcom.org> Documentation review, appplication testing
 Adrian Johnson <ajohnson at redneon.com> PDF backend improvement
 Michael Johnson <ahze at ahze.net> Bug fix for pre-C99 compilers
 Jonathon Jongsma <jonathon.jongsma at gmail.com> Fix documentation typos
-Øyvind Kolås <pippin at freedesktop.org> Bug fixes. Better default values.
+Øyvind Kolås <pippin at freedesktop.org> OpenVG backend, Bug fixes. Better default values.
 Martin Kretzschmar <martink at gnome.org> Arithmetic fix for 64-bit architectures
 Mathieu Lacage <Mathieu.Lacage at sophia.inria.fr> several bug/typo fixes
 Dominic Lachowicz <domlachowicz at gmail.com> PDF conformance fix, fix image surface to zero out contents
@@ -89,6 +89,7 @@ Travis Spencer <tspencer at cs.pdx.edu> XCB backend fix
 Bill Spitzak <spitzak at d2.com> Build fix to find Xrender.h without xrender.pc
 Zhe Su <james.su at gmail.com> Add support for fontconfig's embeddedbitmap option
 Owen Taylor <otaylor at redhat.com> Font rewrite, documentation, win32 backend
+Pierre Tardy <tardyp at gmail.com> EGL support and testing, OpenVG backend
 Karl Tomlinson <karlt+ at karlt.net> Optimisation and obscure bug fixes (mozilla)
 Alp Toker <alp at atoker.com> Fix several code/comment typos
 Malcolm Tredinnick <malcolm at commsecure.com.au> Documentation fixes
diff --git a/NEWS b/NEWS
index 4072aca..ef1a5d4 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,9 @@ New experimental backends:
 
    QT
 
+   OpenVG - The initial work was done by Øyvind Kolås, and made ready for
+            inclusion by Pierre Tardy.
+
 
 Snapshot 1.9.2 (2009-06-12)
 ===========================
diff --git a/boilerplate/Makefile.sources b/boilerplate/Makefile.sources
index f81781f..b40df4c 100644
--- a/boilerplate/Makefile.sources
+++ b/boilerplate/Makefile.sources
@@ -40,3 +40,4 @@ cairo_boilerplate_win32_sources = cairo-boilerplate-win32.c cairo-boilerplate-wi
 cairo_boilerplate_xcb_sources = cairo-boilerplate-xcb.c
 cairo_boilerplate_xlib_headers = cairo-boilerplate-xlib.h
 cairo_boilerplate_xlib_sources = cairo-boilerplate-xlib.c
+cairo_boilerplate_vg_sources = cairo-boilerplate-vg.c
diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index 33eaf5f..e691118 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -159,6 +159,36 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_directfb_private)
 enabled_cairo_boilerplate_sources += $(cairo_boilerplate_directfb_sources)
 endif
 
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_vg_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_vg_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_vg_private)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_vg_sources)
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_vg_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_vg_private)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_vg_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_egl_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_egl_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_egl_private)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_egl_sources)
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_egl_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_egl_private)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_egl_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_glx_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_glx_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_glx_private)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_glx_sources)
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_glx_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_glx_private)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_glx_sources)
+endif
+
 unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_script_headers)
 all_cairo_boilerplate_headers += $(cairo_boilerplate_script_headers)
 all_cairo_boilerplate_private += $(cairo_boilerplate_script_private)
diff --git a/boilerplate/cairo-boilerplate-vg.c b/boilerplate/cairo-boilerplate-vg.c
new file mode 100644
index 0000000..224a2d5
--- /dev/null
+++ b/boilerplate/cairo-boilerplate-vg.c
@@ -0,0 +1,350 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * 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 Chris Wilson.
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-vg.h>
+
+ /* XXX Not sure how to handle library specific context initialization */
+//#define USE_SHIVA
+//#define USE_AMANITH
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+
+typedef struct _vg_closure {
+    Display *dpy;
+    int screen;
+    Window win;
+
+    GLXContext ctx;
+    cairo_surface_t *surface;
+} vg_closure_glx_t;
+
+static void
+_cairo_boilerplate_vg_cleanup_glx (void *closure)
+{
+    vg_closure_glx_t *vgc = closure;
+
+#ifdef USE_AMANITH
+    vgDestroyContextAM ();
+#endif
+#ifdef USE_SHIVA
+    vgDestroyContextSH ();
+#endif
+
+    glXDestroyContext (vgc->dpy, vgc->ctx);
+    XDestroyWindow (vgc->dpy, vgc->win);
+    XCloseDisplay (vgc->dpy);
+    free (vgc);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_vg_create_surface_glx (const char	*name,
+					  cairo_content_t	 content,
+					  double		 width,
+					  double		 height,
+					  double		 max_width,
+					  double		 max_height,
+					  cairo_boilerplate_mode_t mode,
+					  int		 id,
+					  void		**closure)
+{
+    int rgba_attribs[] = {
+	GLX_RGBA,
+	GLX_RED_SIZE, 1,
+	GLX_GREEN_SIZE, 1,
+	GLX_BLUE_SIZE, 1,
+	GLX_ALPHA_SIZE, 1,
+	GLX_DOUBLEBUFFER,
+	None
+    };
+    int rgb_attribs[] = {
+	GLX_RGBA,
+	GLX_RED_SIZE, 1,
+	GLX_GREEN_SIZE, 1,
+	GLX_BLUE_SIZE, 1,
+	GLX_DOUBLEBUFFER,
+	None
+    };
+    XVisualInfo *vi;
+    Display *dpy;
+    Colormap cmap;
+    XSetWindowAttributes swa;
+    cairo_surface_t *surface;
+    cairo_vg_context_t *context;
+    vg_closure_glx_t *vgc;
+
+    vgc = malloc (sizeof (vg_closure_glx_t));
+    *closure = vgc;
+
+    if (width == 0)
+	width = 1;
+    if (height == 0)
+	height = 1;
+
+    dpy = XOpenDisplay (NULL);
+    vgc->dpy = dpy;
+    if (vgc->dpy == NULL) {
+	fprintf (stderr, "Failed to open display: %s\n", XDisplayName(0));
+	free (vgc);
+	return NULL;
+    }
+
+    if (content == CAIRO_CONTENT_COLOR)
+	vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgb_attribs);
+    else
+	vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
+
+    if (vi == NULL) {
+	fprintf (stderr, "Failed to create RGB, double-buffered visual\n");
+	XCloseDisplay (dpy);
+	free (vgc);
+	return NULL;
+    }
+
+    vgc->ctx = glXCreateContext (dpy, vi, NULL, True);
+    cmap = XCreateColormap (dpy,
+			    RootWindow (dpy, vi->screen),
+			    vi->visual,
+			    AllocNone);
+    swa.colormap = cmap;
+    swa.border_pixel = 0;
+    vgc->win = XCreateWindow (dpy, RootWindow (dpy, vi->screen),
+			      -1, -1, 1, 1, 0,
+			      vi->depth,
+			      InputOutput,
+			      vi->visual,
+			      CWBorderPixel | CWColormap, &swa);
+    XFreeColormap (dpy, cmap);
+    XFree (vi);
+
+    XMapWindow (dpy, vgc->win);
+
+    /* we need an active context to initialise VG */
+    glXMakeContextCurrent (dpy, vgc->win, vgc->win, vgc->ctx);
+
+#ifdef USE_AMANITH
+    vgInitContextAM (width, height, VG_FALSE, VG_TRUE);
+#endif
+#ifdef USE_SHIVA
+    vgCreateContextSH (width, height);
+#endif
+
+    context = cairo_vg_context_create_for_glx (dpy, vgc->ctx);
+    vgc->surface = cairo_vg_surface_create (context, content, width, height);
+    cairo_vg_context_destroy (context);
+
+    surface = vgc->surface;
+    if (cairo_surface_status (surface))
+	_cairo_boilerplate_vg_cleanup_glx (vgc);
+
+    return surface;
+}
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+typedef struct _vg_closure_egl {
+    EGLDisplay *dpy;
+    EGLContext *ctx;
+    EGLSurface *dummy;
+} vg_closure_egl_t;
+
+static void
+_cairo_boilerplate_vg_cleanup_egl (void *closure)
+{
+    vg_closure_egl_t *vgc = closure;
+
+#ifdef USE_AMANITH
+    vgDestroyContextAM ();
+#endif
+#ifdef USE_SHIVA
+    vgDestroyContextSH ();
+#endif
+
+    eglDestroyContext (vgc->dpy, vgc->ctx);
+    eglDestroySurface (vgc->dpy, vgc->dummy);
+    eglDestroyDisplay (vgc->dpy);
+    free (vgc);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_vg_create_surface_egl (const char	*name,
+					  cairo_content_t	 content,
+					  double		 width,
+					  double		 height,
+					  double		 max_width,
+					  double		 max_height,
+					  cairo_boilerplate_mode_t mode,
+					  int		 id,
+					  void		**closure)
+{
+    int rgba_attribs[] = {
+	EGL_RED_SIZE, 8,
+	EGL_GREEN_SIZE, 8,
+	EGL_BLUE_SIZE, 8,
+	EGL_ALPHA_SIZE, 8,
+	EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+	ELG_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+	None
+    };
+    int rgb_attribs[] = {
+	EGL_RED_SIZE, 8,
+	EGL_GREEN_SIZE, 8,
+	EGL_BLUE_SIZE, 8,
+	EGL_ALPHA_SIZE, 8,
+	EGL_VG_ALPHA_FORMAT, VG_ALPHA_FORMAT_PRE,
+	EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+	ELG_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+	None
+    };
+    int dummy_attribs[] = {
+	EGL_WIDTH, 8, EGL_HEIGHT, 8,
+	EGL_NONE
+    };
+    EGLDisplay *dpy;
+    int major, minor;
+    EGLConfig *config;
+    int num_configs;
+    EGLContext *egl_context;
+    cairo_vg_context_t *context;
+    cairo_vg_surface_t *surface;
+    vg_closure_egl_t *vgc;
+
+    dpy = eglGetDisplay (EGL_DEFAULT_DISPLAY);
+
+    if (! eglInitialize (dpy, &major, &minor))
+	return NULL;
+
+    eglBindAPI (EGL_OPENVG_API);
+
+    if (! eglChooseConfig (dpy,
+			   attribs,
+			   content == CAIRO_CONTENT_COLOR_ALPHA ?
+			   rgba_attribs : rgb_attribs,
+			   1, &num_configs) ||
+	num_configs != 1)
+    {
+	return NULL;
+    }
+
+    egl_context = eglCreateContext (dpy, config, NULL, NULL);
+    if (egl_context == NULL)
+	return NULL;
+
+    /* Create a dummy surface in order to enable a context to initialise VG */
+    dummy = eglCreatePbufferSurface (dpy, config, dummy_attribs);
+    if (dummy == NULL)
+	return NULL;
+    if (! eglMakeCurrent (dpy, dummy, dummy, egl_context))
+	return NULL;
+
+#ifdef USE_AMANITH
+    vgInitContextAM (width, height, VG_FALSE, VG_TRUE);
+#endif
+#ifdef USE_SHIVA
+    vgCreateContextSH (width, height);
+#endif
+
+    vgc = xmalloc (sizeof (vg_closure_egl_t));
+    vgc->dpy = dpy;
+    vgc->ctx = egl_context;
+    vgc->dummy = dummy;
+    *closure = vgc;
+
+    context = cairo_vg_context_create_for_egl (vgc->dpy, vgc->ctx);
+    vgc->surface = cairo_vg_surface_create (context, content, width, height);
+    cairo_vg_context_destroy (context);
+
+    surface = vgc->surface;
+    if (cairo_surface_status (surface))
+	_cairo_boilerplate_vg_cleanup_egl (vgc);
+
+    return surface;
+}
+#endif
+
+static void
+_cairo_boilerplate_vg_synchronize (void *closure)
+{
+    vgFinish ();
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+#if CAIRO_HAS_GLX_FUNCTIONS
+    {
+	"vg-glx", "vg", NULL, NULL,
+	CAIRO_SURFACE_TYPE_VG, CAIRO_CONTENT_COLOR_ALPHA, 1,
+	_cairo_boilerplate_vg_create_surface_glx,
+	NULL, NULL,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png,
+	_cairo_boilerplate_vg_cleanup_glx,
+	_cairo_boilerplate_vg_synchronize
+    },
+    {
+	"vg-glx", "vg", NULL, NULL,
+	CAIRO_SURFACE_TYPE_VG, CAIRO_CONTENT_COLOR, 1,
+	_cairo_boilerplate_vg_create_surface_glx,
+	NULL, NULL,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png,
+	_cairo_boilerplate_vg_cleanup_glx,
+	_cairo_boilerplate_vg_synchronize
+    },
+#endif
+#if CAIRO_HAS_EGL_FUNCTIONS
+    {
+	"vg-egl", "vg", NULL, NULL,
+	CAIRO_SURFACE_TYPE_VG, CAIRO_CONTENT_COLOR_ALPHA, 1,
+	_cairo_boilerplate_vg_create_surface_egl,
+	NULL, NULL,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png,
+	_cairo_boilerplate_vg_cleanup_egl,
+	_cairo_boilerplate_vg_synchronize
+    },
+    {
+	"vg-egl", "vg", NULL, NULL,
+	CAIRO_SURFACE_TYPE_VG, CAIRO_CONTENT_COLOR, 1,
+	_cairo_boilerplate_vg_create_surface_egl,
+	NULL, NULL,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png,
+	_cairo_boilerplate_vg_cleanup_egl,
+	_cairo_boilerplate_vg_synchronize
+    },
+#endif
+};
+CAIRO_BOILERPLATE (vg, targets)
diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features
index 3029c50..4d42c8b 100644
--- a/build/Makefile.win32.features
+++ b/build/Makefile.win32.features
@@ -14,6 +14,9 @@ CAIRO_HAS_BEOS_SURFACE=0
 CAIRO_HAS_PNG_FUNCTIONS=1
 CAIRO_HAS_GLITZ_SURFACE=0
 CAIRO_HAS_DIRECTFB_SURFACE=0
+CAIRO_HAS_VG_SURFACE=0
+CAIRO_HAS_EGL_FUNCTIONS=0
+CAIRO_HAS_GLX_FUNCTIONS=0
 CAIRO_HAS_SCRIPT_SURFACE=0
 CAIRO_HAS_FT_FONT=0
 CAIRO_HAS_FC_FONT=0
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index fd5f05d..2dc43bd 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -47,6 +47,15 @@ endif
 ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1)
 	@echo "#define CAIRO_HAS_DIRECTFB_SURFACE 1" >> src/cairo-features.h
 endif
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+	@echo "#define CAIRO_HAS_VG_SURFACE 1" >> src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+	@echo "#define CAIRO_HAS_EGL_FUNCTIONS 1" >> src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+	@echo "#define CAIRO_HAS_GLX_FUNCTIONS 1" >> src/cairo-features.h
+endif
 ifeq ($(CAIRO_HAS_SCRIPT_SURFACE),1)
 	@echo "#define CAIRO_HAS_SCRIPT_SURFACE 1" >> src/cairo-features.h
 endif
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 3f86680..e2d3262 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -377,6 +377,7 @@ AC_DEFUN([CAIRO_REPORT],
 	echo "  glitz:         $use_glitz"
 	echo "  BeOS:          $use_beos"
 	echo "  DirectFB:      $use_directfb"
+	echo "  OpenVG:        $use_vg"
 	echo ""
 	echo "The following font backends:"
 	echo "  User:          yes (always builtin)"
@@ -387,6 +388,8 @@ AC_DEFUN([CAIRO_REPORT],
 	echo ""
 	echo "The following functions:"
 	echo "  PNG functions: $use_png"
+	echo "  GLX functions: $use_glx"
+	echo "  EGL functions: $use_egl"
 	echo ""
 	echo "And the following internal features:"
 	echo "  gtk-doc:       $enable_gtk_doc"
diff --git a/configure.ac b/configure.ac
index de1be8f..2d4d5bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -242,6 +242,49 @@ CAIRO_ENABLE_SURFACE_BACKEND(directfb, directfb, no, [
 
 dnl ===========================================================================
 
+CAIRO_ENABLE_SURFACE_BACKEND(vg, OpenVG, no, [
+  dnl There is no pkgconfig for OpenVG; lets do a header check
+  AC_CHECK_HEADER(VG/openvg.h,, [use_vg="no (OpenVG headers not found)"])
+  if test "x$use_vg" = "xyes"; then
+      vg_NONPKGCONFIG_CFLAGS=
+      vg_NONPKGCONFIG_LIBS="-lOpenVG"
+      need_egl_functions=yes
+      need_glx_functions=yes
+  fi
+])
+
+CAIRO_ENABLE_FUNCTIONS(egl, EGL, auto, [
+  if test "x$need_egl_functions" = "xyes"; then
+    AC_CHECK_HEADER(EGL/egl.h,, [use_egl="no (EGL headers not found)"])
+    if test "x$use_egl" = "xyes"; then
+      egl_NONPKGCONFIG_CFLAGS=
+      egl_NONPKGCONFIG_LIBS=
+      for lib in EGL egl13 egl12 egl11; do
+	if test -z "$egl_NONPKGCONFIG_LIBS"; then
+          AC_CHECK_LIB($lib, eglGetError, egl_NONPKGCONFIG_LIBS="-l$lib")
+	fi
+      done
+      if test -z "$egl_NONPKGCONFIG_LIBS"; then
+        use_egl="no (EGL library not found)"
+      fi
+    fi
+  else
+      use_egl="no (not required by any backend)"
+  fi
+])
+
+CAIRO_ENABLE_FUNCTIONS(glx, GLX, auto, [
+  if test "x$need_glx_functions" = "xyes"; then
+    AC_CHECK_HEADER(GL/glx.h,, [use_glx="no (GLX headers not found)"])
+    glx_NONPKGCONFIG_CFLAGS=
+    glx_NONPKGCONFIG_LIBS="-lGL"
+  else
+      use_glx="no (not required by any backend)"
+  fi
+])
+
+dnl ===========================================================================
+
 any2ppm_cs=no
 CAIRO_ENABLE_SURFACE_BACKEND(script, script, no, [
   any2ppm_cs=yes
diff --git a/src/Makefile.sources b/src/Makefile.sources
index ef4e1c8..6bec91e 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -258,3 +258,6 @@ cairo_directfb_sources = cairo-directfb-surface.c
 
 cairo_script_headers = cairo-script.h
 cairo_script_sources = cairo-script-surface.c
+
+cairo_vg_headers = cairo-vg.h
+cairo_vg_sources = cairo-vg-surface.c
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 6b989a8..0de7b88 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -217,6 +217,48 @@ ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1)
 enabled_cairo_pkgconf += cairo-directfb.pc
 endif
 
+unsupported_cairo_headers += $(cairo_vg_headers)
+all_cairo_headers += $(cairo_vg_headers)
+all_cairo_private += $(cairo_vg_private)
+all_cairo_sources += $(cairo_vg_sources)
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+enabled_cairo_headers += $(cairo_vg_headers)
+enabled_cairo_private += $(cairo_vg_private)
+enabled_cairo_sources += $(cairo_vg_sources)
+endif
+all_cairo_pkgconf += cairo-vg.pc
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+enabled_cairo_pkgconf += cairo-vg.pc
+endif
+
+supported_cairo_headers += $(cairo_egl_headers)
+all_cairo_headers += $(cairo_egl_headers)
+all_cairo_private += $(cairo_egl_private)
+all_cairo_sources += $(cairo_egl_sources)
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_egl_headers)
+enabled_cairo_private += $(cairo_egl_private)
+enabled_cairo_sources += $(cairo_egl_sources)
+endif
+all_cairo_pkgconf += cairo-egl.pc
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-egl.pc
+endif
+
+supported_cairo_headers += $(cairo_glx_headers)
+all_cairo_headers += $(cairo_glx_headers)
+all_cairo_private += $(cairo_glx_private)
+all_cairo_sources += $(cairo_glx_sources)
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_glx_headers)
+enabled_cairo_private += $(cairo_glx_private)
+enabled_cairo_sources += $(cairo_glx_sources)
+endif
+all_cairo_pkgconf += cairo-glx.pc
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-glx.pc
+endif
+
 unsupported_cairo_headers += $(cairo_script_headers)
 all_cairo_headers += $(cairo_script_headers)
 all_cairo_private += $(cairo_script_private)
diff --git a/src/cairo-vg-surface.c b/src/cairo-vg-surface.c
new file mode 100644
index 0000000..de3d0c6
--- /dev/null
+++ b/src/cairo-vg-surface.c
@@ -0,0 +1,1938 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Opened Hand Ltd.
+ * Copyright © 2009 Chris Wilson
+ *
+ * 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.og/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.
+ *
+ * Contributor(s):
+ *      Pierre Tardy      <tardyp at gmail.com>
+ *      Øyvind Kolås      <pippin at gimp.org>
+ *      Vladimi Vukicevic <vladimir at mozilla.com> (stubbed out base backend)
+ *      Chris Wilson      <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-vg.h"
+
+#include "cairo-path-fixed-private.h"
+#include "cairo-meta-surface-private.h"
+#include "cairo-cache-private.h"
+
+#include <pixman.h>
+#include <VG/openvg.h>
+
+//#define OPENVG_DEBUG
+
+/*
+ * Work that needs to be done:
+ *  - Glyph cache / proper font support
+ *
+ *  - First-class paths
+ *    Paths are expensive for OpenVG, reuse paths whenever possible.
+ *    So add a path cache, and first class paths!
+ */
+
+typedef struct _cairo_vg_surface cairo_vg_surface_t;
+
+/* XXX need GL specific context control. :( */
+struct _cairo_vg_context {
+    cairo_status_t status;
+    cairo_reference_count_t ref_count;
+
+    unsigned long target_id;
+
+    VGPaint		paint;
+    cairo_vg_surface_t *source;
+    double		alpha;
+
+    cairo_cache_t snapshot_cache;
+
+    void *display;
+    void *context;
+
+    cairo_status_t (*create_target) (cairo_vg_context_t *,
+				     cairo_vg_surface_t *);
+    cairo_status_t (*set_target) (cairo_vg_context_t *,
+				  cairo_vg_surface_t *);
+    void (*destroy_target) (cairo_vg_context_t *, cairo_vg_surface_t *);
+};
+
+struct _cairo_vg_surface {
+    cairo_surface_t base;
+
+    cairo_vg_context_t *context;
+
+    VGImage	    image;
+    VGImageFormat   format;
+    int             width;
+    int             height;
+    cairo_bool_t    own_image;
+
+    cairo_cache_entry_t snapshot_cache_entry;
+
+    cairo_bool_t clipped;
+
+    unsigned long target_id;
+};
+
+static const cairo_surface_backend_t cairo_vg_surface_backend;
+
+slim_hidden_proto (cairo_vg_surface_create);
+
+static cairo_surface_t *
+_vg_surface_create_internal (cairo_vg_context_t *context,
+			     VGImage image,
+			     VGImageFormat format,
+			     int width, int height);
+
+static cairo_vg_context_t *
+_vg_context_reference (cairo_vg_context_t *context)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
+
+    _cairo_reference_count_inc (&context->ref_count);
+
+    return context;
+}
+
+static cairo_vg_context_t *
+_vg_context_lock (cairo_vg_context_t *context)
+{
+    /* XXX if we need to add locking, then it has to be recursive */
+    return context;
+}
+
+static cairo_int_status_t
+_vg_context_set_target (cairo_vg_context_t *context,
+			cairo_vg_surface_t *surface)
+{
+    cairo_status_t status;
+
+    if (surface->target_id == 0) {
+	status = context->create_target (context, surface);
+	if (unlikely (status))
+	    return status;
+    }
+
+    if (context->target_id == surface->target_id)
+	return CAIRO_STATUS_SUCCESS;
+
+    context->target_id = surface->target_id;
+
+    return context->set_target (context, surface);
+}
+
+static void
+_vg_context_destroy_target (cairo_vg_context_t *context,
+			    cairo_vg_surface_t *surface)
+{
+    if (surface->target_id == 0)
+	return;
+
+    if (context->target_id == surface->target_id)
+	context->set_target (context, NULL);
+
+    context->destroy_target (context, surface);
+}
+
+static cairo_bool_t
+_vg_snapshot_cache_can_remove (const void *entry)
+{
+    return TRUE;
+}
+
+static void
+_vg_snapshot_cache_remove (void *cache_entry)
+{
+    cairo_vg_surface_t *surface = cairo_container_of (cache_entry,
+						      cairo_vg_surface_t,
+						      snapshot_cache_entry);
+    surface->snapshot_cache_entry.hash = 0;
+    cairo_surface_destroy (&surface->base);
+}
+
+static cairo_status_t
+_vg_context_init (cairo_vg_context_t *context)
+{
+    cairo_status_t status;
+
+    context->status = CAIRO_STATUS_SUCCESS;
+    CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1);
+
+    status = _cairo_cache_init (&context->snapshot_cache,
+				NULL,
+				_vg_snapshot_cache_can_remove,
+				_vg_snapshot_cache_remove,
+				16*1024*1024);
+    if (unlikely (status))
+	return status;
+
+    context->target_id = 0;
+    context->source = NULL;
+    context->alpha = 1.0;
+
+    context->paint = vgCreatePaint ();
+    vgLoadIdentity ();
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_vg_context_destroy (cairo_vg_context_t *context)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&context->ref_count))
+	return;
+
+    if (context->paint != VG_INVALID_HANDLE)
+	vgDestroyPaint (context->paint);
+
+    _cairo_cache_fini (&context->snapshot_cache);
+    free (context);
+}
+
+static void
+_vg_context_unlock (cairo_vg_context_t *context)
+{
+}
+
+#ifdef OPENVG_DEBUG
+static void check_vg_errors(const char*function,int line)
+{
+    int err = vgGetError();
+    if (err != VG_NO_ERROR){
+	printf("%s+%d:vgError detected: 0x%08x.\n",function, line,err);
+	assert(err == VG_NO_ERROR);
+    }
+
+}
+#define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__)
+#else
+#define CHECK_VG_ERRORS() do{}while(0)
+#endif //OPENVG_DEBUG
+
+static pixman_format_code_t
+_vg_format_to_pixman (VGImageFormat format,
+		      cairo_bool_t *needs_premult_fixup)
+{
+    *needs_premult_fixup = FALSE;
+    switch (format) {
+	/* RGB{A,X} channel ordering */
+    case VG_sRGBX_8888: return 0; //PIXMAN_r8g8b8x8;
+    case VG_sRGBA_8888: return 0;
+    case VG_sRGBA_8888_PRE: return 0; //PIXMAN_r8b8g8a8;
+    case VG_sRGB_565: return PIXMAN_r5g6b5;
+    case VG_sRGBA_5551: return 0;
+    case VG_sRGBA_4444: return 0;
+    case VG_sL_8: return PIXMAN_g8;
+    case VG_lRGBX_8888: return 0;
+    case VG_lRGBA_8888: return 0;
+    case VG_lRGBA_8888_PRE: return 0;
+    case VG_lL_8: return 0;
+    case VG_A_8: return PIXMAN_a8;
+    case VG_BW_1: return PIXMAN_a1;
+    case VG_A_1: return PIXMAN_a1;
+    case VG_A_4: return PIXMAN_a4;
+
+	/* {A,X}RGB channel ordering */
+    case VG_sXRGB_8888: return PIXMAN_x8r8g8b8;
+    case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8;
+    case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8;
+    case VG_sARGB_1555: return 0;
+    case VG_sARGB_4444: return 0;
+    case VG_lXRGB_8888: return 0;
+    case VG_lARGB_8888: return 0;
+    case VG_lARGB_8888_PRE: return 0;
+
+	/* BGR{A,X} channel ordering */
+    case VG_sBGRX_8888: return PIXMAN_b8g8r8x8;
+    case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8;
+    case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8;
+    case VG_sBGR_565: return PIXMAN_b5g6r5;
+    case VG_sBGRA_5551: return 0;
+    case VG_sBGRA_4444: return 0;
+    case VG_lBGRX_8888: return 0;
+    case VG_lBGRA_8888: return 0;
+    case VG_lBGRA_8888_PRE: return 0;
+
+	/* {A,X}BGR channel ordering */
+    case VG_sXBGR_8888: return PIXMAN_x8b8g8r8;
+    case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8;
+    case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8;
+    case VG_sABGR_1555: return 0;
+    case VG_sABGR_4444: return 0;
+    case VG_lXBGR_8888: return 0;
+    case VG_lABGR_8888: return 0;
+    case VG_lABGR_8888_PRE: return 0;
+    default: return 0;
+    }
+}
+
+static pixman_format_code_t
+_vg_format_to_content (VGImageFormat format)
+{
+    /* XXX could use more simple bit tests */
+    switch (format) {
+	/* RGB{A,X} channel ordering */
+    case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR;
+    case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sRGB_565: return CAIRO_CONTENT_COLOR;
+    case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sL_8: return CAIRO_CONTENT_ALPHA;
+    case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR;
+    case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lL_8: return CAIRO_CONTENT_ALPHA;
+    case VG_A_8: return CAIRO_CONTENT_ALPHA;
+    case VG_A_4: return CAIRO_CONTENT_ALPHA;
+    case VG_A_1: return CAIRO_CONTENT_ALPHA;
+    case VG_BW_1: return CAIRO_CONTENT_ALPHA;
+
+	/* {A,X}RGB channel ordering */
+    case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR;
+    case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR;
+    case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+
+	/* BGR{A,X} channel ordering */
+    case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR;
+    case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sBGR_565: return CAIRO_CONTENT_COLOR;
+    case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR;
+    case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+
+	/* {A,X}BGR channel ordering */
+    case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR;
+    case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR;
+    case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    default: return 0;
+    }
+}
+
+static VGImageFormat
+_vg_format_from_pixman (pixman_format_code_t format)
+{
+    /* XXX _PRE needs fixup */
+    switch ((int) format) {
+    case PIXMAN_r5g6b5: return VG_sRGB_565;
+    case PIXMAN_g8: return VG_sL_8;
+    case PIXMAN_a8: return VG_A_8;
+    case PIXMAN_a1: return VG_BW_1;
+    case PIXMAN_x8r8g8b8: return VG_sXRGB_8888;
+    case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE
+    case PIXMAN_b8g8r8x8: return VG_sBGRX_8888;
+    case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE
+    case PIXMAN_b5g6r5: return VG_sBGR_565;
+    case PIXMAN_x8b8g8r8: return VG_sXBGR_8888;
+    case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE
+    default: return 0;
+    }
+}
+
+static VGImageFormat
+_vg_format_for_content (cairo_content_t content)
+{
+    switch (content) {
+    case CAIRO_CONTENT_ALPHA: return VG_A_8;
+    case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888;
+    default: ASSERT_NOT_REACHED;
+    case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE
+    }
+}
+
+static cairo_surface_t *
+_vg_surface_create_similar (void            *abstract_surface,
+			    cairo_content_t  content,
+			    int              width,
+			    int              height)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+
+    if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+	height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+    {
+	return NULL;
+    }
+
+    return cairo_vg_surface_create (surface->context, content, width, height);
+}
+
+static cairo_int_status_t
+_vg_surface_intersect_clip_path (void               *abstract_surface,
+				 cairo_path_fixed_t *path,
+				 cairo_fill_rule_t   fill_rule,
+				 double              tolerance,
+				 cairo_antialias_t   antialias)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_vg_surface_t *mask;
+    cairo_rectangle_int_t extents, clip_extents;
+    cairo_solid_pattern_t white;
+    cairo_status_t status;
+
+    if (path == NULL) {
+	surface->clipped = FALSE;
+	vgMask (VG_INVALID_HANDLE,
+		VG_FILL_MASK, 0, 0, surface->width, surface->height);
+	vgSeti (VG_MASKING, VG_FALSE);
+	CHECK_VG_ERRORS();
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    extents.x = extents.y = 0;
+    extents.width = surface->width;
+    extents.height = surface->height;
+    _cairo_path_fixed_approximate_clip_extents (path, &clip_extents);
+    if (! _cairo_rectangle_intersect (&clip_extents, &extents))
+	surface->clipped = TRUE;
+
+    if (surface->clipped)
+	return CAIRO_STATUS_SUCCESS;
+
+    mask = (cairo_vg_surface_t *)
+	_vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA,
+				    surface->width, surface->height);
+    if (unlikely (mask == NULL))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (unlikely (mask->base.status))
+	return mask->base.status;
+
+    _cairo_pattern_init_solid (&white, CAIRO_COLOR_WHITE, CAIRO_CONTENT_ALPHA);
+    status = _cairo_surface_fill (&mask->base,
+				  CAIRO_OPERATOR_SOURCE, &white.base,
+				  path, fill_rule, tolerance, antialias,
+				  NULL);
+    if (status) {
+	cairo_surface_destroy (&mask->base);
+	return status;
+    }
+
+    vgSeti (VG_MASKING, VG_TRUE);
+    vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height);
+
+    cairo_surface_destroy (&mask->base);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_get_extents (void                  *abstract_surface,
+			 cairo_rectangle_int_t *extents)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+
+    extents->x = 0;
+    extents->y = 0;
+    extents->width  = surface->width;
+    extents->height = surface->height;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+#define MAX_SEG  16  /* max number of knots to upload in a batch */
+
+typedef struct _vg_path {
+    VGPath path;
+    cairo_matrix_t *ctm_inverse;
+
+    VGubyte gseg[MAX_SEG];
+    VGfloat gdata[MAX_SEG*3*2];
+    int dcount;
+    int scount;
+} vg_path_t;
+
+static cairo_status_t
+_vg_move_to (void          *closure,
+	     const cairo_point_t *point)
+{
+    vg_path_t *path = closure;
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    if (path->ctm_inverse)
+	cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
+
+    path->gseg[path->scount++] = VG_MOVE_TO;
+    path->gdata[path->dcount++] = x;
+    path->gdata[path->dcount++] = y;
+
+    if (path->scount >= MAX_SEG-1) {
+	vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
+	path->scount = 0;
+	path->dcount = 0;
+    }
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_line_to (void          *closure,
+	     const cairo_point_t *point)
+{
+    vg_path_t *path = closure;
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    if (path->ctm_inverse)
+	cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
+
+    path->gseg[path->scount++] = VG_LINE_TO;
+    path->gdata[path->dcount++] = x;
+    path->gdata[path->dcount++] = y;
+
+    if (path->scount >= MAX_SEG-1) {
+	vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
+	path->scount = 0;
+	path->dcount = 0;
+    }
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_curve_to (void          *closure,
+	      const cairo_point_t *p0,
+	      const cairo_point_t *p1,
+	      const cairo_point_t *p2)
+{
+    vg_path_t *path = closure;
+    double x0 = _cairo_fixed_to_double (p0->x);
+    double y0 = _cairo_fixed_to_double (p0->y);
+    double x1 = _cairo_fixed_to_double (p1->x);
+    double y1 = _cairo_fixed_to_double (p1->y);
+    double x2 = _cairo_fixed_to_double (p2->x);
+    double y2 = _cairo_fixed_to_double (p2->y);
+
+    if (path->ctm_inverse) {
+	cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0);
+	cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1);
+	cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2);
+    }
+
+    path->gseg[path->scount++] = VG_CUBIC_TO;
+    path->gdata[path->dcount++] = x0;
+    path->gdata[path->dcount++] = y0;
+    path->gdata[path->dcount++] = x1;
+    path->gdata[path->dcount++] = y1;
+    path->gdata[path->dcount++] = x2;
+    path->gdata[path->dcount++] = y2;
+
+    if (path->scount >= MAX_SEG-1) {
+	vgAppendPathData(path->path, path->scount, path->gseg, path->gdata);
+	path->scount = 0;
+	path->dcount = 0;
+    }
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_close_path (void *closure)
+{
+    vg_path_t *path = closure;
+
+    path->gseg[path->scount++] = VG_CLOSE_PATH;
+
+    if (path->scount >= MAX_SEG-1) {
+	vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
+	path->scount = 0;
+	path->dcount = 0;
+    }
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_vg_path_from_cairo (vg_path_t    *vg_path,
+		     cairo_path_fixed_t *path)
+{
+    cairo_status_t status;
+
+    vg_path->scount = 0;
+    vg_path->dcount = 0;
+
+    status = _cairo_path_fixed_interpret (path,
+					  CAIRO_DIRECTION_FORWARD,
+					  _vg_move_to,
+					  _vg_line_to,
+					  _vg_curve_to,
+					  _vg_close_path,
+					  vg_path);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    vgAppendPathData (vg_path->path,
+		      vg_path->scount, vg_path->gseg, vg_path->gdata);
+    CHECK_VG_ERRORS();
+}
+
+static cairo_bool_t
+_vg_is_supported_operator (cairo_operator_t op)
+{
+    switch ((int) op) {
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_DEST_OVER:
+    case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_ADD:
+	return TRUE;
+
+    default:
+	return FALSE;
+    }
+}
+
+static VGBlendMode
+_vg_operator (cairo_operator_t op)
+{
+    switch ((int) op) {
+    case CAIRO_OPERATOR_SOURCE:
+	return VG_BLEND_SRC;
+    case CAIRO_OPERATOR_OVER:
+	return VG_BLEND_SRC_OVER;
+    case CAIRO_OPERATOR_IN:
+	return VG_BLEND_SRC_IN;
+    case CAIRO_OPERATOR_DEST_OVER:
+	return VG_BLEND_DST_OVER;
+    case CAIRO_OPERATOR_DEST_IN:
+	return VG_BLEND_DST_IN;
+    case CAIRO_OPERATOR_ADD:
+	return VG_BLEND_ADDITIVE;
+    default:
+	ASSERT_NOT_REACHED;
+	return VG_BLEND_SRC_OVER;
+    }
+}
+
+static VGFillRule
+_vg_fill_rule_from_cairo (cairo_fill_rule_t rule)
+{
+    switch (rule) {
+    case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD;
+    case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO;
+    }
+
+    ASSERT_NOT_REACHED;
+    return VG_NON_ZERO;
+}
+
+static VGRenderingQuality
+_vg_rendering_quality_from_cairo (cairo_antialias_t aa)
+{
+    switch (aa) {
+    case CAIRO_ANTIALIAS_DEFAULT:
+    case CAIRO_ANTIALIAS_SUBPIXEL:
+	return VG_RENDERING_QUALITY_BETTER;
+
+    case CAIRO_ANTIALIAS_GRAY:
+	return VG_RENDERING_QUALITY_FASTER;
+
+    case CAIRO_ANTIALIAS_NONE:
+	return VG_RENDERING_QUALITY_NONANTIALIASED;
+    }
+
+    ASSERT_NOT_REACHED;
+    return VG_RENDERING_QUALITY_BETTER;
+}
+
+static VGCapStyle
+_vg_line_cap_from_cairo (cairo_line_cap_t cap)
+{
+    switch (cap) {
+    case CAIRO_LINE_CAP_BUTT:   return VG_CAP_BUTT;
+    case CAIRO_LINE_CAP_ROUND:  return VG_CAP_ROUND;
+    case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE;
+    }
+
+    ASSERT_NOT_REACHED;
+    return VG_CAP_BUTT;
+}
+
+static VGJoinStyle
+_vg_line_join_from_cairo (cairo_line_join_t join)
+{
+    switch (join) {
+    case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER;
+    case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND;
+    case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL;
+    }
+
+    ASSERT_NOT_REACHED;
+    return VG_JOIN_MITER;
+}
+
+static void
+_vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src)
+{
+    dst[0] = /* sx  */ src->xx;
+    dst[1] = /* shy */ src->yx;
+    dst[2] = /* w0  */ 0;
+    dst[3] = /* shx */ src->xy;
+    dst[4] = /* sy  */ src->yy;
+    dst[5] = /* w1  */ 0;
+    dst[6] = /* tx  */ src->x0;
+    dst[7] = /* ty  */ src->y0;
+    dst[8] = /* w2  */ 0;
+}
+
+static cairo_status_t
+_vg_setup_gradient_stops (cairo_vg_context_t *context,
+			  const cairo_gradient_pattern_t *pattern)
+{
+    VGint numstops = pattern->n_stops;
+    VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)];
+    int i;
+
+    if (numstops*5 < ARRAY_LENGTH (stack_stops)) {
+	stops = stack_stops;
+    } else {
+	stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat));
+	if (unlikely (stops == NULL))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    for (i = 0; i < numstops; i++) {
+	stops[i*5 + 0] = pattern->stops[i].offset;
+	stops[i*5 + 1] = pattern->stops[i].color.red;
+	stops[i*5 + 2] = pattern->stops[i].color.green;
+	stops[i*5 + 3] = pattern->stops[i].color.blue;
+	stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha;
+    }
+
+    vgSetParameterfv (context->paint,
+		      VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops);
+
+    if (stops != stack_stops)
+	free (stops);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_vg_set_source_matrix (const cairo_pattern_t *pat)
+{
+    cairo_matrix_t mat;
+    cairo_status_t status;
+    VGfloat vmat[9];
+
+    mat = pat->matrix;
+    status = cairo_matrix_invert (&mat);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    _vg_matrix_from_cairo (vmat, &mat);
+
+    vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
+    vgLoadMatrix (vmat);
+    vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER);
+    vgLoadMatrix (vmat);
+    vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+
+    CHECK_VG_ERRORS();
+}
+
+static cairo_status_t
+_vg_setup_linear_source (cairo_vg_context_t *context,
+			 const cairo_linear_pattern_t *lpat)
+{
+    VGfloat linear[4];
+
+    linear[0] = _cairo_fixed_to_double (lpat->p1.x);
+    linear[1] = _cairo_fixed_to_double (lpat->p1.y);
+    linear[2] = _cairo_fixed_to_double (lpat->p2.x);
+    linear[3] = _cairo_fixed_to_double (lpat->p2.y);
+
+    vgSetParameteri (context->paint,
+		     VG_PAINT_COLOR_RAMP_SPREAD_MODE,
+		     VG_COLOR_RAMP_SPREAD_PAD);
+    vgSetParameteri (context->paint,
+		     VG_PAINT_TYPE,
+		     VG_PAINT_TYPE_LINEAR_GRADIENT);
+    vgSetParameterfv (context->paint,
+		      VG_PAINT_LINEAR_GRADIENT, 4, linear);
+
+    _vg_set_source_matrix (&lpat->base.base);
+
+    CHECK_VG_ERRORS();
+    return _vg_setup_gradient_stops (context, &lpat->base);
+
+}
+
+static cairo_status_t
+_vg_setup_radial_source (cairo_vg_context_t *context,
+			 cairo_radial_pattern_t *rpat)
+{
+    VGfloat radial[5];
+
+    radial[0] = _cairo_fixed_to_double (rpat->c1.x);
+    radial[1] = _cairo_fixed_to_double (rpat->c1.y);
+    radial[2] = _cairo_fixed_to_double (rpat->c2.x);
+    radial[3] = _cairo_fixed_to_double (rpat->c2.y);
+    radial[4] = _cairo_fixed_to_double (rpat->r2);
+
+    vgSetParameteri (context->paint,
+		     VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD);
+    vgSetParameteri (context->paint,
+		     VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT);
+    vgSetParameterfv (context->paint,
+		      VG_PAINT_RADIAL_GRADIENT, 5, radial);
+
+    _vg_set_source_matrix (&rpat->base.base);
+
+    /* FIXME: copy/adapt fixes from SVG backend to add inner radius */
+
+    CHECK_VG_ERRORS();
+    return _vg_setup_gradient_stops (context, &rpat->base);
+}
+
+static cairo_status_t
+_vg_setup_solid_source (cairo_vg_context_t *context,
+			cairo_solid_pattern_t *spat)
+{
+    VGfloat color[] = {
+	spat->color.red,
+	spat->color.green,
+	spat->color.blue,
+	spat->color.alpha * context->alpha
+    };
+
+    vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+    vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_vg_surface_t *
+_vg_clone_meta_surface (cairo_vg_context_t *context,
+			cairo_surface_t *surface)
+{
+    VGImage vg_image;
+    VGImageFormat format;
+    cairo_status_t status;
+    cairo_rectangle_int_t extents;
+    cairo_vg_surface_t *clone;
+
+    status = _cairo_surface_get_extents (surface, &extents);
+    if (status)
+	return NULL;
+
+    if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+	extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+    {
+	return NULL;
+    }
+
+    format = _vg_format_for_content (surface->content);
+
+    /* NONALIASED, FASTER, BETTER */
+    vg_image = vgCreateImage (format,
+			      extents.width, extents.height,
+			      VG_IMAGE_QUALITY_FASTER);
+    clone = (cairo_vg_surface_t *)
+	_vg_surface_create_internal (context, vg_image, format,
+				     extents.width, extents.height);
+    cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y);
+
+    status = cairo_meta_surface_replay (surface, &clone->base);
+    if (unlikely (status)) {
+	cairo_surface_destroy (&clone->base);
+	return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
+    }
+
+    return clone;
+}
+
+static cairo_vg_surface_t *
+_vg_clone_image_surface (cairo_vg_context_t *context,
+			 cairo_surface_t *surface)
+{
+    cairo_image_surface_t *image;
+    void *image_extra;
+    cairo_status_t status;
+    VGImage vg_image;
+    VGImageFormat format;
+    cairo_rectangle_int_t extents;
+    cairo_vg_surface_t *clone;
+
+    if (surface->backend->acquire_source_image == NULL)
+	return NULL;
+
+    status = _cairo_surface_get_extents (surface, &extents);
+    if (status)
+	return NULL;
+
+    if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+	extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+    {
+	return NULL;
+    }
+
+    status = _cairo_surface_acquire_source_image (surface,
+						  &image, &image_extra);
+    if (unlikely (status))
+	return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
+
+    format = _vg_format_from_pixman (image->pixman_format);
+    if (format == 0)
+	format = _vg_format_for_content (image->base.content);
+
+    /* NONALIASED, FASTER, BETTER */
+    vg_image = vgCreateImage (format,
+			      image->width, image->height,
+			      VG_IMAGE_QUALITY_FASTER);
+    clone = (cairo_vg_surface_t *)
+	_vg_surface_create_internal (context, vg_image, format,
+				    image->width, image->height);
+    if (unlikely (clone->base.status))
+	return clone;
+
+    vgImageSubData (clone->image,
+		    image->data, image->stride,
+		    format, 0, 0, image->width, image->height);
+
+    _cairo_surface_release_source_image (surface, image, image_extra);
+
+    return clone;
+}
+
+static void
+_vg_surface_remove_from_cache (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface;
+
+    if (surface->snapshot_cache_entry.hash) {
+	cairo_vg_context_t *context;
+
+	context = _vg_context_lock (surface->context);
+	_cairo_cache_remove (&context->snapshot_cache,
+			     &surface->snapshot_cache_entry);
+	_vg_context_unlock (context);
+
+	surface->snapshot_cache_entry.hash = 0;
+    }
+}
+
+static cairo_status_t
+_vg_setup_surface_source (cairo_vg_context_t *context,
+			  cairo_surface_pattern_t *spat)
+{
+    cairo_surface_t *snapshot;
+    cairo_vg_surface_t *clone;
+    cairo_status_t status;
+
+    snapshot = _cairo_surface_has_snapshot (spat->surface,
+					    &cairo_vg_surface_backend,
+					    spat->surface->content);
+    if (snapshot != NULL) {
+	clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot);
+	goto DONE;
+    }
+
+    if (_cairo_surface_is_meta (spat->surface))
+	clone = _vg_clone_meta_surface (context, spat->surface);
+    else
+	clone = _vg_clone_image_surface (context, spat->surface);
+    if (clone == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (unlikely (clone->base.status))
+	return clone->base.status;
+
+    clone->snapshot_cache_entry.hash = clone->base.unique_id;
+    status = _cairo_cache_insert (&context->snapshot_cache,
+				  &clone->snapshot_cache_entry);
+    if (unlikely (status)) {
+	clone->snapshot_cache_entry.hash = 0;
+	cairo_surface_destroy (&clone->base);
+	return status;
+    }
+
+    status = _cairo_surface_attach_snapshot (spat->surface, &clone->base,
+					     _vg_surface_remove_from_cache);
+    if (unlikely (status)) {
+	cairo_surface_destroy (&clone->base);
+	return status;
+    }
+
+DONE:
+    cairo_surface_destroy (&context->source->base);
+    context->source = clone;
+
+    vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
+
+    switch (spat->base.extend) {
+    case CAIRO_EXTEND_PAD:
+	vgSetParameteri (context->paint,
+			 VG_PAINT_PATTERN_TILING_MODE,
+			 VG_TILE_PAD);
+	break;
+
+    case CAIRO_EXTEND_NONE:
+	vgSetParameteri (context->paint,
+			 VG_PAINT_PATTERN_TILING_MODE,
+			 VG_TILE_FILL);
+	{
+	    VGfloat color[] = {0,0,0,0};
+	    vgSetfv (VG_TILE_FILL_COLOR, 4, color);
+	}
+	break;
+
+    case CAIRO_EXTEND_REPEAT:
+	vgSetParameteri (context->paint,
+			 VG_PAINT_PATTERN_TILING_MODE,
+			 VG_TILE_REPEAT);
+	break;
+
+    default:
+	ASSERT_NOT_REACHED;
+    case CAIRO_EXTEND_REFLECT:
+	vgSetParameteri (context->paint,
+			 VG_PAINT_PATTERN_TILING_MODE,
+			 VG_TILE_REFLECT);
+	break;
+    }
+    vgPaintPattern (context->paint, context->source->image);
+
+    _vg_set_source_matrix (&spat->base);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+setup_source (cairo_vg_context_t *context,
+	      const cairo_pattern_t *source)
+{
+    switch (source->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+	return _vg_setup_solid_source (context,
+				       (cairo_solid_pattern_t *) source);
+    case CAIRO_PATTERN_TYPE_LINEAR:
+	return _vg_setup_linear_source (context,
+					(cairo_linear_pattern_t *) source);
+    case CAIRO_PATTERN_TYPE_RADIAL:
+	return _vg_setup_radial_source (context,
+					(cairo_radial_pattern_t *) source);
+    case CAIRO_PATTERN_TYPE_SURFACE:
+	return _vg_setup_surface_source (context,
+					 (cairo_surface_pattern_t *) source);
+    default:
+	ASSERT_NOT_REACHED;
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+}
+
+static cairo_int_status_t
+_vg_surface_stroke (void                 *abstract_surface,
+		    cairo_operator_t      op,
+		    const 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,
+		    cairo_rectangle_int_t *extents)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+    VGfloat state[9];
+    VGfloat strokeTransform[9];
+    vg_path_t vg_path;
+
+    if (surface->clipped)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (! _vg_is_supported_operator (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    context = _vg_context_lock (surface->context);
+    status = _vg_context_set_target (context, surface);
+    if (status) {
+	_vg_context_unlock (context);
+	return status;
+    }
+
+    status = setup_source (context, source);
+    if (status) {
+	_vg_context_unlock (context);
+	return status;
+    }
+
+    vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
+				 VG_PATH_DATATYPE_F,
+				 1, 0, 0, 0,
+				 VG_PATH_CAPABILITY_ALL);
+
+    vgGetMatrix (state);
+    _vg_matrix_from_cairo (strokeTransform, ctm);
+    vgMultMatrix (strokeTransform);
+
+    vg_path.ctm_inverse = ctm_inverse;
+
+    _vg_path_from_cairo (&vg_path, path);
+
+    /* XXX DASH_PATTERN, DASH_PHASE */
+    vgSetf (VG_STROKE_LINE_WIDTH, style->line_width);
+    vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit);
+    vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join));
+    vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap));
+
+    vgSeti (VG_BLEND_MODE, _vg_operator (op));
+
+    vgSetPaint (context->paint, VG_STROKE_PATH);
+
+    vgDrawPath (vg_path.path, VG_STROKE_PATH);
+
+    vgDestroyPath (vg_path.path);
+
+    vgLoadMatrix (state);
+
+    CHECK_VG_ERRORS();
+    _vg_context_unlock (context);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_fill (void                  *abstract_surface,
+		  cairo_operator_t       op,
+		  const cairo_pattern_t *source,
+		  cairo_path_fixed_t    *path,
+		  cairo_fill_rule_t      fill_rule,
+		  double                 tolerance,
+		  cairo_antialias_t      antialias,
+		  cairo_rectangle_int_t *extents)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+    vg_path_t vg_path;
+
+    if (surface->clipped)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (op == CAIRO_OPERATOR_DEST)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (! _vg_is_supported_operator (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    context = _vg_context_lock (surface->context);
+    status = _vg_context_set_target (context, surface);
+    if (status) {
+	_vg_context_unlock (context);
+	return status;
+    }
+
+    status = setup_source (context, source);
+    if (status) {
+	_vg_context_unlock (context);
+	return status;
+    }
+
+    vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
+				 VG_PATH_DATATYPE_F,
+				 1, 0,
+				 0, 0,
+				 VG_PATH_CAPABILITY_ALL);
+    vg_path.ctm_inverse = NULL;
+
+    _vg_path_from_cairo (&vg_path, path);
+
+    /* XXX tolerance */
+
+    vgSeti (VG_BLEND_MODE, _vg_operator (op));
+    vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule));
+    vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias));
+
+    vgSetPaint (context->paint, VG_FILL_PATH);
+
+    vgDrawPath (vg_path.path, VG_FILL_PATH);
+
+    vgDestroyPath (vg_path.path);
+
+    _vg_context_unlock (context);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_paint (void             *abstract_surface,
+		   cairo_operator_t  op,
+		   const cairo_pattern_t  *source,
+		   cairo_rectangle_int_t *extents)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+
+    if (surface->clipped)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (op == CAIRO_OPERATOR_DEST)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (! _vg_is_supported_operator (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    context = _vg_context_lock (surface->context);
+    status = _vg_context_set_target (context, surface);
+    if (status) {
+	_vg_context_unlock (context);
+	return status;
+    }
+
+    status = setup_source (context, source);
+    if (status) {
+	_vg_context_unlock (context);
+	return status;
+    }
+
+    vgSeti (VG_BLEND_MODE, _vg_operator (op));
+    vgSetPaint (context->paint, VG_FILL_PATH);
+
+    { /* creating a rectangular path that should cover the extent */
+	VGubyte segs[] = {
+	    VG_MOVE_TO_ABS, VG_LINE_TO_ABS,
+	    VG_LINE_TO_ABS, VG_LINE_TO_ABS,
+	    VG_CLOSE_PATH
+	};
+	VGfloat data[] = {
+	    0, 0,
+	    surface->width, 0,
+	    surface->width, surface->height,
+	    0, surface->height
+	};
+	VGPath fullext;
+
+	fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+				1,0,0,0, VG_PATH_CAPABILITY_ALL);
+	vgAppendPathData (fullext, sizeof(segs), segs, data);
+
+	vgDrawPath (fullext, VG_FILL_PATH);
+
+	vgDestroyPath (fullext);
+    }
+
+    _vg_context_unlock (context);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_mask (void                   *abstract_surface,
+		  cairo_operator_t       op,
+		  const cairo_pattern_t  *source,
+		  const cairo_pattern_t  *mask,
+		  cairo_rectangle_int_t  *extents)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (surface->clipped)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (! _vg_is_supported_operator (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Handle paint-with-alpha to do fades cheaply */
+    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask;
+	cairo_vg_context_t *context = _vg_context_lock (surface->context);
+	double alpha = context->alpha;
+
+	context->alpha = solid->color.alpha;
+	status = _vg_surface_paint (abstract_surface, op, source, extents);
+	context->alpha = alpha;
+
+	_vg_context_unlock (context);
+
+	return status;
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static void
+_vg_surface_get_font_options (void                  *abstract_surface,
+			      cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+}
+
+static cairo_int_status_t
+_vg_surface_show_glyphs (void                  *abstract_surface,
+			 cairo_operator_t       op,
+			 const cairo_pattern_t *source,
+			 cairo_glyph_t         *glyphs,
+			 int                    num_glyphs,
+			 cairo_scaled_font_t   *scaled_font,
+			 int		         *remaining_glyphs,
+			 cairo_rectangle_int_t *extents)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_path_fixed_t path;
+
+    if (num_glyphs <= 0)
+	return CAIRO_STATUS_SUCCESS;
+
+    _cairo_path_fixed_init (&path);
+
+    /* XXX Glyph cache! OpenVG font support in 1.1? */
+
+    status = _cairo_scaled_font_glyph_path (scaled_font,
+					    glyphs, num_glyphs,
+					    &path);
+    if (unlikely (status))
+	goto BAIL;
+
+    status = _vg_surface_fill (abstract_surface,
+			       op, source, &path,
+			       CAIRO_FILL_RULE_WINDING,
+			       CAIRO_GSTATE_TOLERANCE_DEFAULT,
+			       CAIRO_ANTIALIAS_SUBPIXEL,
+			       extents);
+BAIL:
+    _cairo_path_fixed_fini (&path);
+    return status;
+}
+
+static inline int
+multiply_alpha (int alpha, int color)
+{
+    int temp = alpha * color + 0x80;
+    return (temp + (temp >> 8)) >> 8;
+}
+
+static void
+premultiply_argb (uint8_t   *data,
+		  int	     width,
+		  int	     height,
+		  int	     stride)
+{
+    int i;
+
+    while (height --) {
+	uint32_t *row = (uint32_t *) data;
+
+	for (i = 0; i < width; i++) {
+	    uint32_t p = row[i];
+	    uint8_t  alpha;
+
+	    alpha = p >> 24;
+	    if (alpha == 0) {
+		 row[i] = 0;
+	    } else if (alpha != 0xff) {
+		uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff);
+		uint8_t g = multiply_alpha (alpha, (p >>  8) & 0xff);
+		uint8_t b = multiply_alpha (alpha, (p >>  0) & 0xff);
+		row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0);
+	    }
+	}
+
+	data += stride;
+    }
+}
+
+static cairo_int_status_t
+_vg_get_image (cairo_vg_surface_t *surface,
+	       int x, int y,
+	       int width, int height,
+	       cairo_image_surface_t **image_out)
+{
+    cairo_image_surface_t *image;
+    pixman_image_t *pixman_image;
+    pixman_format_code_t pixman_format;
+    cairo_bool_t needs_premultiply;
+
+    pixman_format = _vg_format_to_pixman (surface->format,
+					  &needs_premultiply);
+    if (pixman_format == 0)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    pixman_image = pixman_image_create_bits (pixman_format,
+					     width, height,
+					     NULL, 0);
+    if (unlikely (pixman_image == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    vgFinish ();
+    CHECK_VG_ERRORS();
+
+    vgGetImageSubData (surface->image,
+		       pixman_image_get_data (pixman_image),
+		       pixman_image_get_stride (pixman_image),
+		       surface->format,
+		       x, y, width, height);
+
+    image = (cairo_image_surface_t *)
+	_cairo_image_surface_create_for_pixman_image (pixman_image,
+						      pixman_format);
+    if (unlikely (image->base.status)) {
+	pixman_image_unref (pixman_image);
+	return image->base.status;
+    }
+
+    if (needs_premultiply)
+	premultiply_argb (image->data, width, height, image->stride);
+
+    *image_out = image;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_surface_acquire_source_image (void *abstract_surface,
+				  cairo_image_surface_t **image_out,
+				  void                  **image_extra)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+
+    CHECK_VG_ERRORS();
+    *image_extra = NULL;
+    return _vg_get_image (surface,
+			  0, 0, surface->width, surface->height,
+			  image_out);
+}
+
+static void
+_vg_surface_release_source_image (void                    *abstract_surface,
+				  cairo_image_surface_t   *image,
+				  void                    *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_vg_surface_acquire_dest_image (void                    *abstract_surface,
+				cairo_rectangle_int_t   *interest_rect,
+				cairo_image_surface_t  **image_out,
+				cairo_rectangle_int_t   *image_rect_out,
+				void                   **image_extra)
+{
+    cairo_vg_surface_t *surface =  abstract_surface;
+
+    *image_rect_out = *interest_rect;
+    *image_extra = NULL;
+    return _vg_get_image (surface,
+			  interest_rect->x, interest_rect->y,
+			  interest_rect->width, interest_rect->height,
+			  image_out);
+}
+
+static void
+unpremultiply_argb (uint8_t *data,
+		    int	     width,
+		    int	     height,
+		    int	     stride)
+{
+    int i;
+
+    while (height--) {
+	uint32_t *row = (uint32_t *) data;
+
+	for (i = 0; i < width; i ++) {
+	    uint32_t p = row[i];
+	    uint8_t  alpha;
+
+	    alpha = p >> 24;
+	    if (alpha == 0) {
+		row[i] = 0;
+	    } else if (alpha != 0xff) {
+		uint8_t r = (((p >> 16) & 0xff) * 255 + alpha / 2) / alpha;
+		uint8_t g = (((p >>  8) & 0xff) * 255 + alpha / 2) / alpha;
+		uint8_t b = (((p >>  0) & 0xff) * 255 + alpha / 2) / alpha;
+		row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0);
+	    }
+	}
+
+	data += stride;
+    }
+}
+
+static void
+_vg_surface_release_dest_image (void                    *abstract_surface,
+				cairo_rectangle_int_t   *interest_rect,
+				cairo_image_surface_t   *image,
+				cairo_rectangle_int_t   *image_rect,
+				void                    *image_extra)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_bool_t needs_unpremultiply;
+
+    /* XXX clipping is incorrect
+     * We can either composite through the current clip mask using fill,
+     * or what for the clipping overhaul patches, due to land any time
+     * soon now...
+     */
+
+    _vg_format_to_pixman (surface->format, &needs_unpremultiply);
+    if (needs_unpremultiply) {
+	unpremultiply_argb (image->data,
+			    image->width, image->height,
+			    image->stride);
+    }
+
+    vgImageSubData (surface->image,
+		    image->data, image->stride,
+		    surface->format,
+		    image_rect->x, image_rect->y,
+		    image_rect->width, image_rect->height);
+
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_vg_surface_finish (void *abstract_surface)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_vg_context_t *context = _vg_context_lock (surface->context);
+
+    if (surface->snapshot_cache_entry.hash) {
+	_cairo_cache_remove (&context->snapshot_cache,
+			     &surface->snapshot_cache_entry);
+
+	surface->snapshot_cache_entry.hash = 0;
+    }
+
+    if (surface->own_image)
+	vgDestroyImage (surface->image);
+
+    _vg_context_destroy_target (context, surface);
+
+    _vg_context_unlock (context);
+    _vg_context_destroy (context);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_vg_surface_backend = {
+    CAIRO_SURFACE_TYPE_VG,
+    _vg_surface_create_similar,
+    _vg_surface_finish,
+    _vg_surface_acquire_source_image,
+    _vg_surface_release_source_image,
+    _vg_surface_acquire_dest_image,
+    _vg_surface_release_dest_image,
+    NULL, /* clone_similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    NULL, /* set_clip_region */
+    _vg_surface_intersect_clip_path,
+    _vg_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    _vg_surface_get_font_options, /* get_font_options */
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+    _vg_surface_paint,
+    _vg_surface_mask,
+    _vg_surface_stroke,
+    _vg_surface_fill,
+    _vg_surface_show_glyphs,
+
+    NULL, /* snapshot */
+    NULL, /* is_similar */
+    NULL, /* reset */
+};
+
+static cairo_surface_t *
+_vg_surface_create_internal (cairo_vg_context_t *context,
+			     VGImage image,
+			     VGImageFormat format,
+			     int width, int height)
+{
+    cairo_vg_surface_t *surface;
+
+    surface = malloc (sizeof (cairo_vg_surface_t));
+    if (unlikely (surface == NULL))
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface->context = _vg_context_reference (context);
+
+    surface->image  = image;
+    surface->format = format;
+
+    _cairo_surface_init (&surface->base,
+			 &cairo_vg_surface_backend,
+			 _vg_format_to_content (format));
+
+    surface->width  = width;
+    surface->height = height;
+    surface->clipped = FALSE;
+
+    surface->snapshot_cache_entry.hash = 0;
+
+    surface->target_id = 0;
+
+    /* Force an initial "clip", that resets the mask */
+    _vg_surface_intersect_clip_path (surface, NULL, 0, 0.0, 0);
+
+    CHECK_VG_ERRORS();
+    return &surface->base;
+}
+
+cairo_surface_t *
+cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
+				   VGImage image,
+				   VGImageFormat format,
+				   int width, int height)
+{
+    cairo_bool_t premult;
+
+    if (context->status)
+	return _cairo_surface_create_in_error (context->status);
+
+    if (image == VG_INVALID_HANDLE)
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    if (_vg_format_to_pixman (format, &premult) == 0)
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    return _vg_surface_create_internal (context, image, format, width, height);
+}
+
+cairo_surface_t *
+cairo_vg_surface_create (cairo_vg_context_t *context,
+			 cairo_content_t  content,
+			 int              width,
+			 int              height)
+{
+    VGImage image;
+    VGImageFormat format;
+    cairo_surface_t *surface;
+
+    if (context->status)
+	return _cairo_surface_create_in_error (context->status);
+
+    if (! CAIRO_CONTENT_VALID (content))
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+    if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+	height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+    {
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    }
+
+
+    format = _vg_format_for_content (content);
+    image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER);
+    if (image == VG_INVALID_HANDLE)
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface = _vg_surface_create_internal (context,
+					   image, format, width, height);
+    if (unlikely (surface->status))
+	return surface;
+
+    ((cairo_vg_surface_t *) surface)->own_image = TRUE;
+    return surface;
+}
+slim_hidden_def (cairo_vg_surface_create);
+
+VGImage
+cairo_vg_surface_get_image (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface;
+
+    if (abstract_surface->backend != &cairo_vg_surface_backend) {
+	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+	return VG_INVALID_HANDLE;
+    }
+
+    surface = (cairo_vg_surface_t *) abstract_surface;
+    return surface->image;
+}
+
+int
+cairo_vg_surface_get_width (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface;
+
+    if (abstract_surface->backend != &cairo_vg_surface_backend) {
+	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+	return 0;
+    }
+
+    surface = (cairo_vg_surface_t *) abstract_surface;
+    return surface->width;
+}
+
+int
+cairo_vg_surface_get_height (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface;
+
+    if (abstract_surface->backend != &cairo_vg_surface_backend) {
+	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+	return 0;
+    }
+
+    surface = (cairo_vg_surface_t *) abstract_surface;
+    return surface->height;
+}
+
+VGImageFormat
+cairo_vg_surface_get_format (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface;
+
+    if (abstract_surface->backend != &cairo_vg_surface_backend) {
+	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+	return 0;
+    }
+
+    surface = (cairo_vg_surface_t *) abstract_surface;
+    return surface->format;
+}
+
+/* GL specific context support :-(
+ *
+ * OpenVG like cairo defers creation of surface (and the necessary
+ * paraphernalia to the application.
+ */
+
+static const cairo_vg_context_t _vg_context_nil = {
+    CAIRO_STATUS_NO_MEMORY,
+    CAIRO_REFERENCE_COUNT_INVALID
+};
+
+static const cairo_vg_context_t _vg_context_nil_invalid_visual = {
+    CAIRO_STATUS_INVALID_VISUAL,
+    CAIRO_REFERENCE_COUNT_INVALID
+};
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+#include <GL/glx.h>
+
+static cairo_status_t
+glx_create_target (cairo_vg_context_t *context,
+		   cairo_vg_surface_t *surface)
+{
+    /* XXX hmm, magic required for creating an FBO points to VGImage! */
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+glx_set_target (cairo_vg_context_t *context,
+		cairo_vg_surface_t *surface)
+{
+#if 0
+    glXMakeContextCurrent (context->display,
+			   (GLXDrawable) surface->target_id,
+			   (GLXDrawable) surface->target_id,
+			   context->context);
+#else
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+}
+
+static void
+glx_destroy_target (cairo_vg_context_t *context,
+		    cairo_vg_surface_t *surface)
+{
+}
+
+cairo_vg_context_t *
+cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx)
+{
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+
+    context = malloc (sizeof (*context));
+    if (unlikely (context == NULL))
+	return (cairo_vg_context_t *) &_vg_context_nil;
+
+    context->display = dpy;
+    context->context = ctx;
+
+    context->create_target  = glx_create_target;
+    context->set_target     = glx_set_target;
+    context->destroy_target = glx_destroy_target;
+
+    status = _vg_context_init (context);
+    if (unlikely (status)) {
+	free (context);
+	return (cairo_vg_context_t *) &_vg_context_nil;
+    }
+
+    return context;
+}
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+static cairo_status_t
+egl_create_target (cairo_vg_context_t *context,
+		   cairo_vg_surface_t *surface)
+{
+    EGLSurface *egl_surface;
+#define RED 1
+#define GREEN 3
+#define BLUE 5
+#define ALPHA 7
+    int attribs[] = {
+	EGL_RED_SIZE, 0,
+	EGL_GREEN_SIZE, 0,
+	EGL_BLUE_SIZE, 0,
+	EGL_ALPHA_SIZE, 0,
+	EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+	EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+	EGL_NONE
+    };
+    pixman_format_code_t pixman_format;
+    EGLConfig config;
+    int num_configs = 0;
+    cairo_bool_t needs_premultiply;
+
+    pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply);
+    if (pixman_format == 0)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* XXX no control over pixel ordering! */
+    attribs[RED]   = PIXMAN_FORMAT_R (pixman_format);
+    attribs[GREEN] = PIXMAN_FORMAT_G (pixman_format);
+    attribs[BLUE]  = PIXMAN_FORMAT_B (pixman_format);
+    attribs[ALPHA] = PIXMAN_FORMAT_A (pixman_format);
+
+    if (! eglChooseConfig (context->display,
+			   attribs,
+			   &config, 1, &num_configs) ||
+	num_configs != 1)
+    {
+	fprintf(stderr, "Error: eglChooseConfig() failed.\n");
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    egl_surface =
+	eglCreatePbufferFromClientBuffer (context->display,
+					  EGL_OPENVG_IMAGE,
+					  (EGLClientBuffer) surface->image,
+					  config,
+					  NULL);
+    surface->target_id = (unsigned long) egl_surface;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+egl_set_target (cairo_vg_context_t *context,
+		cairo_vg_surface_t *surface)
+{
+    if (! eglMakeCurrent (context->display,
+			  (EGLSurface *) surface->target_id,
+			  (EGLSurface *) surface->target_id,
+			  context->context))
+    {
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+egl_destroy_target (cairo_vg_context_t *context,
+		    cairo_vg_surface_t *surface)
+{
+    eglDestroySurface (context->display,
+		       (EGLSurface *) surface->target_id);
+}
+
+cairo_vg_context_t *
+cairo_vg_context_create_for_egl (EGLDisplay egl_display,
+				 EGLContext egl_context)
+{
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+
+    context = malloc (sizeof (*context));
+    if (unlikely (context == NULL))
+	return (cairo_vg_context_t *) &_vg_context_nil;
+
+    status = _vg_context_init (context);
+    if (unlikely (status)) {
+	free (context);
+	return (cairo_vg_context_t *) &_vg_context_nil;
+    }
+
+    context->display = egl_display;
+    context->context = egl_context;
+
+    context->create_target  = egl_create_target;
+    context->set_target     = egl_set_target;
+    context->destroy_target = egl_destroy_target;
+
+    return context;
+}
+#endif
+
+cairo_status_t
+cairo_vg_context_status (cairo_vg_context_t *context)
+{
+    return context->status;
+}
+
+void
+cairo_vg_context_destroy (cairo_vg_context_t *context)
+{
+    if (context == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count))
+	return;
+
+    _vg_context_destroy (context);
+}
diff --git a/src/cairo-vg.h b/src/cairo-vg.h
new file mode 100644
index 0000000..98392fc
--- /dev/null
+++ b/src/cairo-vg.h
@@ -0,0 +1,103 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 * Mozilla Corporation
+ * Copyright © 2009 Chris Wilson
+ *
+ * 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 Mozilla Corporation.
+ *
+ * Contributor(s):
+ *      Vladimir Vukicevic <vladimir at mozilla.com>
+ *      Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_VG_H
+#define CAIRO_VG_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_VG_SURFACE
+
+#include <VG/openvg.h>
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_vg_context cairo_vg_context_t;
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+typedef struct __GLXcontextRec *GLXContext;
+typedef struct _XDisplay Display;
+
+cairo_public cairo_vg_context_t *
+cairo_vg_context_create_for_glx (Display *dpy,
+				 GLXContext ctx);
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+#include <EGL/egl.h>
+
+cairo_public cairo_vg_context_t *
+cairo_vg_context_create_for_egl (EGLDisplay egl_display,
+				 EGLContext egl_context);
+#endif
+
+cairo_public cairo_status_t
+cairo_vg_context_status (cairo_vg_context_t *context);
+
+cairo_public void
+cairo_vg_context_destroy (cairo_vg_context_t *context);
+
+cairo_public cairo_surface_t *
+cairo_vg_surface_create (cairo_vg_context_t *context,
+			 cairo_content_t content, int width, int height);
+
+cairo_public cairo_surface_t *
+cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
+				   VGImage image,
+				   VGImageFormat format,
+				   int width, int height);
+
+cairo_public VGImage
+cairo_vg_surface_get_image (cairo_surface_t *abstract_surface);
+
+cairo_public VGImageFormat
+cairo_vg_surface_get_format (cairo_surface_t *abstract_surface);
+
+cairo_public int
+cairo_vg_surface_get_height (cairo_surface_t *abstract_surface);
+
+cairo_public int
+cairo_vg_surface_get_width (cairo_surface_t *abstract_surface);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_VG_SURFACE*/
+# error Cairo was not compiled with support for the OpenVG backend
+#endif /* CAIRO_HAS_VG_SURFACE*/
+
+#endif /* CAIRO_VG_H */
diff --git a/src/cairo.h b/src/cairo.h
index 8cfcf5f..48ecc37 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1942,6 +1942,7 @@ cairo_surface_status (cairo_surface_t *surface);
  * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10
  * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10
  * @CAIRO_SURFACE_TYPE_META: The surface is a meta-type, since 1.10
+ * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10
  *
  * #cairo_surface_type_t is used to describe the type of a given
  * surface. The surface types are also known as "backends" or "surface
@@ -1983,7 +1984,8 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
     CAIRO_SURFACE_TYPE_SCRIPT,
     CAIRO_SURFACE_TYPE_QT,
-    CAIRO_SURFACE_TYPE_META
+    CAIRO_SURFACE_TYPE_META,
+    CAIRO_SURFACE_TYPE_VG
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t
diff --git a/test/cairo-test.c b/test/cairo-test.c
index 26e7a15..b5d098d 100644
--- a/test/cairo-test.c
+++ b/test/cairo-test.c
@@ -855,7 +855,8 @@ REPEAT:
 
     if (cairo_surface_status (surface)) {
 	MF (MEMFAULT_PRINT_FAULTS ());
-	cairo_test_log (ctx, "Error: Created an error surface\n");
+	cairo_test_log (ctx, "Error: Created an error surface: %s\n",
+			cairo_status_to_string (cairo_surface_status (surface)));
 	ret = CAIRO_TEST_FAILURE;
 	goto UNWIND_STRINGS;
     }
diff --git a/test/get-clip.c b/test/get-clip.c
index c7414b4..5232a0e 100644
--- a/test/get-clip.c
+++ b/test/get-clip.c
@@ -141,6 +141,7 @@ draw (cairo_t *cr, int width, int height)
     case CAIRO_SURFACE_TYPE_SCRIPT:
     case CAIRO_SURFACE_TYPE_QT:
     case CAIRO_SURFACE_TYPE_META:
+    case CAIRO_SURFACE_TYPE_VG:
     default:
         uses_clip_rects = FALSE;
         break;
diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index 26e0d02..3af1fff 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -4357,3 +4357,73 @@ cairo_meta_surface_replay (cairo_surface_t *meta, cairo_surface_t *target)
 
     return ret;
 }
+
+#if CAIRO_HAS_VG_SURFACE
+#include <cairo-vg.h>
+cairo_surface_t *
+cairo_vg_surface_create (cairo_vg_context_t *context,
+			 cairo_content_t content,
+			 int width, int height)
+{
+    cairo_surface_t *ret;
+    long surface_id;
+
+    ret = DLCALL (cairo_vg_surface_create, context, content, width, height);
+    surface_id = _create_surface_id (ret);
+
+    _emit_line_info ();
+    if (_write_lock ()) {
+	_trace_printf ("dict\n"
+		       "  /type /vg set\n"
+		       "  /content //%s set\n"
+		       "  /width %d set\n"
+		       "  /height %d set\n"
+		       "  surface dup /s%ld exch def\n",
+		       _content_to_string (content),
+		       width, height,
+		       surface_id);
+	_surface_object_set_size (ret, width, height);
+	_get_object (SURFACE, ret)->defined = true;
+	_push_operand (SURFACE, ret);
+	_write_unlock ();
+    }
+
+    return ret;
+}
+
+cairo_surface_t *
+cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
+				   VGImage image,
+				   VGImageFormat format,
+				   int width, int height)
+{
+    cairo_surface_t *ret;
+    long surface_id;
+
+    ret = DLCALL (cairo_vg_surface_create_for_image,
+		  context, image, format, width, height);
+    surface_id = _create_surface_id (ret);
+
+    _emit_line_info ();
+    if (_write_lock ()) {
+	cairo_content_t content;
+
+	content = DLCALL (cairo_surface_get_content, ret);
+	_trace_printf ("dict\n"
+		       "  /type /vg set\n"
+		       "  /content //%s set\n"
+		       "  /width %d set\n"
+		       "  /height %d set\n"
+		       "  surface dup /s%ld exch def\n",
+		       _content_to_string (content),
+		       width, height,
+		       surface_id);
+	_surface_object_set_size (ret, width, height);
+	_get_object (SURFACE, ret)->defined = true;
+	_push_operand (SURFACE, ret);
+	_write_unlock ();
+    }
+
+    return ret;
+}
+#endif


More information about the cairo-commit mailing list