[cairo] [PATCH 39/39] [OpenVG] Added egl support to cairo-openvg

tardyp at gmail.com tardyp at gmail.com
Fri Jul 10 10:02:41 PDT 2009


From: Pierre Tardy <tardyp at gmail.com>

This patch adds openvg acceleration for offscreen rendering using egl.
Use cairo_openvg_image_surface_create to create a image like surface
The application has nothing else to do than using cairo_openvg_image_surface_create
instead of cairo_image_surface_create to get openvg acceleration.
---
 src/cairo-openvg-surface.c |  293 +++++++++++++++++++++++++++++++++++++++++---
 src/cairo-openvg.h         |    6 +
 2 files changed, 283 insertions(+), 16 deletions(-)

diff --git a/src/cairo-openvg-surface.c b/src/cairo-openvg-surface.c
index fe18225..b14e0e2 100644
--- a/src/cairo-openvg-surface.c
+++ b/src/cairo-openvg-surface.c
@@ -27,6 +27,7 @@
  * the specific language governing rights and limitations.
  *
  * Contributor(s):
+ *      Pierre Tardy      <tardyp at gmail.com> egl pbuffers support 
  *      Øyvind Kolås      <pippin at gimp.org>
  *      Vladimi Vukicevic <vladimir at mozilla.com> (stubbed out base backend)
  */
@@ -39,6 +40,15 @@
 
 #define MAX_OPACITIES 32
 
+//#define OPENVG_DEBUG
+#define EGL_SUPPORT
+
+// we make this a define, because egl is not mandatory for every openvg implementation.
+// FIXME: this option should be autodetected in the build plumbing
+#ifdef EGL_SUPPORT
+#include <egl/egl.h>
+#endif
+
 typedef struct cached_image_t {
     VGImage  vg_image;        /*< set to 0 when entry not used */
     void    *cairo_image;
@@ -61,8 +71,38 @@ typedef struct cairo_openvg_surface {
     int             opacity_level;
 
     cached_image_t  images[MAX_IMAGES];
+    
+#ifdef EGL_SUPPORT
+    VGImage	    targetImage;
+    EGLSurface	    eglImageSurface;
+    VGImageFormat   vgformat;
+
+#endif
 } cairo_openvg_surface_t;
 
+#ifdef EGL_SUPPORT
+static EGLContext	eglContext	= 0;
+static EGLDisplay	eglDisplay	= 0;
+static EGLConfig	eglConfig	= 0;
+/* OpenVg needs a valid context on a valid surface for VgImage operations */
+static EGLSurface	eglDummySurface = 0;
+#endif
+
+#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 void
 _cairo_openvg_cairo_matrix_to_openvg (const cairo_matrix_t *src,
                                       VGfloat              *dst);
@@ -114,6 +154,7 @@ _cairo_openvg_surface_intersect_clip_path (void               *asurface,
     {
       vgMask (VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, vgsurface->width, vgsurface->height);
       vgSeti (VG_MASKING, VG_FALSE);
+      CHECK_VG_ERRORS();
       return CAIRO_STATUS_SUCCESS;
     }
 
@@ -166,6 +207,7 @@ _cairo_openvg_surface_intersect_clip_path (void               *asurface,
   cairo_destroy (cr);
   
   cairo_surface_destroy (image);
+  CHECK_VG_ERRORS();
   return status;
 }
 
@@ -173,10 +215,11 @@ static cairo_int_status_t
 _cairo_openvg_surface_get_extents (void                  *asurface,
                                    cairo_rectangle_int_t *extents)
 {
+  cairo_openvg_surface_t *vgsurface = asurface;
     extents->x = 0;
     extents->y = 0;
-    extents->width = 0;
-    extents->height = 0;
+    extents->width = vgsurface->width;
+    extents->height = vgsurface->height;
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -215,6 +258,7 @@ _cairo_path_to_openvg_path_move_to (void          *closure,
       stroke->dcount = 0;
     }
 
+  CHECK_VG_ERRORS();
   return CAIRO_STATUS_SUCCESS;
 }
 
@@ -240,6 +284,7 @@ _cairo_path_to_openvg_path_line_to (void          *closure,
       stroke->dcount = 0;
     }
 
+  CHECK_VG_ERRORS();
   return CAIRO_STATUS_SUCCESS;
 }
 
@@ -278,6 +323,7 @@ _cairo_path_to_openvg_path_curve_to (void          *closure,
       stroke->scount = 0;
       stroke->dcount = 0;
     }
+  CHECK_VG_ERRORS();
   return CAIRO_STATUS_SUCCESS;
 }
 
@@ -293,6 +339,7 @@ _cairo_path_to_openvg_path_close_path (void *closure)
       stroke->scount = 0;
       stroke->dcount = 0;
     }
+  CHECK_VG_ERRORS();
   return CAIRO_STATUS_SUCCESS;
 }
 
@@ -312,6 +359,7 @@ _cairo_openvg_cairo_path_to_openvg_path (cairo_path_fixed_t *path,
                                           _cairo_path_to_openvg_path_close_path,
                                           stroke);
     vgAppendPathData(stroke->path, stroke->scount, stroke->gseg, stroke->gdata);
+    CHECK_VG_ERRORS();
     return status;
 }
 
@@ -347,6 +395,7 @@ _cairo_openvg_cairo_operator_to_openvg (cairo_operator_t op)
           return VG_BLEND_SRC_OVER;
     }
 
+    CHECK_VG_ERRORS();
     return VG_BLEND_SRC_OVER;
 }
 
@@ -371,6 +420,7 @@ _cairo_openvg_cairo_line_join_to_openvg (cairo_line_join_t cjoin)
         case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL; break;
     }
 
+    CHECK_VG_ERRORS();
     return VG_JOIN_MITER;
 }
 
@@ -387,6 +437,7 @@ _cairo_openvg_cairo_matrix_to_openvg (const cairo_matrix_t *src,
     dst[6] = /* tx  */ src->x0;
     dst[7] = /* ty  */ src->y0;
     dst[8] = /* w2  */ 0;
+    CHECK_VG_ERRORS();
 }
 
 static cairo_status_t
@@ -414,6 +465,7 @@ _cairo_openvg_setup_gradient_stops (cairo_openvg_surface_t   *vgsurface,
   vgSetParameterfv (vgsurface->source_paint,
                     VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops);
   free (stops);
+  CHECK_VG_ERRORS();
   return CAIRO_STATUS_SUCCESS;
 }
 
@@ -433,6 +485,7 @@ static void setup_paint_matrix (cairo_pattern_t *pat)
     vgLoadMatrix(vmat);
 
     vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+    CHECK_VG_ERRORS();
 }
 
 static cairo_status_t
@@ -453,6 +506,7 @@ _cairo_openvg_setup_linear_source (cairo_openvg_surface_t *vgsurface,
   vgSetParameterfv (vgsurface->source_paint, VG_PAINT_LINEAR_GRADIENT, 4, linear);
 
   setup_paint_matrix ((cairo_pattern_t*) lpat);
+  CHECK_VG_ERRORS();
   return _cairo_openvg_setup_gradient_stops (vgsurface,
                                (cairo_gradient_pattern_t *) &lpat->base);
 
@@ -481,6 +535,7 @@ _cairo_openvg_setup_radial_source (cairo_openvg_surface_t *vgsurface,
 
   /* FIXME: copy/adapt fixes from SVG backend to add inner radius */
 
+  CHECK_VG_ERRORS();
   return _cairo_openvg_setup_gradient_stops (vgsurface,
                                             (cairo_gradient_pattern_t *) rpat);
 }
@@ -493,9 +548,10 @@ _cairo_openvg_setup_solid_source (cairo_openvg_surface_t *vgsurface,
                      spat->color.green,
                      spat->color.blue,
                      spat->color.alpha * vgsurface->alpha};
-  
+ 
   vgSetParameteri(vgsurface->source_paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
   vgSetParameterfv (vgsurface->source_paint, VG_PAINT_COLOR, 4, color);
+  CHECK_VG_ERRORS();
   return CAIRO_STATUS_SUCCESS;
 }
 
@@ -537,7 +593,7 @@ _cairo_openvg_setup_surface_source (cairo_openvg_surface_t  *vgsurface,
       }
   }
 
-  vgsurface->source_image = vgCreateImage (VG_sRGBA_8888, 
+  vgsurface->source_image = vgCreateImage (VG_sARGB_8888, 
     image->width, image->height, VG_IMAGE_QUALITY_FASTER);
   /* NONALIASED, FASTER, BETTER */
 
@@ -587,7 +643,7 @@ _cairo_openvg_setup_surface_source (cairo_openvg_surface_t  *vgsurface,
   } */
 
   vgImageSubData (vgsurface->source_image, image->data, image->width*4,
-     VG_sARGB_8888, 0, 0, image->width, image->height);
+     VG_sXRGB_8888, 0, 0, image->width, image->height);
 
   /*free (data);*/
 
@@ -629,6 +685,7 @@ USED_CACHE:
 
   setup_paint_matrix ((cairo_pattern_t*) spat);
 
+  CHECK_VG_ERRORS();
   return status;
 }
 
@@ -639,7 +696,7 @@ setup_source (cairo_openvg_surface_t *vgsurface,
   /* this will bomb if one of the prior paint ops have failed to
    * clean up after itself
    */
-  
+
   assert (vgsurface->source_image == 0);
   
   if (source->type == CAIRO_PATTERN_TYPE_SOLID)
@@ -666,6 +723,7 @@ setup_source (cairo_openvg_surface_t *vgsurface,
     {
       printf ("not handling source of type: %i\n", source->type);
     }
+  CHECK_VG_ERRORS();
   return CAIRO_INT_STATUS_UNSUPPORTED;
 }
 
@@ -735,6 +793,7 @@ _cairo_openvg_surface_stroke (void                 *asurface,
 BAIL:
   vgLoadMatrix (state);
 
+  CHECK_VG_ERRORS();
   return rv;
 }
 
@@ -772,10 +831,12 @@ _cairo_openvg_surface_fill (void                  *asurface,
   vgSetPaint(vgsurface->source_paint, VG_FILL_PATH);
   vgDrawPath (vg_path.path, VG_FILL_PATH);
   vgDestroyPath (vg_path.path);
+  CHECK_VG_ERRORS();
 
   teardown_source (vgsurface, source);
 BAIL:
 
+  CHECK_VG_ERRORS();
   return rv;
 }
 
@@ -832,6 +893,7 @@ _cairo_openvg_surface_paint (void             *asurface,
   teardown_source (vgsurface, source);
 BAIL:
 
+  CHECK_VG_ERRORS();
   return rv;
 }
 
@@ -864,6 +926,7 @@ _cairo_openvg_surface_mask (void                   *asurface,
       vgsurface->opacity_level--;
       vgsurface->alpha = vgsurface->opacity[vgsurface->opacity_level];
       assert (vgsurface->opacity_level>=0);
+      CHECK_VG_ERRORS();
       return status;
     }
 
@@ -904,6 +967,14 @@ _cairo_openvg_surface_mask (void                   *asurface,
 
   return status;
 }
+static void
+_cairo_openvg_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);
+}
 
 
 
@@ -935,15 +1006,56 @@ _cairo_openvg_surface_show_glyphs (void                  *asurface,
 
   status = _cairo_openvg_surface_fill (asurface, op, source, &path, CAIRO_FILL_RULE_WINDING, 0.0, CAIRO_ANTIALIAS_SUBPIXEL, extents);
   _cairo_path_fixed_fini (&path);
+  CHECK_VG_ERRORS();
   return status;
 }
+#ifdef EGL_SUPPORT
+static cairo_status_t
+_cairo_openvg_surface_acquire_source_image (void                   *abstract_surface,
+                                         cairo_image_surface_t **image_out,
+                                         void                  **image_extra)
+{
+  cairo_openvg_surface_t *s = (cairo_openvg_surface_t*) abstract_surface;
+  cairo_image_surface_t *i = (cairo_image_surface_t *)
+      cairo_image_surface_create (CAIRO_FORMAT_RGB24, // FIXME RGBA gives bad results.
+				  s->width,      /* Width */
+				  s->height);    /* Height */
+  vgFinish();
+  CHECK_VG_ERRORS();
+  /* we need to change context to the dummy surface in order to make openvg happy to give its pixels */ 
+  eglMakeCurrent(eglDisplay, eglDummySurface, eglDummySurface, eglContext);
+  vgGetImageSubData(s->targetImage,
+               i->data, i->stride,
+               s->vgformat,
+		 0, 0, s->width, s->height);
+  eglMakeCurrent(eglDisplay, s->eglImageSurface, s->eglImageSurface, eglContext);
+  CHECK_VG_ERRORS();
+  *image_out = i;
+  return CAIRO_STATUS_SUCCESS;
 
+}
+static void
+_cairo_openvg_surface_release_source_image (void                  *abstract_surface,
+                                         cairo_image_surface_t *image,
+                                         void                  *image_extra)
+{
+    cairo_surface_finish((cairo_surface_t *)image);
+}
+#endif // EGL_SUPPORT
 static cairo_status_t
 _cairo_openvg_surface_finish (void *abstract_surface)
 {
   cairo_openvg_surface_t *vgsurface = (cairo_openvg_surface_t*) abstract_surface;
   if (vgsurface->source_paint != VG_INVALID_HANDLE)
-    vgDestroyPaint (vgsurface->source_paint);
+      vgDestroyPaint (vgsurface->source_paint);
+
+#ifdef EGL_SUPPORT
+  eglMakeCurrent(eglDisplay, eglDummySurface, eglDummySurface, eglContext);
+  eglDestroySurface(eglDisplay, vgsurface->eglImageSurface);
+  vgDestroyImage(vgsurface->targetImage);
+#endif
+
+  CHECK_VG_ERRORS();
   return CAIRO_STATUS_SUCCESS;
 }
 
@@ -952,8 +1064,13 @@ cairo_openvg_surface_backend = {
     CAIRO_SURFACE_TYPE_OPENVG,
     _cairo_openvg_surface_create_similar,
     _cairo_openvg_surface_finish,
+#ifdef EGL_SUPPORT
+    _cairo_openvg_surface_acquire_source_image,
+    _cairo_openvg_surface_release_source_image,
+#else
     NULL, /* acquire_source_image */
     NULL, /* release_source_image */
+#endif
     NULL, /* acquire_dest_image */
     NULL, /* release_dest_image */
     _cairo_openvg_surface_clone_similar,
@@ -968,7 +1085,7 @@ cairo_openvg_surface_backend = {
     _cairo_openvg_surface_intersect_clip_path,
     _cairo_openvg_surface_get_extents,
     NULL, /* old_show_glyphs */
-    NULL, /* get_font_options */
+    _cairo_openvg_surface_get_font_options, /* get_font_options */
     NULL, /* flush */
     NULL, /* mark_dirty_rectangle */
     NULL, /* scaled_font_fini */
@@ -984,31 +1101,175 @@ cairo_openvg_surface_backend = {
     NULL, /* is_similar */
     NULL, /* reset */
 };
+#ifdef EGL_SUPPORT
+/* FIXME static configuration of the pixel format! */
+static const int ai32ARGB32ConfigAttribs[] =
+{
+    EGL_RED_SIZE,       8,
+    EGL_GREEN_SIZE,     8,
+    EGL_BLUE_SIZE,      8,
+    EGL_ALPHA_SIZE,     8,
+    EGL_SURFACE_TYPE,   EGL_PBUFFER_BIT,
+    EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+    EGL_NONE
+};
+static const int ai32RGB24ConfigAttribs[] =
+{
+    EGL_RED_SIZE,       8,
+    EGL_GREEN_SIZE,     8,
+    EGL_BLUE_SIZE,      8,
+    EGL_ALPHA_SIZE,     8,
+    EGL_VG_ALPHA_FORMAT,EGL_VG_ALPHA_FORMAT_PRE,
+    EGL_SURFACE_TYPE,   EGL_PBUFFER_BIT,
+    EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+    EGL_NONE
+};
+#define ai32ConfigAttribs ai32RGB24ConfigAttribs
+static const int ai32PBufferAttribs[] =
+{
+    EGL_WIDTH,	8, EGL_HEIGHT, 8, /* small non zero aligned stub values (just in case) */
+    EGL_NONE /* other attribs are default ones */
+};
 
-cairo_surface_t *
-cairo_openvg_surface_create (int width, int height)
+static void initEGL(void)
+{
+    int i32NumConfigs, i32MajorVersion, i32MinorVersion;
+    unsigned int		err;
+
+    eglDisplay = (NativeDisplayType)eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+    if(!eglInitialize(eglDisplay, &i32MajorVersion, &i32MinorVersion))
+    {
+	printf("Error: eglInitialize() failed.\n");
+	exit(0);
+    }
+    printf("eglversion %d.%d\n",i32MajorVersion, i32MinorVersion);
+
+    eglBindAPI(EGL_OPENVG_API);
+
+    if(!eglChooseConfig(eglDisplay, ai32ARGB32ConfigAttribs, &eglConfig, 1, &i32NumConfigs) || (i32NumConfigs != 1))
+    {
+	printf("Error: eglChooseConfig() failed.\n");
+	exit(0);
+    }
+
+    eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, NULL);
+    err = eglGetError();
+    if((err != EGL_SUCCESS) || (eglContext == EGL_NO_CONTEXT))
+    {
+	printf("Error: eglCreateContext() failed: 0x%08x.\n", err);
+	exit(0);
+    }
+    /* it seems we cannot create a vgImage without a valid eglContext, so make a dummy one*/ 
+    
+    eglDummySurface = eglCreatePbufferSurface(eglDisplay, eglConfig,ai32PBufferAttribs);
+    err = eglGetError();
+    if( err != EGL_SUCCESS){
+	printf("Error: eglCreatePbufferSurface() failed: 0x%08x. %p\n", eglGetError(),eglDummySurface);
+	exit(1);
+    }
+    eglMakeCurrent(eglDisplay, eglDummySurface, eglDummySurface, eglContext);
+    err = eglGetError();
+    if( err != EGL_SUCCESS){
+	printf("Error: vgMakeCurrent() failed: 0x%08x. %p\n", eglGetError(),eglContext);
+	exit(1);
+    }
+
+}
+#if 0 /* not sure when to call this... */
+void destroyEGL(void)
+{
+	eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+	eglTerminate(eglDisplay);
+}
+#endif // destroyegl not used
+#endif //EGL_SUPPORT
+
+static cairo_surface_t *
+_cairo_openvg_surface_create_internal (int width, int height, cairo_openvg_surface_t *s)
 {
-    cairo_openvg_surface_t *s = malloc(sizeof(cairo_openvg_surface_t));
-    memset(s, 0, sizeof(cairo_openvg_surface_t));
     _cairo_surface_init(&s->base, &cairo_openvg_surface_backend, CAIRO_CONTENT_COLOR_ALPHA);
     s->width = width;
     s->height = height;
-
     s->alpha = 1.0;
     s->opacity_level = 0;
     s->opacity[s->opacity_level] = 1.0;
     s->source_paint = vgCreatePaint();
 
     vgLoadIdentity();
-    /* The default transform of cairo and OpenVG differ, (cairo has the
+    /* pippin: The default transform of cairo and OpenVG differ, (cairo has the
      * y axis pointing downwards while openvg has it pointing upwards,
      * let's swap that around
      */
-    vgTranslate (0.0, height);
-    vgScale (1.0, -1.0);
+    /* FIXME tardyp: dont needed on my openvg implementation ?! */
+    //vgTranslate (0.0, height);
+    //vgScale (1.0, -1.0);
 
     /* Force an initial "clip", that resets the mask */
     _cairo_openvg_surface_intersect_clip_path (s, NULL, 0, 0.0, 0);
 
+    CHECK_VG_ERRORS();
     return (cairo_surface_t *) s;
 }
+
+cairo_surface_t *
+cairo_openvg_image_surface_create (cairo_format_t format,int width, int height)
+{
+#ifdef EGL_SUPPORT
+    cairo_openvg_surface_t *s = calloc(1,sizeof(cairo_openvg_surface_t));
+    static int egl_init=0;
+    int err;
+    if (! egl_init)
+    {
+	initEGL();
+	egl_init = 1;
+    }
+    switch (format){
+    case CAIRO_FORMAT_ARGB32:
+	s->vgformat = VG_sARGB_8888;
+	break;
+    case CAIRO_FORMAT_RGB24:
+	s->vgformat = VG_sARGB_8888;
+	break;
+    case CAIRO_FORMAT_A8:
+    case CAIRO_FORMAT_A1:
+    default:
+	printf("Error: for now cairo-openvg only supports CAIRO_FORMAT_ARGB32 and CAIRO_FORMAT_RGB24\n");
+	return NULL;
+    }
+    s->targetImage = vgCreateImage(s->vgformat,
+				   width, height,
+				   VG_IMAGE_QUALITY_BETTER);
+    if(s->targetImage == VG_INVALID_HANDLE)
+    {
+	printf("Error: vgCreateImage() failed: 0x%08x.\n", vgGetError());
+	return NULL;
+    }
+    eglMakeCurrent(eglDisplay, eglDummySurface, eglDummySurface, eglContext);
+    err = eglGetError();
+    if( err != EGL_SUCCESS){
+	printf("Error: vgMakeCurrent() failed: 0x%08x. %p\n", eglGetError(),eglContext);
+	return NULL;
+    }
+
+    s->eglImageSurface  = eglCreatePbufferFromClientBuffer(eglDisplay, EGL_OPENVG_IMAGE, (EGLClientBuffer)s->targetImage, eglConfig, NULL);
+
+    eglMakeCurrent(eglDisplay, s->eglImageSurface, s->eglImageSurface, eglContext);
+
+    err = eglGetError();
+    if(err != EGL_SUCCESS)
+    {
+	printf("Error: eglCreatePbufferFromClientBuffer() failed: 0x%08x.\n", err);
+	return NULL;
+    }
+    return _cairo_openvg_surface_create_internal(width,height,s);
+#else
+    return NULL;
+#endif
+}
+cairo_surface_t *
+cairo_openvg_surface_create (int width, int height)
+{
+    cairo_openvg_surface_t *s = calloc(1,sizeof(cairo_openvg_surface_t));
+    return _cairo_openvg_surface_create_internal(width,height,s);
+}
diff --git a/src/cairo-openvg.h b/src/cairo-openvg.h
index 24f0844..0c152c5 100644
--- a/src/cairo-openvg.h
+++ b/src/cairo-openvg.h
@@ -42,6 +42,12 @@ CAIRO_BEGIN_DECLS
 cairo_public cairo_surface_t *
 cairo_openvg_surface_create (int width, int height);
 
+/* this API makes a egl pbuffer context and draws to it 
+   this surface can then be used with APIs like cairo_surface_write_to_png()
+*/
+cairo_public cairo_surface_t *
+cairo_openvg_image_surface_create (cairo_format_t format,int width, int height);
+
 CAIRO_END_DECLS
 
 #endif /* CAIRO_OPENVG_H */
-- 
1.6.0.4



More information about the cairo mailing list