beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-vg-surface.c
blob6e0d9a0ed69af0c8a4625b4bc7a3adf3c5082b93
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
4 * Copyright © 2008 Opened Hand Ltd.
5 * Copyright © 2009 Chris Wilson
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.
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
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/
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.
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>
37 #include "cairoint.h"
39 #include "cairo-vg.h"
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"
49 #include <pixman.h>
50 #include <VG/openvg.h>
52 //#define OPENVG_DEBUG
55 * Work that needs to be done:
56 * - Glyph cache / proper font support
58 * - First-class paths
59 * Paths are expensive for OpenVG, reuse paths whenever possible.
60 * So add a path cache, and first class paths!
63 typedef struct _cairo_vg_surface cairo_vg_surface_t;
65 /* XXX need GL specific context control. :( */
66 struct _cairo_vg_context {
67 cairo_status_t status;
68 cairo_reference_count_t ref_count;
70 unsigned long target_id;
72 VGPaint paint;
73 cairo_vg_surface_t *source;
74 double alpha;
76 cairo_cache_t snapshot_cache;
78 void *display;
79 void *context;
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 *);
88 struct _cairo_vg_surface {
89 cairo_surface_t base;
91 cairo_vg_context_t *context;
93 VGImage image;
94 VGImageFormat format;
95 int width;
96 int height;
97 cairo_bool_t own_image;
99 cairo_cache_entry_t snapshot_cache_entry;
101 cairo_surface_clipper_t clipper;
103 unsigned long target_id;
106 static const cairo_surface_backend_t cairo_vg_surface_backend;
108 slim_hidden_proto (cairo_vg_surface_create);
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);
116 static cairo_vg_context_t *
117 _vg_context_reference (cairo_vg_context_t *context)
119 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
121 _cairo_reference_count_inc (&context->ref_count);
123 return context;
126 static cairo_vg_context_t *
127 _vg_context_lock (cairo_vg_context_t *context)
129 /* XXX if we need to add locking, then it has to be recursive */
130 return context;
133 static cairo_int_status_t
134 _vg_context_set_target (cairo_vg_context_t *context,
135 cairo_vg_surface_t *surface)
137 cairo_status_t status;
139 if (surface->target_id == 0) {
140 status = context->create_target (context, surface);
141 if (unlikely (status))
142 return status;
145 if (context->target_id == surface->target_id)
146 return CAIRO_STATUS_SUCCESS;
148 context->target_id = surface->target_id;
150 return context->set_target (context, surface);
153 static void
154 _vg_context_destroy_target (cairo_vg_context_t *context,
155 cairo_vg_surface_t *surface)
157 if (surface->target_id == 0)
158 return;
160 if (context->target_id == surface->target_id)
161 context->set_target (context, NULL);
163 context->destroy_target (context, surface);
166 static cairo_bool_t
167 _vg_snapshot_cache_can_remove (const void *entry)
169 return TRUE;
172 static void
173 _vg_snapshot_cache_remove (void *cache_entry)
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);
182 static cairo_status_t
183 _vg_context_init (cairo_vg_context_t *context)
185 cairo_status_t status;
187 context->status = CAIRO_STATUS_SUCCESS;
188 CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1);
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;
198 context->target_id = 0;
199 context->source = NULL;
200 context->alpha = 1.0;
202 context->paint = vgCreatePaint ();
203 vgLoadIdentity ();
205 return CAIRO_STATUS_SUCCESS;
208 static void
209 _vg_context_destroy (cairo_vg_context_t *context)
211 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
213 if (! _cairo_reference_count_dec_and_test (&context->ref_count))
214 return;
216 if (context->paint != VG_INVALID_HANDLE)
217 vgDestroyPaint (context->paint);
219 _cairo_cache_fini (&context->snapshot_cache);
220 free (context);
223 static void
224 _vg_context_unlock (cairo_vg_context_t *context)
228 #ifdef OPENVG_DEBUG
229 static void check_vg_errors(const char*function,int line)
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);
238 #define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__)
239 #else
240 #define CHECK_VG_ERRORS() do{}while(0)
241 #endif //OPENVG_DEBUG
243 static pixman_format_code_t
244 _vg_format_to_pixman (VGImageFormat format,
245 cairo_bool_t *needs_premult_fixup)
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;
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;
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;
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;
300 static pixman_format_code_t
301 _vg_format_to_content (VGImageFormat format)
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;
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;
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;
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;
356 static VGImageFormat
357 _vg_format_from_pixman (pixman_format_code_t format)
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;
376 static VGImageFormat
377 _vg_format_for_content (cairo_content_t content)
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
387 static cairo_surface_t *
388 _vg_surface_create_similar (void *abstract_surface,
389 cairo_content_t content,
390 int width,
391 int height)
393 cairo_vg_surface_t *surface = abstract_surface;
395 if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
396 height > vgGeti (VG_MAX_IMAGE_HEIGHT))
398 return NULL;
401 return cairo_vg_surface_create (surface->context, content, width, height);
404 static cairo_status_t
405 _vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
406 cairo_path_fixed_t *path,
407 cairo_fill_rule_t fill_rule,
408 double tolerance,
409 cairo_antialias_t antialias)
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;
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;
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;
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;
443 vgSeti (VG_MASKING, VG_TRUE);
444 vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height);
446 cairo_surface_destroy (&mask->base);
448 CHECK_VG_ERRORS();
449 return CAIRO_STATUS_SUCCESS;
452 static cairo_bool_t
453 _vg_surface_get_extents (void *abstract_surface,
454 cairo_rectangle_int_t *extents)
456 cairo_vg_surface_t *surface = abstract_surface;
458 extents->x = 0;
459 extents->y = 0;
460 extents->width = surface->width;
461 extents->height = surface->height;
463 return TRUE;
466 #define MAX_SEG 16 /* max number of knots to upload in a batch */
468 typedef struct _vg_path {
469 VGPath path;
470 const cairo_matrix_t *ctm_inverse;
472 VGubyte gseg[MAX_SEG];
473 VGfloat gdata[MAX_SEG*3*2];
474 int dcount;
475 int scount;
476 } vg_path_t;
478 static cairo_status_t
479 _vg_move_to (void *closure,
480 const cairo_point_t *point)
482 vg_path_t *path = closure;
483 double x = _cairo_fixed_to_double (point->x);
484 double y = _cairo_fixed_to_double (point->y);
486 if (path->ctm_inverse)
487 cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
489 path->gseg[path->scount++] = VG_MOVE_TO;
490 path->gdata[path->dcount++] = x;
491 path->gdata[path->dcount++] = y;
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;
499 CHECK_VG_ERRORS();
500 return CAIRO_STATUS_SUCCESS;
503 static cairo_status_t
504 _vg_line_to (void *closure,
505 const cairo_point_t *point)
507 vg_path_t *path = closure;
508 double x = _cairo_fixed_to_double (point->x);
509 double y = _cairo_fixed_to_double (point->y);
511 if (path->ctm_inverse)
512 cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
514 path->gseg[path->scount++] = VG_LINE_TO;
515 path->gdata[path->dcount++] = x;
516 path->gdata[path->dcount++] = y;
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;
524 CHECK_VG_ERRORS();
525 return CAIRO_STATUS_SUCCESS;
528 static cairo_status_t
529 _vg_curve_to (void *closure,
530 const cairo_point_t *p0,
531 const cairo_point_t *p1,
532 const cairo_point_t *p2)
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);
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);
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;
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;
562 CHECK_VG_ERRORS();
563 return CAIRO_STATUS_SUCCESS;
566 static cairo_status_t
567 _vg_close_path (void *closure)
569 vg_path_t *path = closure;
571 path->gseg[path->scount++] = VG_CLOSE_PATH;
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;
579 CHECK_VG_ERRORS();
580 return CAIRO_STATUS_SUCCESS;
583 static void
584 _vg_path_from_cairo (vg_path_t *vg_path,
585 const cairo_path_fixed_t *path)
587 cairo_status_t status;
589 vg_path->scount = 0;
590 vg_path->dcount = 0;
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);
600 vgAppendPathData (vg_path->path,
601 vg_path->scount, vg_path->gseg, vg_path->gdata);
602 CHECK_VG_ERRORS();
605 static cairo_bool_t
606 _vg_is_supported_operator (cairo_operator_t op)
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;
617 default:
618 return FALSE;
622 static VGBlendMode
623 _vg_operator (cairo_operator_t op)
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;
644 static VGFillRule
645 _vg_fill_rule_from_cairo (cairo_fill_rule_t rule)
647 switch (rule) {
648 case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD;
649 case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO;
652 ASSERT_NOT_REACHED;
653 return VG_NON_ZERO;
656 static VGRenderingQuality
657 _vg_rendering_quality_from_cairo (cairo_antialias_t aa)
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;
666 case CAIRO_ANTIALIAS_GRAY:
667 case CAIRO_ANTIALIAS_FAST:
668 return VG_RENDERING_QUALITY_FASTER;
670 case CAIRO_ANTIALIAS_NONE:
671 return VG_RENDERING_QUALITY_NONANTIALIASED;
674 ASSERT_NOT_REACHED;
675 return VG_RENDERING_QUALITY_BETTER;
678 static VGCapStyle
679 _vg_line_cap_from_cairo (cairo_line_cap_t cap)
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;
687 ASSERT_NOT_REACHED;
688 return VG_CAP_BUTT;
691 static VGJoinStyle
692 _vg_line_join_from_cairo (cairo_line_join_t join)
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;
700 ASSERT_NOT_REACHED;
701 return VG_JOIN_MITER;
704 static void
705 _vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src)
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;
718 static cairo_status_t
719 _vg_setup_gradient_stops (cairo_vg_context_t *context,
720 const cairo_gradient_pattern_t *pattern)
722 VGint numstops = pattern->n_stops;
723 VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)];
724 int i;
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);
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;
742 vgSetParameterfv (context->paint,
743 VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops);
745 if (stops != stack_stops)
746 free (stops);
748 CHECK_VG_ERRORS();
749 return CAIRO_STATUS_SUCCESS;
752 static void
753 _vg_set_source_matrix (const cairo_pattern_t *pat)
755 cairo_matrix_t mat;
756 cairo_status_t status;
757 VGfloat vmat[9];
759 mat = pat->matrix;
760 status = cairo_matrix_invert (&mat);
761 assert (status == CAIRO_STATUS_SUCCESS);
763 _vg_matrix_from_cairo (vmat, &mat);
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);
771 CHECK_VG_ERRORS();
774 static cairo_status_t
775 _vg_setup_linear_source (cairo_vg_context_t *context,
776 const cairo_linear_pattern_t *lpat)
778 VGfloat linear[4];
780 linear[0] = lpat->pd1.x;
781 linear[1] = lpat->pd1.y;
782 linear[2] = lpat->pd2.x;
783 linear[3] = lpat->pd2.y;
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);
794 _vg_set_source_matrix (&lpat->base.base);
796 CHECK_VG_ERRORS();
797 return _vg_setup_gradient_stops (context, &lpat->base);
801 static cairo_status_t
802 _vg_setup_radial_source (cairo_vg_context_t *context,
803 const cairo_radial_pattern_t *rpat)
805 VGfloat radial[5];
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;
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);
820 _vg_set_source_matrix (&rpat->base.base);
822 /* FIXME: copy/adapt fixes from SVG backend to add inner radius */
824 CHECK_VG_ERRORS();
825 return _vg_setup_gradient_stops (context, &rpat->base);
828 static cairo_status_t
829 _vg_setup_solid_source (cairo_vg_context_t *context,
830 const cairo_solid_pattern_t *spat)
832 VGfloat color[] = {
833 spat->color.red,
834 spat->color.green,
835 spat->color.blue,
836 spat->color.alpha * context->alpha
839 vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
840 vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color);
842 CHECK_VG_ERRORS();
843 return CAIRO_STATUS_SUCCESS;
846 static cairo_vg_surface_t *
847 _vg_clone_recording_surface (cairo_vg_context_t *context,
848 cairo_surface_t *surface)
850 VGImage vg_image;
851 VGImageFormat format;
852 cairo_status_t status;
853 cairo_rectangle_int_t extents;
854 cairo_vg_surface_t *clone;
856 status = _cairo_surface_get_extents (surface, &extents);
857 if (status)
858 return NULL;
860 if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
861 extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
863 return NULL;
866 format = _vg_format_for_content (surface->content);
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);
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);
883 return clone;
886 static cairo_vg_surface_t *
887 _vg_clone_image_surface (cairo_vg_context_t *context,
888 cairo_surface_t *surface)
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;
898 if (surface->backend->acquire_source_image == NULL)
899 return NULL;
901 status = _cairo_surface_get_extents (surface, &extents);
902 if (status)
903 return NULL;
905 if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
906 extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
908 return NULL;
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);
916 format = _vg_format_from_pixman (image->pixman_format);
917 if (format == 0)
918 format = _vg_format_for_content (image->base.content);
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;
930 vgImageSubData (clone->image,
931 image->data, image->stride,
932 format, 0, 0, image->width, image->height);
934 _cairo_surface_release_source_image (surface, image, image_extra);
936 return clone;
939 static void
940 _vg_surface_remove_from_cache (cairo_surface_t *abstract_surface)
942 cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface;
944 if (surface->snapshot_cache_entry.hash) {
945 cairo_vg_context_t *context;
947 context = _vg_context_lock (surface->context);
948 _cairo_cache_remove (&context->snapshot_cache,
949 &surface->snapshot_cache_entry);
950 _vg_context_unlock (context);
952 surface->snapshot_cache_entry.hash = 0;
956 static cairo_status_t
957 _vg_setup_surface_source (cairo_vg_context_t *context,
958 const cairo_surface_pattern_t *spat)
960 cairo_surface_t *snapshot;
961 cairo_vg_surface_t *clone;
962 cairo_status_t status;
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;
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;
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;
989 _cairo_surface_attach_snapshot (spat->surface, &clone->base,
990 _vg_surface_remove_from_cache);
992 DONE:
993 cairo_surface_destroy (&context->source->base);
994 context->source = clone;
996 vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
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;
1005 case CAIRO_EXTEND_NONE:
1006 vgSetParameteri (context->paint,
1007 VG_PAINT_PATTERN_TILING_MODE,
1008 VG_TILE_FILL);
1010 VGfloat color[] = {0,0,0,0};
1011 vgSetfv (VG_TILE_FILL_COLOR, 4, color);
1013 break;
1015 case CAIRO_EXTEND_REPEAT:
1016 vgSetParameteri (context->paint,
1017 VG_PAINT_PATTERN_TILING_MODE,
1018 VG_TILE_REPEAT);
1019 break;
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;
1029 vgPaintPattern (context->paint, context->source->image);
1031 _vg_set_source_matrix (&spat->base);
1033 CHECK_VG_ERRORS();
1034 return CAIRO_STATUS_SUCCESS;
1037 static cairo_status_t
1038 setup_source (cairo_vg_context_t *context,
1039 const cairo_pattern_t *source)
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;
1060 static cairo_int_status_t
1061 _vg_surface_stroke (void *abstract_surface,
1062 cairo_operator_t op,
1063 const cairo_pattern_t *source,
1064 const cairo_path_fixed_t *path,
1065 const cairo_stroke_style_t *style,
1066 const cairo_matrix_t *ctm,
1067 const cairo_matrix_t *ctm_inverse,
1068 double tolerance,
1069 cairo_antialias_t antialias,
1070 const cairo_clip_t *clip)
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;
1079 if (! _vg_is_supported_operator (op))
1080 return CAIRO_INT_STATUS_UNSUPPORTED;
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;
1089 status = setup_source (context, source);
1090 if (status) {
1091 _vg_context_unlock (context);
1092 return status;
1095 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1096 if (unlikely (status)) {
1097 _vg_context_unlock (context);
1098 return status;
1101 vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
1102 VG_PATH_DATATYPE_F,
1103 1, 0, 0, 0,
1104 VG_PATH_CAPABILITY_ALL);
1106 vgGetMatrix (state);
1107 _vg_matrix_from_cairo (strokeTransform, ctm);
1108 vgMultMatrix (strokeTransform);
1110 vg_path.ctm_inverse = ctm_inverse;
1112 _vg_path_from_cairo (&vg_path, path);
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));
1120 vgSeti (VG_BLEND_MODE, _vg_operator (op));
1122 vgSetPaint (context->paint, VG_STROKE_PATH);
1124 vgDrawPath (vg_path.path, VG_STROKE_PATH);
1126 vgDestroyPath (vg_path.path);
1128 vgLoadMatrix (state);
1130 CHECK_VG_ERRORS();
1131 _vg_context_unlock (context);
1133 return CAIRO_STATUS_SUCCESS;
1136 static cairo_int_status_t
1137 _vg_surface_fill (void *abstract_surface,
1138 cairo_operator_t op,
1139 const cairo_pattern_t *source,
1140 const cairo_path_fixed_t *path,
1141 cairo_fill_rule_t fill_rule,
1142 double tolerance,
1143 cairo_antialias_t antialias,
1144 const cairo_clip_t *clip)
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;
1151 if (op == CAIRO_OPERATOR_DEST)
1152 return CAIRO_STATUS_SUCCESS;
1154 if (! _vg_is_supported_operator (op))
1155 return CAIRO_INT_STATUS_UNSUPPORTED;
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;
1164 status = setup_source (context, source);
1165 if (status) {
1166 _vg_context_unlock (context);
1167 return status;
1170 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1171 if (unlikely (status)) {
1172 _vg_context_unlock (context);
1173 return status;
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;
1183 _vg_path_from_cairo (&vg_path, path);
1185 /* XXX tolerance */
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));
1191 vgSetPaint (context->paint, VG_FILL_PATH);
1193 vgDrawPath (vg_path.path, VG_FILL_PATH);
1195 vgDestroyPath (vg_path.path);
1197 _vg_context_unlock (context);
1199 CHECK_VG_ERRORS();
1200 return CAIRO_STATUS_SUCCESS;
1203 static cairo_int_status_t
1204 _vg_surface_paint (void *abstract_surface,
1205 cairo_operator_t op,
1206 const cairo_pattern_t *source,
1207 const cairo_clip_t *clip)
1209 cairo_vg_surface_t *surface = abstract_surface;
1210 cairo_vg_context_t *context;
1211 cairo_status_t status;
1213 if (op == CAIRO_OPERATOR_DEST)
1214 return CAIRO_STATUS_SUCCESS;
1216 if (! _vg_is_supported_operator (op))
1217 return CAIRO_INT_STATUS_UNSUPPORTED;
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;
1226 status = setup_source (context, source);
1227 if (status) {
1228 _vg_context_unlock (context);
1229 return status;
1232 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1233 if (unlikely (status)) {
1234 _vg_context_unlock (context);
1235 return status;
1238 vgSeti (VG_BLEND_MODE, _vg_operator (op));
1239 vgSetPaint (context->paint, VG_FILL_PATH);
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
1247 VGfloat data[] = {
1248 0, 0,
1249 surface->width, 0,
1250 surface->width, surface->height,
1251 0, surface->height
1253 VGPath fullext;
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);
1259 vgDrawPath (fullext, VG_FILL_PATH);
1261 vgDestroyPath (fullext);
1264 _vg_context_unlock (context);
1266 CHECK_VG_ERRORS();
1267 return CAIRO_STATUS_SUCCESS;
1270 static cairo_int_status_t
1271 _vg_surface_mask (void *abstract_surface,
1272 cairo_operator_t op,
1273 const cairo_pattern_t *source,
1274 const cairo_pattern_t *mask,
1275 const cairo_clip_t *clip)
1277 cairo_vg_surface_t *surface = abstract_surface;
1278 cairo_status_t status;
1280 if (! _vg_is_supported_operator (op))
1281 return CAIRO_INT_STATUS_UNSUPPORTED;
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;
1289 context->alpha = solid->color.alpha;
1290 status = _vg_surface_paint (abstract_surface, op, source, clip);
1291 context->alpha = alpha;
1293 _vg_context_unlock (context);
1295 return status;
1298 return CAIRO_INT_STATUS_UNSUPPORTED;
1301 static void
1302 _vg_surface_get_font_options (void *abstract_surface,
1303 cairo_font_options_t *options)
1305 _cairo_font_options_init_default (options);
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);
1311 static cairo_int_status_t
1312 _vg_surface_show_glyphs (void *abstract_surface,
1313 cairo_operator_t op,
1314 const cairo_pattern_t *source,
1315 cairo_glyph_t *glyphs,
1316 int num_glyphs,
1317 cairo_scaled_font_t *scaled_font,
1318 const cairo_clip_t *clip)
1320 cairo_status_t status = CAIRO_STATUS_SUCCESS;
1321 cairo_path_fixed_t path;
1323 if (num_glyphs <= 0)
1324 return CAIRO_STATUS_SUCCESS;
1326 _cairo_path_fixed_init (&path);
1328 /* XXX Glyph cache! OpenVG font support in 1.1? */
1330 status = _cairo_scaled_font_glyph_path (scaled_font,
1331 glyphs, num_glyphs,
1332 &path);
1333 if (unlikely (status))
1334 goto BAIL;
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;
1347 static inline int
1348 multiply_alpha (int alpha, int color)
1350 int temp = alpha * color + 0x80;
1351 return (temp + (temp >> 8)) >> 8;
1354 static void
1355 premultiply_argb (uint8_t *data,
1356 int width,
1357 int height,
1358 int stride)
1360 int i;
1362 while (height --) {
1363 uint32_t *row = (uint32_t *) data;
1365 for (i = 0; i < width; i++) {
1366 uint32_t p = row[i];
1367 uint8_t alpha;
1369 alpha = p >> 24;
1370 if (alpha == 0) {
1371 row[i] = 0;
1372 } else if (alpha != 0xff) {
1373 uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff);
1374 uint8_t g = multiply_alpha (alpha, (p >> 8) & 0xff);
1375 uint8_t b = multiply_alpha (alpha, (p >> 0) & 0xff);
1376 row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0);
1380 data += stride;
1384 static cairo_int_status_t
1385 _vg_get_image (cairo_vg_surface_t *surface,
1386 int x, int y,
1387 int width, int height,
1388 cairo_image_surface_t **image_out)
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;
1395 pixman_format = _vg_format_to_pixman (surface->format,
1396 &needs_premultiply);
1397 if (pixman_format == 0)
1398 return CAIRO_INT_STATUS_UNSUPPORTED;
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);
1406 vgFinish ();
1407 CHECK_VG_ERRORS();
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);
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;
1423 if (needs_premultiply)
1424 premultiply_argb (image->data, width, height, image->stride);
1426 *image_out = image;
1427 return CAIRO_STATUS_SUCCESS;
1430 static cairo_status_t
1431 _vg_surface_acquire_source_image (void *abstract_surface,
1432 cairo_image_surface_t **image_out,
1433 void **image_extra)
1435 cairo_vg_surface_t *surface = abstract_surface;
1437 CHECK_VG_ERRORS();
1438 *image_extra = NULL;
1439 return _vg_get_image (surface,
1440 0, 0, surface->width, surface->height,
1441 image_out);
1444 static void
1445 _vg_surface_release_source_image (void *abstract_surface,
1446 cairo_image_surface_t *image,
1447 void *image_extra)
1449 cairo_surface_destroy (&image->base);
1452 static cairo_status_t
1453 _vg_surface_finish (void *abstract_surface)
1455 cairo_vg_surface_t *surface = abstract_surface;
1456 cairo_vg_context_t *context = _vg_context_lock (surface->context);
1458 if (surface->snapshot_cache_entry.hash) {
1459 _cairo_cache_remove (&context->snapshot_cache,
1460 &surface->snapshot_cache_entry);
1462 surface->snapshot_cache_entry.hash = 0;
1465 _cairo_surface_clipper_reset (&surface->clipper);
1467 if (surface->own_image)
1468 vgDestroyImage (surface->image);
1470 _vg_context_destroy_target (context, surface);
1472 _vg_context_unlock (context);
1473 _vg_context_destroy (context);
1475 CHECK_VG_ERRORS();
1476 return CAIRO_STATUS_SUCCESS;
1479 static const cairo_surface_backend_t cairo_vg_surface_backend = {
1480 CAIRO_SURFACE_TYPE_VG,
1481 _vg_surface_finish,
1483 _cairo_default_context_create, /* XXX */
1485 _vg_surface_create_similar,
1486 NULL, /* create similar image */
1487 NULL, /* map to image */
1488 NULL, /* unmap image */
1490 _cairo_surface_default_source,
1491 _vg_surface_acquire_source_image,
1492 _vg_surface_release_source_image,
1493 NULL, /* snapshot */
1495 NULL, /* copy_page */
1496 NULL, /* show_page */
1498 _vg_surface_get_extents,
1499 _vg_surface_get_font_options, /* get_font_options */
1501 NULL, /* flush */
1502 NULL, /* mark dirty */
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,
1512 static cairo_surface_t *
1513 _vg_surface_create_internal (cairo_vg_context_t *context,
1514 VGImage image,
1515 VGImageFormat format,
1516 int width, int height)
1518 cairo_vg_surface_t *surface;
1520 surface = malloc (sizeof (cairo_vg_surface_t));
1521 if (unlikely (surface == NULL))
1522 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1524 surface->context = _vg_context_reference (context);
1526 surface->image = image;
1527 surface->format = format;
1529 _cairo_surface_init (&surface->base,
1530 &cairo_vg_surface_backend,
1531 NULL, /* device */
1532 _vg_format_to_content (format));
1534 surface->width = width;
1535 surface->height = height;
1537 _cairo_surface_clipper_init (&surface->clipper,
1538 _vg_surface_clipper_intersect_clip_path);
1540 surface->snapshot_cache_entry.hash = 0;
1542 surface->target_id = 0;
1544 CHECK_VG_ERRORS();
1545 return &surface->base;
1548 cairo_surface_t *
1549 cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
1550 VGImage image,
1551 VGImageFormat format,
1552 int width, int height)
1554 cairo_bool_t premult;
1556 if (context->status)
1557 return _cairo_surface_create_in_error (context->status);
1559 if (image == VG_INVALID_HANDLE)
1560 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1561 if (_vg_format_to_pixman (format, &premult) == 0)
1562 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
1564 return _vg_surface_create_internal (context, image, format, width, height);
1567 cairo_surface_t *
1568 cairo_vg_surface_create (cairo_vg_context_t *context,
1569 cairo_content_t content,
1570 int width,
1571 int height)
1573 VGImage image;
1574 VGImageFormat format;
1575 cairo_surface_t *surface;
1577 if (context->status)
1578 return _cairo_surface_create_in_error (context->status);
1580 if (! CAIRO_CONTENT_VALID (content))
1581 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
1583 if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
1584 height > vgGeti (VG_MAX_IMAGE_HEIGHT))
1586 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
1590 format = _vg_format_for_content (content);
1591 image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER);
1592 if (image == VG_INVALID_HANDLE)
1593 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1595 surface = _vg_surface_create_internal (context,
1596 image, format, width, height);
1597 if (unlikely (surface->status))
1598 return surface;
1600 ((cairo_vg_surface_t *) surface)->own_image = TRUE;
1601 return surface;
1603 slim_hidden_def (cairo_vg_surface_create);
1605 VGImage
1606 cairo_vg_surface_get_image (cairo_surface_t *abstract_surface)
1608 cairo_vg_surface_t *surface;
1610 if (abstract_surface->backend != &cairo_vg_surface_backend) {
1611 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1612 return VG_INVALID_HANDLE;
1615 surface = (cairo_vg_surface_t *) abstract_surface;
1616 return surface->image;
1620 cairo_vg_surface_get_width (cairo_surface_t *abstract_surface)
1622 cairo_vg_surface_t *surface;
1624 if (abstract_surface->backend != &cairo_vg_surface_backend) {
1625 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1626 return 0;
1629 surface = (cairo_vg_surface_t *) abstract_surface;
1630 return surface->width;
1634 cairo_vg_surface_get_height (cairo_surface_t *abstract_surface)
1636 cairo_vg_surface_t *surface;
1638 if (abstract_surface->backend != &cairo_vg_surface_backend) {
1639 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1640 return 0;
1643 surface = (cairo_vg_surface_t *) abstract_surface;
1644 return surface->height;
1647 VGImageFormat
1648 cairo_vg_surface_get_format (cairo_surface_t *abstract_surface)
1650 cairo_vg_surface_t *surface;
1652 if (abstract_surface->backend != &cairo_vg_surface_backend) {
1653 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1654 return 0;
1657 surface = (cairo_vg_surface_t *) abstract_surface;
1658 return surface->format;
1661 /* GL specific context support :-(
1663 * OpenVG like cairo defers creation of surface (and the necessary
1664 * paraphernalia to the application.
1667 static const cairo_vg_context_t _vg_context_nil = {
1668 CAIRO_STATUS_NO_MEMORY,
1669 CAIRO_REFERENCE_COUNT_INVALID
1672 static const cairo_vg_context_t _vg_context_nil_invalid_visual = {
1673 CAIRO_STATUS_INVALID_VISUAL,
1674 CAIRO_REFERENCE_COUNT_INVALID
1677 #if CAIRO_HAS_GLX_FUNCTIONS
1678 #include <GL/glx.h>
1680 static cairo_status_t
1681 glx_create_target (cairo_vg_context_t *context,
1682 cairo_vg_surface_t *surface)
1684 /* XXX hmm, magic required for creating an FBO points to VGImage! */
1685 return CAIRO_INT_STATUS_UNSUPPORTED;
1688 static cairo_status_t
1689 glx_set_target (cairo_vg_context_t *context,
1690 cairo_vg_surface_t *surface)
1692 #if 0
1693 glXMakeContextCurrent (context->display,
1694 (GLXDrawable) surface->target_id,
1695 (GLXDrawable) surface->target_id,
1696 context->context);
1697 #else
1698 return CAIRO_INT_STATUS_UNSUPPORTED;
1699 #endif
1702 static void
1703 glx_destroy_target (cairo_vg_context_t *context,
1704 cairo_vg_surface_t *surface)
1708 cairo_vg_context_t *
1709 cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx)
1711 cairo_vg_context_t *context;
1712 cairo_status_t status;
1714 context = malloc (sizeof (*context));
1715 if (unlikely (context == NULL))
1716 return (cairo_vg_context_t *) &_vg_context_nil;
1718 context->display = dpy;
1719 context->context = ctx;
1721 context->create_target = glx_create_target;
1722 context->set_target = glx_set_target;
1723 context->destroy_target = glx_destroy_target;
1725 status = _vg_context_init (context);
1726 if (unlikely (status)) {
1727 free (context);
1728 return (cairo_vg_context_t *) &_vg_context_nil;
1731 return context;
1733 #endif
1735 #if CAIRO_HAS_EGL_FUNCTIONS
1736 static cairo_status_t
1737 egl_create_target (cairo_vg_context_t *context,
1738 cairo_vg_surface_t *surface)
1740 EGLSurface *egl_surface;
1741 #define RED 1
1742 #define GREEN 3
1743 #define BLUE 5
1744 #define ALPHA 7
1745 int attribs[] = {
1746 EGL_RED_SIZE, 0,
1747 EGL_GREEN_SIZE, 0,
1748 EGL_BLUE_SIZE, 0,
1749 EGL_ALPHA_SIZE, 0,
1750 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
1751 EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
1752 EGL_NONE
1754 pixman_format_code_t pixman_format;
1755 EGLConfig config;
1756 int num_configs = 0;
1757 cairo_bool_t needs_premultiply;
1759 pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply);
1760 if (pixman_format == 0)
1761 return CAIRO_INT_STATUS_UNSUPPORTED;
1763 /* XXX no control over pixel ordering! */
1764 attribs[RED] = PIXMAN_FORMAT_R (pixman_format);
1765 attribs[GREEN] = PIXMAN_FORMAT_G (pixman_format);
1766 attribs[BLUE] = PIXMAN_FORMAT_B (pixman_format);
1767 attribs[ALPHA] = PIXMAN_FORMAT_A (pixman_format);
1769 if (! eglChooseConfig (context->display,
1770 attribs,
1771 &config, 1, &num_configs) ||
1772 num_configs != 1)
1774 fprintf(stderr, "Error: eglChooseConfig() failed.\n");
1775 return CAIRO_INT_STATUS_UNSUPPORTED;
1778 egl_surface =
1779 eglCreatePbufferFromClientBuffer (context->display,
1780 EGL_OPENVG_IMAGE,
1781 (EGLClientBuffer) surface->image,
1782 config,
1783 NULL);
1784 surface->target_id = (unsigned long) egl_surface;
1786 return CAIRO_STATUS_SUCCESS;
1789 static cairo_status_t
1790 egl_set_target (cairo_vg_context_t *context,
1791 cairo_vg_surface_t *surface)
1793 if (! eglMakeCurrent (context->display,
1794 (EGLSurface *) surface->target_id,
1795 (EGLSurface *) surface->target_id,
1796 context->context))
1798 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1801 return CAIRO_STATUS_SUCCESS;
1804 static void
1805 egl_destroy_target (cairo_vg_context_t *context,
1806 cairo_vg_surface_t *surface)
1808 eglDestroySurface (context->display,
1809 (EGLSurface *) surface->target_id);
1812 cairo_vg_context_t *
1813 cairo_vg_context_create_for_egl (EGLDisplay egl_display,
1814 EGLContext egl_context)
1816 cairo_vg_context_t *context;
1817 cairo_status_t status;
1819 context = malloc (sizeof (*context));
1820 if (unlikely (context == NULL))
1821 return (cairo_vg_context_t *) &_vg_context_nil;
1823 status = _vg_context_init (context);
1824 if (unlikely (status)) {
1825 free (context);
1826 return (cairo_vg_context_t *) &_vg_context_nil;
1829 context->display = egl_display;
1830 context->context = egl_context;
1832 context->create_target = egl_create_target;
1833 context->set_target = egl_set_target;
1834 context->destroy_target = egl_destroy_target;
1836 return context;
1838 #endif
1840 cairo_status_t
1841 cairo_vg_context_status (cairo_vg_context_t *context)
1843 return context->status;
1846 void
1847 cairo_vg_context_destroy (cairo_vg_context_t *context)
1849 if (context == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count))
1850 return;
1852 _vg_context_destroy (context);