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