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