Upload Tizen2.0 source
[framework/graphics/cairo.git] / src / cairo-vg-surface.c
1 /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2 /* cairo - a vector graphics library with display and print output
3  *
4  * Copyright © 2008 Opened Hand Ltd.
5  * Copyright © 2009 Chris Wilson
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it either under the terms of the GNU Lesser General Public
9  * License version 2.1 as published by the Free Software Foundation
10  * (the "LGPL") or, at your option, under the terms of the Mozilla
11  * Public License Version 1.1 (the "MPL"). If you do not alter this
12  * notice, a recipient may use your version of this file under either
13  * the MPL or the LGPL.
14  *
15  * You should have received a copy of the LGPL along with this library
16  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
18  * You should have received a copy of the MPL along with this library
19  * in the file COPYING-MPL-1.1
20  *
21  * The contents of this file are subject to the Mozilla Public License
22  * Version 1.1 (the "License"); you may not use this file except in
23  * compliance with the License. You may obtain a copy of the License at
24  * http://www.mozilla.og/MPL/
25  *
26  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28  * the specific language governing rights and limitations.
29  *
30  * Contributor(s):
31  *      Pierre Tardy      <tardyp@gmail.com>
32  *      Øyvind Kolås      <pippin@gimp.org>
33  *      Vladimi Vukicevic <vladimir@mozilla.com> (stubbed out base backend)
34  *      Chris Wilson      <chris@chris-wilson.co.uk>
35  */
36
37 #include "cairoint.h"
38
39 #include "cairo-vg.h"
40
41 #include "cairo-cache-private.h"
42 #include "cairo-default-context-private.h"
43 #include "cairo-error-private.h"
44 #include "cairo-image-surface-private.h"
45 #include "cairo-path-fixed-private.h"
46 #include "cairo-recording-surface-inline.h"
47 #include "cairo-surface-clipper-private.h"
48
49 #include <pixman.h>
50 #include <VG/openvg.h>
51
52 //#define OPENVG_DEBUG
53
54 /*
55  * Work that needs to be done:
56  *  - Glyph cache / proper font support
57  *
58  *  - First-class paths
59  *    Paths are expensive for OpenVG, reuse paths whenever possible.
60  *    So add a path cache, and first class paths!
61  */
62
63 typedef struct _cairo_vg_surface cairo_vg_surface_t;
64
65 /* XXX need GL specific context control. :( */
66 struct _cairo_vg_context {
67     cairo_status_t status;
68     cairo_reference_count_t ref_count;
69
70     unsigned long target_id;
71
72     VGPaint             paint;
73     cairo_vg_surface_t *source;
74     double              alpha;
75
76     cairo_cache_t snapshot_cache;
77
78     void *display;
79     void *context;
80
81     cairo_status_t (*create_target) (cairo_vg_context_t *,
82                                      cairo_vg_surface_t *);
83     cairo_status_t (*set_target) (cairo_vg_context_t *,
84                                   cairo_vg_surface_t *);
85     void (*destroy_target) (cairo_vg_context_t *, cairo_vg_surface_t *);
86 };
87
88 struct _cairo_vg_surface {
89     cairo_surface_t base;
90
91     cairo_vg_context_t *context;
92
93     VGImage         image;
94     VGImageFormat   format;
95     int             width;
96     int             height;
97     cairo_bool_t    own_image;
98
99     cairo_cache_entry_t snapshot_cache_entry;
100
101     cairo_surface_clipper_t clipper;
102
103     unsigned long target_id;
104 };
105
106 static const cairo_surface_backend_t cairo_vg_surface_backend;
107
108 slim_hidden_proto (cairo_vg_surface_create);
109
110 static cairo_surface_t *
111 _vg_surface_create_internal (cairo_vg_context_t *context,
112                              VGImage image,
113                              VGImageFormat format,
114                              int width, int height);
115
116 static cairo_vg_context_t *
117 _vg_context_reference (cairo_vg_context_t *context)
118 {
119     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
120
121     _cairo_reference_count_inc (&context->ref_count);
122
123     return context;
124 }
125
126 static cairo_vg_context_t *
127 _vg_context_lock (cairo_vg_context_t *context)
128 {
129     /* XXX if we need to add locking, then it has to be recursive */
130     return context;
131 }
132
133 static cairo_int_status_t
134 _vg_context_set_target (cairo_vg_context_t *context,
135                         cairo_vg_surface_t *surface)
136 {
137     cairo_status_t status;
138
139     if (surface->target_id == 0) {
140         status = context->create_target (context, surface);
141         if (unlikely (status))
142             return status;
143     }
144
145     if (context->target_id == surface->target_id)
146         return CAIRO_STATUS_SUCCESS;
147
148     context->target_id = surface->target_id;
149
150     return context->set_target (context, surface);
151 }
152
153 static void
154 _vg_context_destroy_target (cairo_vg_context_t *context,
155                             cairo_vg_surface_t *surface)
156 {
157     if (surface->target_id == 0)
158         return;
159
160     if (context->target_id == surface->target_id)
161         context->set_target (context, NULL);
162
163     context->destroy_target (context, surface);
164 }
165
166 static cairo_bool_t
167 _vg_snapshot_cache_can_remove (const void *entry)
168 {
169     return TRUE;
170 }
171
172 static void
173 _vg_snapshot_cache_remove (void *cache_entry)
174 {
175     cairo_vg_surface_t *surface = cairo_container_of (cache_entry,
176                                                       cairo_vg_surface_t,
177                                                       snapshot_cache_entry);
178     surface->snapshot_cache_entry.hash = 0;
179     cairo_surface_destroy (&surface->base);
180 }
181
182 static cairo_status_t
183 _vg_context_init (cairo_vg_context_t *context)
184 {
185     cairo_status_t status;
186
187     context->status = CAIRO_STATUS_SUCCESS;
188     CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1);
189
190     status = _cairo_cache_init (&context->snapshot_cache,
191                                 NULL,
192                                 _vg_snapshot_cache_can_remove,
193                                 _vg_snapshot_cache_remove,
194                                 16*1024*1024);
195     if (unlikely (status))
196         return status;
197
198     context->target_id = 0;
199     context->source = NULL;
200     context->alpha = 1.0;
201
202     context->paint = vgCreatePaint ();
203     vgLoadIdentity ();
204
205     return CAIRO_STATUS_SUCCESS;
206 }
207
208 static void
209 _vg_context_destroy (cairo_vg_context_t *context)
210 {
211     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
212
213     if (! _cairo_reference_count_dec_and_test (&context->ref_count))
214         return;
215
216     if (context->paint != VG_INVALID_HANDLE)
217         vgDestroyPaint (context->paint);
218
219     _cairo_cache_fini (&context->snapshot_cache);
220     free (context);
221 }
222
223 static void
224 _vg_context_unlock (cairo_vg_context_t *context)
225 {
226 }
227
228 #ifdef OPENVG_DEBUG
229 static void check_vg_errors(const char*function,int line)
230 {
231     int err = vgGetError();
232     if (err != VG_NO_ERROR){
233         printf("%s+%d:vgError detected: 0x%08x.\n",function, line,err);
234         assert(err == VG_NO_ERROR);
235     }
236
237 }
238 #define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__)
239 #else
240 #define CHECK_VG_ERRORS() do{}while(0)
241 #endif //OPENVG_DEBUG
242
243 static pixman_format_code_t
244 _vg_format_to_pixman (VGImageFormat format,
245                       cairo_bool_t *needs_premult_fixup)
246 {
247     *needs_premult_fixup = FALSE;
248     switch (format) {
249         /* RGB{A,X} channel ordering */
250     case VG_sRGBX_8888: return PIXMAN_r8g8b8x8;
251     case VG_sRGBA_8888: *needs_premult_fixup = TRUE; return PIXMAN_r8g8b8a8;
252     case VG_sRGBA_8888_PRE: return PIXMAN_r8g8b8a8;
253     case VG_sRGB_565: return PIXMAN_r5g6b5;
254     case VG_sRGBA_5551: return 0;
255     case VG_sRGBA_4444: return 0;
256     case VG_sL_8: return PIXMAN_g8;
257     case VG_lRGBX_8888: return 0;
258     case VG_lRGBA_8888: return 0;
259     case VG_lRGBA_8888_PRE: return 0;
260     case VG_lL_8: return 0;
261     case VG_A_8: return PIXMAN_a8;
262     case VG_BW_1: return PIXMAN_a1;
263     case VG_A_1: return PIXMAN_a1;
264     case VG_A_4: return PIXMAN_a4;
265
266         /* {A,X}RGB channel ordering */
267     case VG_sXRGB_8888: return PIXMAN_x8r8g8b8;
268     case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8;
269     case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8;
270     case VG_sARGB_1555: return 0;
271     case VG_sARGB_4444: return 0;
272     case VG_lXRGB_8888: return 0;
273     case VG_lARGB_8888: return 0;
274     case VG_lARGB_8888_PRE: return 0;
275
276         /* BGR{A,X} channel ordering */
277     case VG_sBGRX_8888: return PIXMAN_b8g8r8x8;
278     case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8;
279     case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8;
280     case VG_sBGR_565: return PIXMAN_b5g6r5;
281     case VG_sBGRA_5551: return 0;
282     case VG_sBGRA_4444: return 0;
283     case VG_lBGRX_8888: return 0;
284     case VG_lBGRA_8888: return 0;
285     case VG_lBGRA_8888_PRE: return 0;
286
287         /* {A,X}BGR channel ordering */
288     case VG_sXBGR_8888: return PIXMAN_x8b8g8r8;
289     case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8;
290     case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8;
291     case VG_sABGR_1555: return 0;
292     case VG_sABGR_4444: return 0;
293     case VG_lXBGR_8888: return 0;
294     case VG_lABGR_8888: return 0;
295     case VG_lABGR_8888_PRE: return 0;
296     default: return 0;
297     }
298 }
299
300 static pixman_format_code_t
301 _vg_format_to_content (VGImageFormat format)
302 {
303     /* XXX could use more simple bit tests */
304     switch (format) {
305         /* RGB{A,X} channel ordering */
306     case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR;
307     case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
308     case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
309     case VG_sRGB_565: return CAIRO_CONTENT_COLOR;
310     case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
311     case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
312     case VG_sL_8: return CAIRO_CONTENT_ALPHA;
313     case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR;
314     case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
315     case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
316     case VG_lL_8: return CAIRO_CONTENT_ALPHA;
317     case VG_A_8: return CAIRO_CONTENT_ALPHA;
318     case VG_A_4: return CAIRO_CONTENT_ALPHA;
319     case VG_A_1: return CAIRO_CONTENT_ALPHA;
320     case VG_BW_1: return CAIRO_CONTENT_ALPHA;
321
322         /* {A,X}RGB channel ordering */
323     case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR;
324     case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
325     case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
326     case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA;
327     case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA;
328     case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR;
329     case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
330     case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
331
332         /* BGR{A,X} channel ordering */
333     case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR;
334     case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
335     case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
336     case VG_sBGR_565: return CAIRO_CONTENT_COLOR;
337     case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
338     case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
339     case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR;
340     case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
341     case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
342
343         /* {A,X}BGR channel ordering */
344     case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR;
345     case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
346     case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
347     case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA;
348     case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA;
349     case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR;
350     case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
351     case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
352     default: return 0;
353     }
354 }
355
356 static VGImageFormat
357 _vg_format_from_pixman (pixman_format_code_t format)
358 {
359     /* XXX _PRE needs fixup */
360     switch ((int) format) {
361     case PIXMAN_r5g6b5: return VG_sRGB_565;
362     case PIXMAN_g8: return VG_sL_8;
363     case PIXMAN_a8: return VG_A_8;
364     case PIXMAN_a1: return VG_BW_1;
365     case PIXMAN_x8r8g8b8: return VG_sXRGB_8888;
366     case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE
367     case PIXMAN_b8g8r8x8: return VG_sBGRX_8888;
368     case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE
369     case PIXMAN_b5g6r5: return VG_sBGR_565;
370     case PIXMAN_x8b8g8r8: return VG_sXBGR_8888;
371     case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE
372     default: return 0;
373     }
374 }
375
376 static VGImageFormat
377 _vg_format_for_content (cairo_content_t content)
378 {
379     switch (content) {
380     case CAIRO_CONTENT_ALPHA: return VG_A_8;
381     case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888;
382     default: ASSERT_NOT_REACHED;
383     case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE
384     }
385 }
386
387 static cairo_surface_t *
388 _vg_surface_create_similar (void            *abstract_surface,
389                             cairo_content_t  content,
390                             int              width,
391                             int              height)
392 {
393     cairo_vg_surface_t *surface = abstract_surface;
394
395     if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
396         height > vgGeti (VG_MAX_IMAGE_HEIGHT))
397     {
398         return NULL;
399     }
400
401     return cairo_vg_surface_create (surface->context, content, width, height);
402 }
403
404 static cairo_status_t
405 _vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
406                                          cairo_path_fixed_t *path,
407                                          cairo_fill_rule_t   fill_rule,
408                                          double              tolerance,
409                                          cairo_antialias_t   antialias)
410 {
411     cairo_vg_surface_t *surface = cairo_container_of (clipper,
412                                                       cairo_vg_surface_t,
413                                                       clipper);
414     cairo_vg_surface_t *mask;
415     cairo_status_t status;
416
417     if (path == NULL) {
418         vgMask (VG_INVALID_HANDLE,
419                 VG_FILL_MASK, 0, 0, surface->width, surface->height);
420         vgSeti (VG_MASKING, VG_FALSE);
421         CHECK_VG_ERRORS();
422         return CAIRO_STATUS_SUCCESS;
423     }
424
425     mask = (cairo_vg_surface_t *)
426         _vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA,
427                                     surface->width, surface->height);
428     if (unlikely (mask == NULL))
429         return CAIRO_INT_STATUS_UNSUPPORTED;
430     if (unlikely (mask->base.status))
431         return mask->base.status;
432
433     status = _cairo_surface_fill (&mask->base,
434                                   CAIRO_OPERATOR_SOURCE,
435                                   &_cairo_pattern_white.base,
436                                   path, fill_rule, tolerance, antialias,
437                                   NULL);
438     if (status) {
439         cairo_surface_destroy (&mask->base);
440         return status;
441     }
442
443     vgSeti (VG_MASKING, VG_TRUE);
444     vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height);
445
446     cairo_surface_destroy (&mask->base);
447
448     CHECK_VG_ERRORS();
449     return CAIRO_STATUS_SUCCESS;
450 }
451
452 static cairo_bool_t
453 _vg_surface_get_extents (void                  *abstract_surface,
454                          cairo_rectangle_int_t *extents)
455 {
456     cairo_vg_surface_t *surface = abstract_surface;
457
458     extents->x = 0;
459     extents->y = 0;
460     extents->width  = surface->width;
461     extents->height = surface->height;
462
463     return TRUE;
464 }
465
466 #define MAX_SEG  16  /* max number of knots to upload in a batch */
467
468 typedef struct _vg_path {
469     VGPath path;
470     const cairo_matrix_t *ctm_inverse;
471
472     VGubyte gseg[MAX_SEG];
473     VGfloat gdata[MAX_SEG*3*2];
474     int dcount;
475     int scount;
476 } vg_path_t;
477
478 static cairo_status_t
479 _vg_move_to (void          *closure,
480              const cairo_point_t *point)
481 {
482     vg_path_t *path = closure;
483     double x = _cairo_fixed_to_double (point->x);
484     double y = _cairo_fixed_to_double (point->y);
485
486     if (path->ctm_inverse)
487         cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
488
489     path->gseg[path->scount++] = VG_MOVE_TO;
490     path->gdata[path->dcount++] = x;
491     path->gdata[path->dcount++] = y;
492
493     if (path->scount >= MAX_SEG-1) {
494         vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
495         path->scount = 0;
496         path->dcount = 0;
497     }
498
499     CHECK_VG_ERRORS();
500     return CAIRO_STATUS_SUCCESS;
501 }
502
503 static cairo_status_t
504 _vg_line_to (void          *closure,
505              const cairo_point_t *point)
506 {
507     vg_path_t *path = closure;
508     double x = _cairo_fixed_to_double (point->x);
509     double y = _cairo_fixed_to_double (point->y);
510
511     if (path->ctm_inverse)
512         cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
513
514     path->gseg[path->scount++] = VG_LINE_TO;
515     path->gdata[path->dcount++] = x;
516     path->gdata[path->dcount++] = y;
517
518     if (path->scount >= MAX_SEG-1) {
519         vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
520         path->scount = 0;
521         path->dcount = 0;
522     }
523
524     CHECK_VG_ERRORS();
525     return CAIRO_STATUS_SUCCESS;
526 }
527
528 static cairo_status_t
529 _vg_curve_to (void          *closure,
530               const cairo_point_t *p0,
531               const cairo_point_t *p1,
532               const cairo_point_t *p2)
533 {
534     vg_path_t *path = closure;
535     double x0 = _cairo_fixed_to_double (p0->x);
536     double y0 = _cairo_fixed_to_double (p0->y);
537     double x1 = _cairo_fixed_to_double (p1->x);
538     double y1 = _cairo_fixed_to_double (p1->y);
539     double x2 = _cairo_fixed_to_double (p2->x);
540     double y2 = _cairo_fixed_to_double (p2->y);
541
542     if (path->ctm_inverse) {
543         cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0);
544         cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1);
545         cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2);
546     }
547
548     path->gseg[path->scount++] = VG_CUBIC_TO;
549     path->gdata[path->dcount++] = x0;
550     path->gdata[path->dcount++] = y0;
551     path->gdata[path->dcount++] = x1;
552     path->gdata[path->dcount++] = y1;
553     path->gdata[path->dcount++] = x2;
554     path->gdata[path->dcount++] = y2;
555
556     if (path->scount >= MAX_SEG-1) {
557         vgAppendPathData(path->path, path->scount, path->gseg, path->gdata);
558         path->scount = 0;
559         path->dcount = 0;
560     }
561
562     CHECK_VG_ERRORS();
563     return CAIRO_STATUS_SUCCESS;
564 }
565
566 static cairo_status_t
567 _vg_close_path (void *closure)
568 {
569     vg_path_t *path = closure;
570
571     path->gseg[path->scount++] = VG_CLOSE_PATH;
572
573     if (path->scount >= MAX_SEG-1) {
574         vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
575         path->scount = 0;
576         path->dcount = 0;
577     }
578
579     CHECK_VG_ERRORS();
580     return CAIRO_STATUS_SUCCESS;
581 }
582
583 static void
584 _vg_path_from_cairo (vg_path_t    *vg_path,
585                      const cairo_path_fixed_t *path)
586 {
587     cairo_status_t status;
588
589     vg_path->scount = 0;
590     vg_path->dcount = 0;
591
592     status = _cairo_path_fixed_interpret (path,
593                                           _vg_move_to,
594                                           _vg_line_to,
595                                           _vg_curve_to,
596                                           _vg_close_path,
597                                           vg_path);
598     assert (status == CAIRO_STATUS_SUCCESS);
599
600     vgAppendPathData (vg_path->path,
601                       vg_path->scount, vg_path->gseg, vg_path->gdata);
602     CHECK_VG_ERRORS();
603 }
604
605 static cairo_bool_t
606 _vg_is_supported_operator (cairo_operator_t op)
607 {
608     switch ((int) op) {
609     case CAIRO_OPERATOR_SOURCE:
610     case CAIRO_OPERATOR_OVER:
611     case CAIRO_OPERATOR_IN:
612     case CAIRO_OPERATOR_DEST_OVER:
613     case CAIRO_OPERATOR_DEST_IN:
614     case CAIRO_OPERATOR_ADD:
615         return TRUE;
616
617     default:
618         return FALSE;
619     }
620 }
621
622 static VGBlendMode
623 _vg_operator (cairo_operator_t op)
624 {
625     switch ((int) op) {
626     case CAIRO_OPERATOR_SOURCE:
627         return VG_BLEND_SRC;
628     case CAIRO_OPERATOR_OVER:
629         return VG_BLEND_SRC_OVER;
630     case CAIRO_OPERATOR_IN:
631         return VG_BLEND_SRC_IN;
632     case CAIRO_OPERATOR_DEST_OVER:
633         return VG_BLEND_DST_OVER;
634     case CAIRO_OPERATOR_DEST_IN:
635         return VG_BLEND_DST_IN;
636     case CAIRO_OPERATOR_ADD:
637         return VG_BLEND_ADDITIVE;
638     default:
639         ASSERT_NOT_REACHED;
640         return VG_BLEND_SRC_OVER;
641     }
642 }
643
644 static VGFillRule
645 _vg_fill_rule_from_cairo (cairo_fill_rule_t rule)
646 {
647     switch (rule) {
648     case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD;
649     case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO;
650     }
651
652     ASSERT_NOT_REACHED;
653     return VG_NON_ZERO;
654 }
655
656 static VGRenderingQuality
657 _vg_rendering_quality_from_cairo (cairo_antialias_t aa)
658 {
659     switch (aa) {
660     case CAIRO_ANTIALIAS_DEFAULT:
661     case CAIRO_ANTIALIAS_SUBPIXEL:
662     case CAIRO_ANTIALIAS_GOOD:
663     case CAIRO_ANTIALIAS_BEST:
664         return VG_RENDERING_QUALITY_BETTER;
665
666     case CAIRO_ANTIALIAS_GRAY:
667     case CAIRO_ANTIALIAS_FAST:
668         return VG_RENDERING_QUALITY_FASTER;
669
670     case CAIRO_ANTIALIAS_NONE:
671         return VG_RENDERING_QUALITY_NONANTIALIASED;
672     }
673
674     ASSERT_NOT_REACHED;
675     return VG_RENDERING_QUALITY_BETTER;
676 }
677
678 static VGCapStyle
679 _vg_line_cap_from_cairo (cairo_line_cap_t cap)
680 {
681     switch (cap) {
682     case CAIRO_LINE_CAP_BUTT:   return VG_CAP_BUTT;
683     case CAIRO_LINE_CAP_ROUND:  return VG_CAP_ROUND;
684     case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE;
685     }
686
687     ASSERT_NOT_REACHED;
688     return VG_CAP_BUTT;
689 }
690
691 static VGJoinStyle
692 _vg_line_join_from_cairo (cairo_line_join_t join)
693 {
694     switch (join) {
695     case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER;
696     case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND;
697     case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL;
698     }
699
700     ASSERT_NOT_REACHED;
701     return VG_JOIN_MITER;
702 }
703
704 static void
705 _vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src)
706 {
707     dst[0] = /* sx  */ src->xx;
708     dst[1] = /* shy */ src->yx;
709     dst[2] = /* w0  */ 0;
710     dst[3] = /* shx */ src->xy;
711     dst[4] = /* sy  */ src->yy;
712     dst[5] = /* w1  */ 0;
713     dst[6] = /* tx  */ src->x0;
714     dst[7] = /* ty  */ src->y0;
715     dst[8] = /* w2  */ 0;
716 }
717
718 static cairo_status_t
719 _vg_setup_gradient_stops (cairo_vg_context_t *context,
720                           const cairo_gradient_pattern_t *pattern)
721 {
722     VGint numstops = pattern->n_stops;
723     VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)];
724     int i;
725
726     if (numstops*5 < ARRAY_LENGTH (stack_stops)) {
727         stops = stack_stops;
728     } else {
729         stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat));
730         if (unlikely (stops == NULL))
731             return _cairo_error (CAIRO_STATUS_NO_MEMORY);
732     }
733
734     for (i = 0; i < numstops; i++) {
735         stops[i*5 + 0] = pattern->stops[i].offset;
736         stops[i*5 + 1] = pattern->stops[i].color.red;
737         stops[i*5 + 2] = pattern->stops[i].color.green;
738         stops[i*5 + 3] = pattern->stops[i].color.blue;
739         stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha;
740     }
741
742     vgSetParameterfv (context->paint,
743                       VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops);
744
745     if (stops != stack_stops)
746         free (stops);
747
748     CHECK_VG_ERRORS();
749     return CAIRO_STATUS_SUCCESS;
750 }
751
752 static void
753 _vg_set_source_matrix (const cairo_pattern_t *pat)
754 {
755     cairo_matrix_t mat;
756     cairo_status_t status;
757     VGfloat vmat[9];
758
759     mat = pat->matrix;
760     status = cairo_matrix_invert (&mat);
761     assert (status == CAIRO_STATUS_SUCCESS);
762
763     _vg_matrix_from_cairo (vmat, &mat);
764
765     vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
766     vgLoadMatrix (vmat);
767     vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER);
768     vgLoadMatrix (vmat);
769     vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
770
771     CHECK_VG_ERRORS();
772 }
773
774 static cairo_status_t
775 _vg_setup_linear_source (cairo_vg_context_t *context,
776                          const cairo_linear_pattern_t *lpat)
777 {
778     VGfloat linear[4];
779
780     linear[0] = lpat->pd1.x;
781     linear[1] = lpat->pd1.y;
782     linear[2] = lpat->pd2.x;
783     linear[3] = lpat->pd2.y;
784
785     vgSetParameteri (context->paint,
786                      VG_PAINT_COLOR_RAMP_SPREAD_MODE,
787                      VG_COLOR_RAMP_SPREAD_PAD);
788     vgSetParameteri (context->paint,
789                      VG_PAINT_TYPE,
790                      VG_PAINT_TYPE_LINEAR_GRADIENT);
791     vgSetParameterfv (context->paint,
792                       VG_PAINT_LINEAR_GRADIENT, 4, linear);
793
794     _vg_set_source_matrix (&lpat->base.base);
795
796     CHECK_VG_ERRORS();
797     return _vg_setup_gradient_stops (context, &lpat->base);
798
799 }
800
801 static cairo_status_t
802 _vg_setup_radial_source (cairo_vg_context_t *context,
803                          const cairo_radial_pattern_t *rpat)
804 {
805     VGfloat radial[5];
806
807     radial[0] = rpat->cd1.center.x;
808     radial[1] = rpat->cd1.center.y;
809     radial[2] = rpat->cd2.center.x;
810     radial[3] = rpat->cd2.center.y;
811     radial[4] = rpat->cd2.radius;
812
813     vgSetParameteri (context->paint,
814                      VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD);
815     vgSetParameteri (context->paint,
816                      VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT);
817     vgSetParameterfv (context->paint,
818                       VG_PAINT_RADIAL_GRADIENT, 5, radial);
819
820     _vg_set_source_matrix (&rpat->base.base);
821
822     /* FIXME: copy/adapt fixes from SVG backend to add inner radius */
823
824     CHECK_VG_ERRORS();
825     return _vg_setup_gradient_stops (context, &rpat->base);
826 }
827
828 static cairo_status_t
829 _vg_setup_solid_source (cairo_vg_context_t *context,
830                         const cairo_solid_pattern_t *spat)
831 {
832     VGfloat color[] = {
833         spat->color.red,
834         spat->color.green,
835         spat->color.blue,
836         spat->color.alpha * context->alpha
837     };
838
839     vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
840     vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color);
841
842     CHECK_VG_ERRORS();
843     return CAIRO_STATUS_SUCCESS;
844 }
845
846 static cairo_vg_surface_t *
847 _vg_clone_recording_surface (cairo_vg_context_t *context,
848                         cairo_surface_t *surface)
849 {
850     VGImage vg_image;
851     VGImageFormat format;
852     cairo_status_t status;
853     cairo_rectangle_int_t extents;
854     cairo_vg_surface_t *clone;
855
856     status = _cairo_surface_get_extents (surface, &extents);
857     if (status)
858         return NULL;
859
860     if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
861         extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
862     {
863         return NULL;
864     }
865
866     format = _vg_format_for_content (surface->content);
867
868     /* NONALIASED, FASTER, BETTER */
869     vg_image = vgCreateImage (format,
870                               extents.width, extents.height,
871                               VG_IMAGE_QUALITY_FASTER);
872     clone = (cairo_vg_surface_t *)
873         _vg_surface_create_internal (context, vg_image, format,
874                                      extents.width, extents.height);
875     cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y);
876
877     status = _cairo_recording_surface_replay (surface, &clone->base);
878     if (unlikely (status)) {
879         cairo_surface_destroy (&clone->base);
880         return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
881     }
882
883     return clone;
884 }
885
886 static cairo_vg_surface_t *
887 _vg_clone_image_surface (cairo_vg_context_t *context,
888                          cairo_surface_t *surface)
889 {
890     cairo_image_surface_t *image;
891     void *image_extra;
892     cairo_status_t status;
893     VGImage vg_image;
894     VGImageFormat format;
895     cairo_rectangle_int_t extents;
896     cairo_vg_surface_t *clone;
897
898     if (surface->backend->acquire_source_image == NULL)
899         return NULL;
900
901     status = _cairo_surface_get_extents (surface, &extents);
902     if (status)
903         return NULL;
904
905     if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
906         extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
907     {
908         return NULL;
909     }
910
911     status = _cairo_surface_acquire_source_image (surface,
912                                                   &image, &image_extra);
913     if (unlikely (status))
914         return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
915
916     format = _vg_format_from_pixman (image->pixman_format);
917     if (format == 0)
918         format = _vg_format_for_content (image->base.content);
919
920     /* NONALIASED, FASTER, BETTER */
921     vg_image = vgCreateImage (format,
922                               image->width, image->height,
923                               VG_IMAGE_QUALITY_FASTER);
924     clone = (cairo_vg_surface_t *)
925         _vg_surface_create_internal (context, vg_image, format,
926                                     image->width, image->height);
927     if (unlikely (clone->base.status))
928         return clone;
929
930     vgImageSubData (clone->image,
931                     image->data, image->stride,
932                     format, 0, 0, image->width, image->height);
933
934     _cairo_surface_release_source_image (surface, image, image_extra);
935
936     return clone;
937 }
938
939 static void
940 _vg_surface_remove_from_cache (cairo_surface_t *abstract_surface)
941 {
942     cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface;
943
944     if (surface->snapshot_cache_entry.hash) {
945         cairo_vg_context_t *context;
946
947         context = _vg_context_lock (surface->context);
948         _cairo_cache_remove (&context->snapshot_cache,
949                              &surface->snapshot_cache_entry);
950         _vg_context_unlock (context);
951
952         surface->snapshot_cache_entry.hash = 0;
953     }
954 }
955
956 static cairo_status_t
957 _vg_setup_surface_source (cairo_vg_context_t *context,
958                           const cairo_surface_pattern_t *spat)
959 {
960     cairo_surface_t *snapshot;
961     cairo_vg_surface_t *clone;
962     cairo_status_t status;
963
964     snapshot = _cairo_surface_has_snapshot (spat->surface,
965                                             &cairo_vg_surface_backend);
966     if (snapshot != NULL) {
967         clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot);
968         goto DONE;
969     }
970
971     if (_cairo_surface_is_recording (spat->surface))
972         clone = _vg_clone_recording_surface (context, spat->surface);
973     else
974         clone = _vg_clone_image_surface (context, spat->surface);
975     if (clone == NULL)
976         return CAIRO_INT_STATUS_UNSUPPORTED;
977     if (unlikely (clone->base.status))
978         return clone->base.status;
979
980     clone->snapshot_cache_entry.hash = clone->base.unique_id;
981     status = _cairo_cache_insert (&context->snapshot_cache,
982                                   &clone->snapshot_cache_entry);
983     if (unlikely (status)) {
984         clone->snapshot_cache_entry.hash = 0;
985         cairo_surface_destroy (&clone->base);
986         return status;
987     }
988
989     _cairo_surface_attach_snapshot (spat->surface, &clone->base,
990                                     _vg_surface_remove_from_cache);
991
992 DONE:
993     cairo_surface_destroy (&context->source->base);
994     context->source = clone;
995
996     vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
997
998     switch (spat->base.extend) {
999     case CAIRO_EXTEND_PAD:
1000         vgSetParameteri (context->paint,
1001                          VG_PAINT_PATTERN_TILING_MODE,
1002                          VG_TILE_PAD);
1003         break;
1004
1005     case CAIRO_EXTEND_NONE:
1006         vgSetParameteri (context->paint,
1007                          VG_PAINT_PATTERN_TILING_MODE,
1008                          VG_TILE_FILL);
1009         {
1010             VGfloat color[] = {0,0,0,0};
1011             vgSetfv (VG_TILE_FILL_COLOR, 4, color);
1012         }
1013         break;
1014
1015     case CAIRO_EXTEND_REPEAT:
1016         vgSetParameteri (context->paint,
1017                          VG_PAINT_PATTERN_TILING_MODE,
1018                          VG_TILE_REPEAT);
1019         break;
1020
1021     default:
1022         ASSERT_NOT_REACHED;
1023     case CAIRO_EXTEND_REFLECT:
1024         vgSetParameteri (context->paint,
1025                          VG_PAINT_PATTERN_TILING_MODE,
1026                          VG_TILE_REFLECT);
1027         break;
1028     }
1029     vgPaintPattern (context->paint, context->source->image);
1030
1031     _vg_set_source_matrix (&spat->base);
1032
1033     CHECK_VG_ERRORS();
1034     return CAIRO_STATUS_SUCCESS;
1035 }
1036
1037 static cairo_status_t
1038 setup_source (cairo_vg_context_t *context,
1039               const cairo_pattern_t *source)
1040 {
1041     switch (source->type) {
1042     case CAIRO_PATTERN_TYPE_SOLID:
1043         return _vg_setup_solid_source (context,
1044                                        (cairo_solid_pattern_t *) source);
1045     case CAIRO_PATTERN_TYPE_LINEAR:
1046         return _vg_setup_linear_source (context,
1047                                         (cairo_linear_pattern_t *) source);
1048     case CAIRO_PATTERN_TYPE_RADIAL:
1049         return _vg_setup_radial_source (context,
1050                                         (cairo_radial_pattern_t *) source);
1051     case CAIRO_PATTERN_TYPE_SURFACE:
1052         return _vg_setup_surface_source (context,
1053                                          (cairo_surface_pattern_t *) source);
1054     default:
1055         ASSERT_NOT_REACHED;
1056         return CAIRO_INT_STATUS_UNSUPPORTED;
1057     }
1058 }
1059
1060 static cairo_int_status_t
1061 _vg_surface_stroke (void                       *abstract_surface,
1062                     cairo_operator_t            op,
1063                     const cairo_pattern_t      *source,
1064                     const cairo_path_fixed_t   *path,
1065                     const cairo_stroke_style_t *style,
1066                     const cairo_matrix_t       *ctm,
1067                     const cairo_matrix_t       *ctm_inverse,
1068                     double                      tolerance,
1069                     cairo_antialias_t           antialias,
1070                     const cairo_clip_t         *clip)
1071 {
1072     cairo_vg_surface_t *surface = abstract_surface;
1073     cairo_vg_context_t *context;
1074     cairo_status_t status;
1075     VGfloat state[9];
1076     VGfloat strokeTransform[9];
1077     vg_path_t vg_path;
1078
1079     if (! _vg_is_supported_operator (op))
1080         return CAIRO_INT_STATUS_UNSUPPORTED;
1081
1082     context = _vg_context_lock (surface->context);
1083     status = _vg_context_set_target (context, surface);
1084     if (status) {
1085         _vg_context_unlock (context);
1086         return status;
1087     }
1088
1089     status = setup_source (context, source);
1090     if (status) {
1091         _vg_context_unlock (context);
1092         return status;
1093     }
1094
1095     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1096     if (unlikely (status)) {
1097         _vg_context_unlock (context);
1098         return status;
1099     }
1100
1101     vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
1102                                  VG_PATH_DATATYPE_F,
1103                                  1, 0, 0, 0,
1104                                  VG_PATH_CAPABILITY_ALL);
1105
1106     vgGetMatrix (state);
1107     _vg_matrix_from_cairo (strokeTransform, ctm);
1108     vgMultMatrix (strokeTransform);
1109
1110     vg_path.ctm_inverse = ctm_inverse;
1111
1112     _vg_path_from_cairo (&vg_path, path);
1113
1114     /* XXX DASH_PATTERN, DASH_PHASE */
1115     vgSetf (VG_STROKE_LINE_WIDTH, style->line_width);
1116     vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit);
1117     vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join));
1118     vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap));
1119
1120     vgSeti (VG_BLEND_MODE, _vg_operator (op));
1121
1122     vgSetPaint (context->paint, VG_STROKE_PATH);
1123
1124     vgDrawPath (vg_path.path, VG_STROKE_PATH);
1125
1126     vgDestroyPath (vg_path.path);
1127
1128     vgLoadMatrix (state);
1129
1130     CHECK_VG_ERRORS();
1131     _vg_context_unlock (context);
1132
1133     return CAIRO_STATUS_SUCCESS;
1134 }
1135
1136 static cairo_int_status_t
1137 _vg_surface_fill (void                     *abstract_surface,
1138                   cairo_operator_t          op,
1139                   const cairo_pattern_t    *source,
1140                   const cairo_path_fixed_t *path,
1141                   cairo_fill_rule_t         fill_rule,
1142                   double                    tolerance,
1143                   cairo_antialias_t         antialias,
1144                   const cairo_clip_t       *clip)
1145 {
1146     cairo_vg_surface_t *surface = abstract_surface;
1147     cairo_vg_context_t *context;
1148     cairo_status_t status;
1149     vg_path_t vg_path;
1150
1151     if (op == CAIRO_OPERATOR_DEST)
1152         return CAIRO_STATUS_SUCCESS;
1153
1154     if (! _vg_is_supported_operator (op))
1155         return CAIRO_INT_STATUS_UNSUPPORTED;
1156
1157     context = _vg_context_lock (surface->context);
1158     status = _vg_context_set_target (context, surface);
1159     if (status) {
1160         _vg_context_unlock (context);
1161         return status;
1162     }
1163
1164     status = setup_source (context, source);
1165     if (status) {
1166         _vg_context_unlock (context);
1167         return status;
1168     }
1169
1170     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1171     if (unlikely (status)) {
1172         _vg_context_unlock (context);
1173         return status;
1174     }
1175
1176     vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
1177                                  VG_PATH_DATATYPE_F,
1178                                  1, 0,
1179                                  0, 0,
1180                                  VG_PATH_CAPABILITY_ALL);
1181     vg_path.ctm_inverse = NULL;
1182
1183     _vg_path_from_cairo (&vg_path, path);
1184
1185     /* XXX tolerance */
1186
1187     vgSeti (VG_BLEND_MODE, _vg_operator (op));
1188     vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule));
1189     vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias));
1190
1191     vgSetPaint (context->paint, VG_FILL_PATH);
1192
1193     vgDrawPath (vg_path.path, VG_FILL_PATH);
1194
1195     vgDestroyPath (vg_path.path);
1196
1197     _vg_context_unlock (context);
1198
1199     CHECK_VG_ERRORS();
1200     return CAIRO_STATUS_SUCCESS;
1201 }
1202
1203 static cairo_int_status_t
1204 _vg_surface_paint (void                  *abstract_surface,
1205                    cairo_operator_t       op,
1206                    const cairo_pattern_t *source,
1207                    const cairo_clip_t    *clip)
1208 {
1209     cairo_vg_surface_t *surface = abstract_surface;
1210     cairo_vg_context_t *context;
1211     cairo_status_t status;
1212
1213     if (op == CAIRO_OPERATOR_DEST)
1214         return CAIRO_STATUS_SUCCESS;
1215
1216     if (! _vg_is_supported_operator (op))
1217         return CAIRO_INT_STATUS_UNSUPPORTED;
1218
1219     context = _vg_context_lock (surface->context);
1220     status = _vg_context_set_target (context, surface);
1221     if (status) {
1222         _vg_context_unlock (context);
1223         return status;
1224     }
1225
1226     status = setup_source (context, source);
1227     if (status) {
1228         _vg_context_unlock (context);
1229         return status;
1230     }
1231
1232     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1233     if (unlikely (status)) {
1234         _vg_context_unlock (context);
1235         return status;
1236     }
1237
1238     vgSeti (VG_BLEND_MODE, _vg_operator (op));
1239     vgSetPaint (context->paint, VG_FILL_PATH);
1240
1241     { /* creating a rectangular path that should cover the extent */
1242         VGubyte segs[] = {
1243             VG_MOVE_TO_ABS, VG_LINE_TO_ABS,
1244             VG_LINE_TO_ABS, VG_LINE_TO_ABS,
1245             VG_CLOSE_PATH
1246         };
1247         VGfloat data[] = {
1248             0, 0,
1249             surface->width, 0,
1250             surface->width, surface->height,
1251             0, surface->height
1252         };
1253         VGPath fullext;
1254
1255         fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
1256                                 1,0,0,0, VG_PATH_CAPABILITY_ALL);
1257         vgAppendPathData (fullext, sizeof(segs), segs, data);
1258
1259         vgDrawPath (fullext, VG_FILL_PATH);
1260
1261         vgDestroyPath (fullext);
1262     }
1263
1264     _vg_context_unlock (context);
1265
1266     CHECK_VG_ERRORS();
1267     return CAIRO_STATUS_SUCCESS;
1268 }
1269
1270 static cairo_int_status_t
1271 _vg_surface_mask (void                   *abstract_surface,
1272                   cairo_operator_t        op,
1273                   const cairo_pattern_t  *source,
1274                   const cairo_pattern_t  *mask,
1275                   const cairo_clip_t     *clip)
1276 {
1277     cairo_vg_surface_t *surface = abstract_surface;
1278     cairo_status_t status;
1279
1280     if (! _vg_is_supported_operator (op))
1281         return CAIRO_INT_STATUS_UNSUPPORTED;
1282
1283     /* Handle paint-with-alpha to do fades cheaply */
1284     if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
1285         cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask;
1286         cairo_vg_context_t *context = _vg_context_lock (surface->context);
1287         double alpha = context->alpha;
1288
1289         context->alpha = solid->color.alpha;
1290         status = _vg_surface_paint (abstract_surface, op, source, clip);
1291         context->alpha = alpha;
1292
1293         _vg_context_unlock (context);
1294
1295         return status;
1296     }
1297
1298     return CAIRO_INT_STATUS_UNSUPPORTED;
1299 }
1300
1301 static void
1302 _vg_surface_get_font_options (void                  *abstract_surface,
1303                               cairo_font_options_t  *options)
1304 {
1305     _cairo_font_options_init_default (options);
1306
1307     cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
1308     _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
1309 }
1310
1311 static cairo_int_status_t
1312 _vg_surface_show_glyphs (void                   *abstract_surface,
1313                          cairo_operator_t        op,
1314                          const cairo_pattern_t  *source,
1315                          cairo_glyph_t          *glyphs,
1316                          int                     num_glyphs,
1317                          cairo_scaled_font_t    *scaled_font,
1318                          const cairo_clip_t     *clip)
1319 {
1320     cairo_status_t status = CAIRO_STATUS_SUCCESS;
1321     cairo_path_fixed_t path;
1322
1323     if (num_glyphs <= 0)
1324         return CAIRO_STATUS_SUCCESS;
1325
1326     _cairo_path_fixed_init (&path);
1327
1328     /* XXX Glyph cache! OpenVG font support in 1.1? */
1329
1330     status = _cairo_scaled_font_glyph_path (scaled_font,
1331                                             glyphs, num_glyphs,
1332                                             &path);
1333     if (unlikely (status))
1334         goto BAIL;
1335
1336     status = _vg_surface_fill (abstract_surface,
1337                                op, source, &path,
1338                                CAIRO_FILL_RULE_WINDING,
1339                                CAIRO_GSTATE_TOLERANCE_DEFAULT,
1340                                CAIRO_ANTIALIAS_DEFAULT,
1341                                clip);
1342 BAIL:
1343     _cairo_path_fixed_fini (&path);
1344     return status;
1345 }
1346
1347 static inline int
1348 multiply_alpha (int alpha, int color)
1349 {
1350     int temp = alpha * color + 0x80;
1351     return (temp + (temp >> 8)) >> 8;
1352 }
1353
1354 static void
1355 premultiply_argb (uint8_t   *data,
1356                   int        width,
1357                   int        height,
1358                   int        stride)
1359 {
1360     int i;
1361
1362     while (height --) {
1363         uint32_t *row = (uint32_t *) data;
1364
1365         for (i = 0; i < width; i++) {
1366             uint32_t p = row[i];
1367             uint8_t  alpha;
1368
1369             alpha = p >> 24;
1370             if (alpha == 0) {
1371                  row[i] = 0;
1372             } else if (alpha != 0xff) {
1373                 uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff);
1374                 uint8_t g = multiply_alpha (alpha, (p >>  8) & 0xff);
1375                 uint8_t b = multiply_alpha (alpha, (p >>  0) & 0xff);
1376                 row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0);
1377             }
1378         }
1379
1380         data += stride;
1381     }
1382 }
1383
1384 static cairo_int_status_t
1385 _vg_get_image (cairo_vg_surface_t *surface,
1386                int x, int y,
1387                int width, int height,
1388                cairo_image_surface_t **image_out)
1389 {
1390     cairo_image_surface_t *image;
1391     pixman_image_t *pixman_image;
1392     pixman_format_code_t pixman_format;
1393     cairo_bool_t needs_premultiply;
1394
1395     pixman_format = _vg_format_to_pixman (surface->format,
1396                                           &needs_premultiply);
1397     if (pixman_format == 0)
1398         return CAIRO_INT_STATUS_UNSUPPORTED;
1399
1400     pixman_image = pixman_image_create_bits (pixman_format,
1401                                              width, height,
1402                                              NULL, 0);
1403     if (unlikely (pixman_image == NULL))
1404         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1405
1406     vgFinish ();
1407     CHECK_VG_ERRORS();
1408
1409     vgGetImageSubData (surface->image,
1410                        pixman_image_get_data (pixman_image),
1411                        pixman_image_get_stride (pixman_image),
1412                        surface->format,
1413                        x, y, width, height);
1414
1415     image = (cairo_image_surface_t *)
1416         _cairo_image_surface_create_for_pixman_image (pixman_image,
1417                                                       pixman_format);
1418     if (unlikely (image->base.status)) {
1419         pixman_image_unref (pixman_image);
1420         return image->base.status;
1421     }
1422
1423     if (needs_premultiply)
1424         premultiply_argb (image->data, width, height, image->stride);
1425
1426     *image_out = image;
1427     return CAIRO_STATUS_SUCCESS;
1428 }
1429
1430 static cairo_status_t
1431 _vg_surface_acquire_source_image (void *abstract_surface,
1432                                   cairo_image_surface_t **image_out,
1433                                   void                  **image_extra)
1434 {
1435     cairo_vg_surface_t *surface = abstract_surface;
1436
1437     CHECK_VG_ERRORS();
1438     *image_extra = NULL;
1439     return _vg_get_image (surface,
1440                           0, 0, surface->width, surface->height,
1441                           image_out);
1442 }
1443
1444 static void
1445 _vg_surface_release_source_image (void                    *abstract_surface,
1446                                   cairo_image_surface_t   *image,
1447                                   void                    *image_extra)
1448 {
1449     cairo_surface_destroy (&image->base);
1450 }
1451
1452 static cairo_status_t
1453 _vg_surface_finish (void *abstract_surface)
1454 {
1455     cairo_vg_surface_t *surface = abstract_surface;
1456     cairo_vg_context_t *context = _vg_context_lock (surface->context);
1457
1458     if (surface->snapshot_cache_entry.hash) {
1459         _cairo_cache_remove (&context->snapshot_cache,
1460                              &surface->snapshot_cache_entry);
1461
1462         surface->snapshot_cache_entry.hash = 0;
1463     }
1464
1465     _cairo_surface_clipper_reset (&surface->clipper);
1466
1467     if (surface->own_image)
1468         vgDestroyImage (surface->image);
1469
1470     _vg_context_destroy_target (context, surface);
1471
1472     _vg_context_unlock (context);
1473     _vg_context_destroy (context);
1474
1475     CHECK_VG_ERRORS();
1476     return CAIRO_STATUS_SUCCESS;
1477 }
1478
1479 static const cairo_surface_backend_t cairo_vg_surface_backend = {
1480     CAIRO_SURFACE_TYPE_VG,
1481     _vg_surface_finish,
1482
1483     _cairo_default_context_create, /* XXX */
1484
1485     _vg_surface_create_similar,
1486     NULL, /* create similar image */
1487     NULL, /* map to image */
1488     NULL, /* unmap image */
1489
1490     _cairo_surface_default_source,
1491     _vg_surface_acquire_source_image,
1492     _vg_surface_release_source_image,
1493     NULL, /* snapshot */
1494
1495     NULL, /* copy_page */
1496     NULL, /* show_page */
1497
1498     _vg_surface_get_extents,
1499     _vg_surface_get_font_options, /* get_font_options */
1500
1501     NULL, /* flush */
1502     NULL, /* mark dirty */
1503
1504     _vg_surface_paint,
1505     _vg_surface_mask,
1506     _vg_surface_stroke,
1507     _vg_surface_fill,
1508     NULL, /* fill-stroke */
1509     _vg_surface_show_glyphs,
1510 };
1511
1512 static cairo_surface_t *
1513 _vg_surface_create_internal (cairo_vg_context_t *context,
1514                              VGImage image,
1515                              VGImageFormat format,
1516                              int width, int height)
1517 {
1518     cairo_vg_surface_t *surface;
1519
1520     surface = malloc (sizeof (cairo_vg_surface_t));
1521     if (unlikely (surface == NULL))
1522         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1523
1524     surface->context = _vg_context_reference (context);
1525
1526     surface->image  = image;
1527     surface->format = format;
1528
1529     _cairo_surface_init (&surface->base,
1530                          &cairo_vg_surface_backend,
1531                          NULL, /* device */
1532                          _vg_format_to_content (format));
1533
1534     surface->width  = width;
1535     surface->height = height;
1536
1537     _cairo_surface_clipper_init (&surface->clipper,
1538                                  _vg_surface_clipper_intersect_clip_path);
1539
1540     surface->snapshot_cache_entry.hash = 0;
1541
1542     surface->target_id = 0;
1543
1544     CHECK_VG_ERRORS();
1545     return &surface->base;
1546 }
1547
1548 cairo_surface_t *
1549 cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
1550                                    VGImage image,
1551                                    VGImageFormat format,
1552                                    int width, int height)
1553 {
1554     cairo_bool_t premult;
1555
1556     if (context->status)
1557         return _cairo_surface_create_in_error (context->status);
1558
1559     if (image == VG_INVALID_HANDLE)
1560         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1561     if (_vg_format_to_pixman (format, &premult) == 0)
1562         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
1563
1564     return _vg_surface_create_internal (context, image, format, width, height);
1565 }
1566
1567 cairo_surface_t *
1568 cairo_vg_surface_create (cairo_vg_context_t *context,
1569                          cairo_content_t  content,
1570                          int              width,
1571                          int              height)
1572 {
1573     VGImage image;
1574     VGImageFormat format;
1575     cairo_surface_t *surface;
1576
1577     if (context->status)
1578         return _cairo_surface_create_in_error (context->status);
1579
1580     if (! CAIRO_CONTENT_VALID (content))
1581         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
1582
1583     if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
1584         height > vgGeti (VG_MAX_IMAGE_HEIGHT))
1585     {
1586         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
1587     }
1588
1589
1590     format = _vg_format_for_content (content);
1591     image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER);
1592     if (image == VG_INVALID_HANDLE)
1593         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1594
1595     surface = _vg_surface_create_internal (context,
1596                                            image, format, width, height);
1597     if (unlikely (surface->status))
1598         return surface;
1599
1600     ((cairo_vg_surface_t *) surface)->own_image = TRUE;
1601     return surface;
1602 }
1603 slim_hidden_def (cairo_vg_surface_create);
1604
1605 VGImage
1606 cairo_vg_surface_get_image (cairo_surface_t *abstract_surface)
1607 {
1608     cairo_vg_surface_t *surface;
1609
1610     if (abstract_surface->backend != &cairo_vg_surface_backend) {
1611         _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1612         return VG_INVALID_HANDLE;
1613     }
1614
1615     surface = (cairo_vg_surface_t *) abstract_surface;
1616     return surface->image;
1617 }
1618
1619 int
1620 cairo_vg_surface_get_width (cairo_surface_t *abstract_surface)
1621 {
1622     cairo_vg_surface_t *surface;
1623
1624     if (abstract_surface->backend != &cairo_vg_surface_backend) {
1625         _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1626         return 0;
1627     }
1628
1629     surface = (cairo_vg_surface_t *) abstract_surface;
1630     return surface->width;
1631 }
1632
1633 int
1634 cairo_vg_surface_get_height (cairo_surface_t *abstract_surface)
1635 {
1636     cairo_vg_surface_t *surface;
1637
1638     if (abstract_surface->backend != &cairo_vg_surface_backend) {
1639         _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1640         return 0;
1641     }
1642
1643     surface = (cairo_vg_surface_t *) abstract_surface;
1644     return surface->height;
1645 }
1646
1647 VGImageFormat
1648 cairo_vg_surface_get_format (cairo_surface_t *abstract_surface)
1649 {
1650     cairo_vg_surface_t *surface;
1651
1652     if (abstract_surface->backend != &cairo_vg_surface_backend) {
1653         _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1654         return 0;
1655     }
1656
1657     surface = (cairo_vg_surface_t *) abstract_surface;
1658     return surface->format;
1659 }
1660
1661 /* GL specific context support :-(
1662  *
1663  * OpenVG like cairo defers creation of surface (and the necessary
1664  * paraphernalia to the application.
1665  */
1666
1667 static const cairo_vg_context_t _vg_context_nil = {
1668     CAIRO_STATUS_NO_MEMORY,
1669     CAIRO_REFERENCE_COUNT_INVALID
1670 };
1671
1672 static const cairo_vg_context_t _vg_context_nil_invalid_visual = {
1673     CAIRO_STATUS_INVALID_VISUAL,
1674     CAIRO_REFERENCE_COUNT_INVALID
1675 };
1676
1677 #if CAIRO_HAS_GLX_FUNCTIONS
1678 #include <GL/glx.h>
1679
1680 static cairo_status_t
1681 glx_create_target (cairo_vg_context_t *context,
1682                    cairo_vg_surface_t *surface)
1683 {
1684     /* XXX hmm, magic required for creating an FBO points to VGImage! */
1685     return CAIRO_INT_STATUS_UNSUPPORTED;
1686 }
1687
1688 static cairo_status_t
1689 glx_set_target (cairo_vg_context_t *context,
1690                 cairo_vg_surface_t *surface)
1691 {
1692 #if 0
1693     glXMakeContextCurrent (context->display,
1694                            (GLXDrawable) surface->target_id,
1695                            (GLXDrawable) surface->target_id,
1696                            context->context);
1697 #else
1698     return CAIRO_INT_STATUS_UNSUPPORTED;
1699 #endif
1700 }
1701
1702 static void
1703 glx_destroy_target (cairo_vg_context_t *context,
1704                     cairo_vg_surface_t *surface)
1705 {
1706 }
1707
1708 cairo_vg_context_t *
1709 cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx)
1710 {
1711     cairo_vg_context_t *context;
1712     cairo_status_t status;
1713
1714     context = malloc (sizeof (*context));
1715     if (unlikely (context == NULL))
1716         return (cairo_vg_context_t *) &_vg_context_nil;
1717
1718     context->display = dpy;
1719     context->context = ctx;
1720
1721     context->create_target  = glx_create_target;
1722     context->set_target     = glx_set_target;
1723     context->destroy_target = glx_destroy_target;
1724
1725     status = _vg_context_init (context);
1726     if (unlikely (status)) {
1727         free (context);
1728         return (cairo_vg_context_t *) &_vg_context_nil;
1729     }
1730
1731     return context;
1732 }
1733 #endif
1734
1735 #if CAIRO_HAS_EGL_FUNCTIONS
1736 static cairo_status_t
1737 egl_create_target (cairo_vg_context_t *context,
1738                    cairo_vg_surface_t *surface)
1739 {
1740     EGLSurface *egl_surface;
1741 #define RED 1
1742 #define GREEN 3
1743 #define BLUE 5
1744 #define ALPHA 7
1745     int attribs[] = {
1746         EGL_RED_SIZE, 0,
1747         EGL_GREEN_SIZE, 0,
1748         EGL_BLUE_SIZE, 0,
1749         EGL_ALPHA_SIZE, 0,
1750         EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
1751         EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
1752         EGL_NONE
1753     };
1754     pixman_format_code_t pixman_format;
1755     EGLConfig config;
1756     int num_configs = 0;
1757     cairo_bool_t needs_premultiply;
1758
1759     pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply);
1760     if (pixman_format == 0)
1761         return CAIRO_INT_STATUS_UNSUPPORTED;
1762
1763     /* XXX no control over pixel ordering! */
1764     attribs[RED]   = PIXMAN_FORMAT_R (pixman_format);
1765     attribs[GREEN] = PIXMAN_FORMAT_G (pixman_format);
1766     attribs[BLUE]  = PIXMAN_FORMAT_B (pixman_format);
1767     attribs[ALPHA] = PIXMAN_FORMAT_A (pixman_format);
1768
1769     if (! eglChooseConfig (context->display,
1770                            attribs,
1771                            &config, 1, &num_configs) ||
1772         num_configs != 1)
1773     {
1774         fprintf(stderr, "Error: eglChooseConfig() failed.\n");
1775         return CAIRO_INT_STATUS_UNSUPPORTED;
1776     }
1777
1778     egl_surface =
1779         eglCreatePbufferFromClientBuffer (context->display,
1780                                           EGL_OPENVG_IMAGE,
1781                                           (EGLClientBuffer) surface->image,
1782                                           config,
1783                                           NULL);
1784     surface->target_id = (unsigned long) egl_surface;
1785
1786     return CAIRO_STATUS_SUCCESS;
1787 }
1788
1789 static cairo_status_t
1790 egl_set_target (cairo_vg_context_t *context,
1791                 cairo_vg_surface_t *surface)
1792 {
1793     if (! eglMakeCurrent (context->display,
1794                           (EGLSurface *) surface->target_id,
1795                           (EGLSurface *) surface->target_id,
1796                           context->context))
1797     {
1798         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1799     }
1800
1801     return CAIRO_STATUS_SUCCESS;
1802 }
1803
1804 static void
1805 egl_destroy_target (cairo_vg_context_t *context,
1806                     cairo_vg_surface_t *surface)
1807 {
1808     eglDestroySurface (context->display,
1809                        (EGLSurface *) surface->target_id);
1810 }
1811
1812 cairo_vg_context_t *
1813 cairo_vg_context_create_for_egl (EGLDisplay egl_display,
1814                                  EGLContext egl_context)
1815 {
1816     cairo_vg_context_t *context;
1817     cairo_status_t status;
1818
1819     context = malloc (sizeof (*context));
1820     if (unlikely (context == NULL))
1821         return (cairo_vg_context_t *) &_vg_context_nil;
1822
1823     status = _vg_context_init (context);
1824     if (unlikely (status)) {
1825         free (context);
1826         return (cairo_vg_context_t *) &_vg_context_nil;
1827     }
1828
1829     context->display = egl_display;
1830     context->context = egl_context;
1831
1832     context->create_target  = egl_create_target;
1833     context->set_target     = egl_set_target;
1834     context->destroy_target = egl_destroy_target;
1835
1836     return context;
1837 }
1838 #endif
1839
1840 cairo_status_t
1841 cairo_vg_context_status (cairo_vg_context_t *context)
1842 {
1843     return context->status;
1844 }
1845
1846 void
1847 cairo_vg_context_destroy (cairo_vg_context_t *context)
1848 {
1849     if (context == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count))
1850         return;
1851
1852     _vg_context_destroy (context);
1853 }