Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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