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