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 *
_vg_context_reference(cairo_vg_context_t * context)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 *
_vg_context_lock(cairo_vg_context_t * context)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
_vg_context_set_target(cairo_vg_context_t * context,cairo_vg_surface_t * surface)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
_vg_context_destroy_target(cairo_vg_context_t * context,cairo_vg_surface_t * surface)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
_vg_snapshot_cache_can_remove(const void * entry)167 _vg_snapshot_cache_can_remove (const void *entry)
168 {
169     return TRUE;
170 }
171 
172 static void
_vg_snapshot_cache_remove(void * cache_entry)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
_vg_context_init(cairo_vg_context_t * context)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
_vg_context_destroy(cairo_vg_context_t * context)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
_vg_context_unlock(cairo_vg_context_t * context)224 _vg_context_unlock (cairo_vg_context_t *context)
225 {
226 }
227 
228 #ifdef OPENVG_DEBUG
check_vg_errors(const char * function,int line)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
_vg_format_to_pixman(VGImageFormat format,cairo_bool_t * needs_premult_fixup)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
_vg_format_to_content(VGImageFormat format)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
_vg_format_from_pixman(pixman_format_code_t format)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
_vg_format_for_content(cairo_content_t content)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 *
_vg_surface_create_similar(void * abstract_surface,cairo_content_t content,int width,int height)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
_vg_surface_clipper_intersect_clip_path(cairo_surface_clipper_t * clipper,cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_antialias_t antialias)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
_vg_surface_get_extents(void * abstract_surface,cairo_rectangle_int_t * extents)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
_vg_move_to(void * closure,const cairo_point_t * point)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
_vg_line_to(void * closure,const cairo_point_t * point)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
_vg_curve_to(void * closure,const cairo_point_t * p0,const cairo_point_t * p1,const cairo_point_t * p2)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
_vg_close_path(void * closure)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
_vg_path_from_cairo(vg_path_t * vg_path,const cairo_path_fixed_t * path)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
_vg_is_supported_operator(cairo_operator_t op)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
_vg_operator(cairo_operator_t op)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
_vg_fill_rule_from_cairo(cairo_fill_rule_t rule)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
_vg_rendering_quality_from_cairo(cairo_antialias_t aa)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
_vg_line_cap_from_cairo(cairo_line_cap_t cap)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
_vg_line_join_from_cairo(cairo_line_join_t join)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
_vg_matrix_from_cairo(VGfloat * dst,const cairo_matrix_t * src)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
_vg_setup_gradient_stops(cairo_vg_context_t * context,const cairo_gradient_pattern_t * pattern)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
_vg_set_source_matrix(const cairo_pattern_t * pat)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
_vg_setup_linear_source(cairo_vg_context_t * context,const cairo_linear_pattern_t * lpat)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
_vg_setup_radial_source(cairo_vg_context_t * context,const cairo_radial_pattern_t * rpat)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
_vg_setup_solid_source(cairo_vg_context_t * context,const cairo_solid_pattern_t * spat)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 *
_vg_clone_recording_surface(cairo_vg_context_t * context,cairo_surface_t * surface)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 *
_vg_clone_image_surface(cairo_vg_context_t * context,cairo_surface_t * surface)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
_vg_surface_remove_from_cache(cairo_surface_t * abstract_surface)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
_vg_setup_surface_source(cairo_vg_context_t * context,const cairo_surface_pattern_t * spat)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
setup_source(cairo_vg_context_t * context,const cairo_pattern_t * source)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
_vg_surface_stroke(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,const cairo_stroke_style_t * style,const cairo_matrix_t * ctm,const cairo_matrix_t * ctm_inverse,double tolerance,cairo_antialias_t antialias,const cairo_clip_t * clip)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
_vg_surface_fill(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_antialias_t antialias,const cairo_clip_t * clip)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
_vg_surface_paint(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_clip_t * clip)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
_vg_surface_mask(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_pattern_t * mask,const cairo_clip_t * clip)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
_vg_surface_get_font_options(void * abstract_surface,cairo_font_options_t * options)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
_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,const cairo_clip_t * clip)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
multiply_alpha(int alpha,int color)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
premultiply_argb(uint8_t * data,int width,int height,int stride)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] = ((uint32_t)alpha << 24) | (r << 16) | (g << 8) | (b << 0);
1377 	    }
1378 	}
1379 
1380 	data += stride;
1381     }
1382 }
1383 
1384 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)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
_vg_surface_acquire_source_image(void * abstract_surface,cairo_image_surface_t ** image_out,void ** image_extra)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
_vg_surface_release_source_image(void * abstract_surface,cairo_image_surface_t * image,void * image_extra)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
_vg_surface_finish(void * abstract_surface)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 *
_vg_surface_create_internal(cairo_vg_context_t * context,VGImage image,VGImageFormat format,int width,int height)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 = _cairo_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 			 FALSE); /* is_vector */
1534 
1535     surface->width  = width;
1536     surface->height = height;
1537 
1538     _cairo_surface_clipper_init (&surface->clipper,
1539 				 _vg_surface_clipper_intersect_clip_path);
1540 
1541     surface->snapshot_cache_entry.hash = 0;
1542 
1543     surface->target_id = 0;
1544 
1545     CHECK_VG_ERRORS();
1546     return &surface->base;
1547 }
1548 
1549 cairo_surface_t *
cairo_vg_surface_create_for_image(cairo_vg_context_t * context,VGImage image,VGImageFormat format,int width,int height)1550 cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
1551 				   VGImage image,
1552 				   VGImageFormat format,
1553 				   int width, int height)
1554 {
1555     cairo_bool_t premult;
1556 
1557     if (context->status)
1558 	return _cairo_surface_create_in_error (context->status);
1559 
1560     if (image == VG_INVALID_HANDLE)
1561 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1562     if (_vg_format_to_pixman (format, &premult) == 0)
1563 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
1564 
1565     return _vg_surface_create_internal (context, image, format, width, height);
1566 }
1567 
1568 cairo_surface_t *
cairo_vg_surface_create(cairo_vg_context_t * context,cairo_content_t content,int width,int height)1569 cairo_vg_surface_create (cairo_vg_context_t *context,
1570 			 cairo_content_t  content,
1571 			 int              width,
1572 			 int              height)
1573 {
1574     VGImage image;
1575     VGImageFormat format;
1576     cairo_surface_t *surface;
1577 
1578     if (context->status)
1579 	return _cairo_surface_create_in_error (context->status);
1580 
1581     if (! CAIRO_CONTENT_VALID (content))
1582 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
1583 
1584     if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
1585 	height > vgGeti (VG_MAX_IMAGE_HEIGHT))
1586     {
1587 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
1588     }
1589 
1590 
1591     format = _vg_format_for_content (content);
1592     image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER);
1593     if (image == VG_INVALID_HANDLE)
1594 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1595 
1596     surface = _vg_surface_create_internal (context,
1597 					   image, format, width, height);
1598     if (unlikely (surface->status))
1599 	return surface;
1600 
1601     ((cairo_vg_surface_t *) surface)->own_image = TRUE;
1602     return surface;
1603 }
1604 slim_hidden_def (cairo_vg_surface_create);
1605 
1606 VGImage
cairo_vg_surface_get_image(cairo_surface_t * abstract_surface)1607 cairo_vg_surface_get_image (cairo_surface_t *abstract_surface)
1608 {
1609     cairo_vg_surface_t *surface;
1610 
1611     if (abstract_surface->backend != &cairo_vg_surface_backend) {
1612 	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1613 	return VG_INVALID_HANDLE;
1614     }
1615 
1616     surface = (cairo_vg_surface_t *) abstract_surface;
1617     return surface->image;
1618 }
1619 
1620 int
cairo_vg_surface_get_width(cairo_surface_t * abstract_surface)1621 cairo_vg_surface_get_width (cairo_surface_t *abstract_surface)
1622 {
1623     cairo_vg_surface_t *surface;
1624 
1625     if (abstract_surface->backend != &cairo_vg_surface_backend) {
1626 	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1627 	return 0;
1628     }
1629 
1630     surface = (cairo_vg_surface_t *) abstract_surface;
1631     return surface->width;
1632 }
1633 
1634 int
cairo_vg_surface_get_height(cairo_surface_t * abstract_surface)1635 cairo_vg_surface_get_height (cairo_surface_t *abstract_surface)
1636 {
1637     cairo_vg_surface_t *surface;
1638 
1639     if (abstract_surface->backend != &cairo_vg_surface_backend) {
1640 	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1641 	return 0;
1642     }
1643 
1644     surface = (cairo_vg_surface_t *) abstract_surface;
1645     return surface->height;
1646 }
1647 
1648 VGImageFormat
cairo_vg_surface_get_format(cairo_surface_t * abstract_surface)1649 cairo_vg_surface_get_format (cairo_surface_t *abstract_surface)
1650 {
1651     cairo_vg_surface_t *surface;
1652 
1653     if (abstract_surface->backend != &cairo_vg_surface_backend) {
1654 	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1655 	return 0;
1656     }
1657 
1658     surface = (cairo_vg_surface_t *) abstract_surface;
1659     return surface->format;
1660 }
1661 
1662 /* GL specific context support :-(
1663  *
1664  * OpenVG like cairo defers creation of surface (and the necessary
1665  * paraphernalia to the application.
1666  */
1667 
1668 static const cairo_vg_context_t _vg_context_nil = {
1669     CAIRO_STATUS_NO_MEMORY,
1670     CAIRO_REFERENCE_COUNT_INVALID
1671 };
1672 
1673 static const cairo_vg_context_t _vg_context_nil_invalid_visual = {
1674     CAIRO_STATUS_INVALID_VISUAL,
1675     CAIRO_REFERENCE_COUNT_INVALID
1676 };
1677 
1678 #if CAIRO_HAS_GLX_FUNCTIONS
1679 #include <GL/glx.h>
1680 
1681 static cairo_status_t
glx_create_target(cairo_vg_context_t * context,cairo_vg_surface_t * surface)1682 glx_create_target (cairo_vg_context_t *context,
1683 		   cairo_vg_surface_t *surface)
1684 {
1685     /* XXX hmm, magic required for creating an FBO points to VGImage! */
1686     return CAIRO_INT_STATUS_UNSUPPORTED;
1687 }
1688 
1689 static cairo_status_t
glx_set_target(cairo_vg_context_t * context,cairo_vg_surface_t * surface)1690 glx_set_target (cairo_vg_context_t *context,
1691 		cairo_vg_surface_t *surface)
1692 {
1693 #if 0
1694     glXMakeContextCurrent (context->display,
1695 			   (GLXDrawable) surface->target_id,
1696 			   (GLXDrawable) surface->target_id,
1697 			   context->context);
1698 #else
1699     return CAIRO_INT_STATUS_UNSUPPORTED;
1700 #endif
1701 }
1702 
1703 static void
glx_destroy_target(cairo_vg_context_t * context,cairo_vg_surface_t * surface)1704 glx_destroy_target (cairo_vg_context_t *context,
1705 		    cairo_vg_surface_t *surface)
1706 {
1707 }
1708 
1709 cairo_vg_context_t *
cairo_vg_context_create_for_glx(Display * dpy,GLXContext ctx)1710 cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx)
1711 {
1712     cairo_vg_context_t *context;
1713     cairo_status_t status;
1714 
1715     context = _cairo_malloc (sizeof (*context));
1716     if (unlikely (context == NULL))
1717 	return (cairo_vg_context_t *) &_vg_context_nil;
1718 
1719     context->display = dpy;
1720     context->context = ctx;
1721 
1722     context->create_target  = glx_create_target;
1723     context->set_target     = glx_set_target;
1724     context->destroy_target = glx_destroy_target;
1725 
1726     status = _vg_context_init (context);
1727     if (unlikely (status)) {
1728 	free (context);
1729 	return (cairo_vg_context_t *) &_vg_context_nil;
1730     }
1731 
1732     return context;
1733 }
1734 #endif
1735 
1736 #if CAIRO_HAS_EGL_FUNCTIONS
1737 static cairo_status_t
egl_create_target(cairo_vg_context_t * context,cairo_vg_surface_t * surface)1738 egl_create_target (cairo_vg_context_t *context,
1739 		   cairo_vg_surface_t *surface)
1740 {
1741     EGLSurface *egl_surface;
1742 #define RED 1
1743 #define GREEN 3
1744 #define BLUE 5
1745 #define ALPHA 7
1746     int attribs[] = {
1747 	EGL_RED_SIZE, 0,
1748 	EGL_GREEN_SIZE, 0,
1749 	EGL_BLUE_SIZE, 0,
1750 	EGL_ALPHA_SIZE, 0,
1751 	EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
1752 	EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
1753 	EGL_NONE
1754     };
1755     pixman_format_code_t pixman_format;
1756     EGLConfig config;
1757     int num_configs = 0;
1758     cairo_bool_t needs_premultiply;
1759 
1760     pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply);
1761     if (pixman_format == 0)
1762 	return CAIRO_INT_STATUS_UNSUPPORTED;
1763 
1764     /* XXX no control over pixel ordering! */
1765     attribs[RED]   = PIXMAN_FORMAT_R (pixman_format);
1766     attribs[GREEN] = PIXMAN_FORMAT_G (pixman_format);
1767     attribs[BLUE]  = PIXMAN_FORMAT_B (pixman_format);
1768     attribs[ALPHA] = PIXMAN_FORMAT_A (pixman_format);
1769 
1770     if (! eglChooseConfig (context->display,
1771 			   attribs,
1772 			   &config, 1, &num_configs) ||
1773 	num_configs != 1)
1774     {
1775 	fprintf(stderr, "Error: eglChooseConfig() failed.\n");
1776 	return CAIRO_INT_STATUS_UNSUPPORTED;
1777     }
1778 
1779     egl_surface =
1780 	eglCreatePbufferFromClientBuffer (context->display,
1781 					  EGL_OPENVG_IMAGE,
1782 					  (EGLClientBuffer) surface->image,
1783 					  config,
1784 					  NULL);
1785     surface->target_id = (unsigned long) egl_surface;
1786 
1787     return CAIRO_STATUS_SUCCESS;
1788 }
1789 
1790 static cairo_status_t
egl_set_target(cairo_vg_context_t * context,cairo_vg_surface_t * surface)1791 egl_set_target (cairo_vg_context_t *context,
1792 		cairo_vg_surface_t *surface)
1793 {
1794     if (! eglMakeCurrent (context->display,
1795 			  (EGLSurface *) surface->target_id,
1796 			  (EGLSurface *) surface->target_id,
1797 			  context->context))
1798     {
1799 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1800     }
1801 
1802     return CAIRO_STATUS_SUCCESS;
1803 }
1804 
1805 static void
egl_destroy_target(cairo_vg_context_t * context,cairo_vg_surface_t * surface)1806 egl_destroy_target (cairo_vg_context_t *context,
1807 		    cairo_vg_surface_t *surface)
1808 {
1809     eglDestroySurface (context->display,
1810 		       (EGLSurface *) surface->target_id);
1811 }
1812 
1813 cairo_vg_context_t *
cairo_vg_context_create_for_egl(EGLDisplay egl_display,EGLContext egl_context)1814 cairo_vg_context_create_for_egl (EGLDisplay egl_display,
1815 				 EGLContext egl_context)
1816 {
1817     cairo_vg_context_t *context;
1818     cairo_status_t status;
1819 
1820     context = _cairo_malloc (sizeof (*context));
1821     if (unlikely (context == NULL))
1822 	return (cairo_vg_context_t *) &_vg_context_nil;
1823 
1824     status = _vg_context_init (context);
1825     if (unlikely (status)) {
1826 	free (context);
1827 	return (cairo_vg_context_t *) &_vg_context_nil;
1828     }
1829 
1830     context->display = egl_display;
1831     context->context = egl_context;
1832 
1833     context->create_target  = egl_create_target;
1834     context->set_target     = egl_set_target;
1835     context->destroy_target = egl_destroy_target;
1836 
1837     return context;
1838 }
1839 #endif
1840 
1841 cairo_status_t
cairo_vg_context_status(cairo_vg_context_t * context)1842 cairo_vg_context_status (cairo_vg_context_t *context)
1843 {
1844     return context->status;
1845 }
1846 
1847 void
cairo_vg_context_destroy(cairo_vg_context_t * context)1848 cairo_vg_context_destroy (cairo_vg_context_t *context)
1849 {
1850     if (context == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count))
1851 	return;
1852 
1853     _vg_context_destroy (context);
1854 }
1855