beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-cogl-surface.c
blobc57fd7f433c5ac1f97275c9012e08e0788797ae5
1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2011 Intel Corporation.
5 * This library is free software; you can redistribute it and/or
6 * modify it either under the terms of the GNU Lesser General Public
7 * License version 2.1 as published by the Free Software Foundation
8 * (the "LGPL") or, at your option, under the terms of the Mozilla
9 * Public License Version 1.1 (the "MPL"). If you do not alter this
10 * notice, a recipient may use your version of this file under either
11 * the MPL or the LGPL.
13 * You should have received a copy of the LGPL along with this library
14 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
19 * The contents of this file are subject to the Mozilla Public License
20 * Version 1.1 (the "License"); you may not use this file except in
21 * compliance with the License. You may obtain a copy of the License at
22 * http://www.mozilla.og/MPL/
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26 * the specific language governing rights and limitations.
28 * Contributor(s):
29 * Robert Bragg <robert@linux.intel.com>
31 #include "cairoint.h"
33 #include "cairo-cache-private.h"
34 #include "cairo-error-private.h"
35 #include "cairo-path-fixed-private.h"
36 #include "cairo-recording-surface-private.h"
37 #include "cairo-surface-clipper-private.h"
38 #include "cairo-fixed-private.h"
39 #include "cairo-device-private.h"
40 #include "cairo-composite-rectangles-private.h"
41 #include "cairo-image-surface-inline.h"
42 #include "cairo-cogl-private.h"
43 #include "cairo-cogl-gradient-private.h"
44 #include "cairo-arc-private.h"
45 #include "cairo-traps-private.h"
46 #include "cairo-cogl-context-private.h"
47 #include "cairo-cogl-utils-private.h"
48 #include "cairo-box-inline.h"
49 #include "cairo-surface-subsurface-inline.h"
50 #include "cairo-surface-fallback-private.h"
51 #include "cairo-surface-offset-private.h"
53 #include "cairo-cogl.h"
55 #include <cogl/cogl2-experimental.h>
56 #include <glib.h>
58 #define CAIRO_COGL_DEBUG 0
59 //#define FILL_WITH_COGL_PATH
60 //#define USE_CAIRO_PATH_FLATTENER
61 #define ENABLE_PATH_CACHE
62 //#define DISABLE_BATCHING
63 #define USE_COGL_RECTANGLE_API
64 #define ENABLE_RECTANGLES_FASTPATH
66 #if defined (USE_COGL_RECTANGLE_API) || defined (ENABLE_PATH_CACHE)
67 #define NEED_COGL_CONTEXT
68 #endif
70 #if CAIRO_COGL_DEBUG && __GNUC__
71 #define UNSUPPORTED(reason) ({ \
72 g_warning ("cairo-cogl: hit unsupported operation: %s", reason); \
73 CAIRO_INT_STATUS_UNSUPPORTED; \
75 #else
76 #define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
77 #endif
79 #define CAIRO_COGL_PATH_META_CACHE_SIZE (1024 * 1024)
81 typedef struct _cairo_cogl_texture_attributes {
82 /* nabbed from cairo_surface_attributes_t... */
83 cairo_matrix_t matrix;
84 cairo_extend_t extend;
85 cairo_filter_t filter;
86 cairo_bool_t has_component_alpha;
88 CoglPipelineWrapMode s_wrap;
89 CoglPipelineWrapMode t_wrap;
90 } cairo_cogl_texture_attributes_t;
92 typedef enum _cairo_cogl_journal_entry_type {
93 CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE,
94 CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE,
95 CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH,
96 CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP
97 } cairo_cogl_journal_entry_type_t;
99 typedef struct _cairo_cogl_journal_entry {
100 cairo_cogl_journal_entry_type_t type;
101 } cairo_cogl_journal_entry_t;
103 typedef struct _cairo_cogl_journal_clip_entry {
104 cairo_cogl_journal_entry_t base;
105 cairo_clip_t *clip;
106 } cairo_cogl_journal_clip_entry_t;
108 typedef struct _cairo_cogl_journal_rect_entry {
109 cairo_cogl_journal_entry_t base;
110 CoglPipeline *pipeline;
111 float x;
112 float y;
113 float width;
114 float height;
115 int n_layers;
116 cairo_matrix_t ctm;
117 } cairo_cogl_journal_rect_entry_t;
119 typedef struct _cairo_cogl_journal_prim_entry {
120 cairo_cogl_journal_entry_t base;
121 CoglPipeline *pipeline;
122 CoglPrimitive *primitive;
123 gboolean has_transform;
124 cairo_matrix_t transform;
125 } cairo_cogl_journal_prim_entry_t;
127 typedef struct _cairo_cogl_journal_path_entry {
128 cairo_cogl_journal_entry_t base;
129 CoglPipeline *pipeline;
130 CoglPath *path;
131 } cairo_cogl_journal_path_entry_t;
133 typedef struct _cairo_cogl_path_fill_meta {
134 cairo_cache_entry_t cache_entry;
135 cairo_reference_count_t ref_count;
136 int counter;
137 cairo_path_fixed_t *user_path;
138 cairo_matrix_t ctm_inverse;
140 /* TODO */
141 #if 0
142 /* A cached path tessellation should be re-usable with different rotations
143 * and translations but not for different scales.
145 * one idea is to track the diagonal lenghts of a unit rectangle
146 * transformed through the original ctm use to tessellate the geometry
147 * so we can check what the lengths are for any new ctm to know if
148 * this geometry is compatible.
150 #endif
152 CoglPrimitive *prim;
153 } cairo_cogl_path_fill_meta_t;
155 typedef struct _cairo_cogl_path_stroke_meta {
156 cairo_cache_entry_t cache_entry;
157 cairo_reference_count_t ref_count;
158 int counter;
159 cairo_path_fixed_t *user_path;
160 cairo_matrix_t ctm_inverse;
161 cairo_stroke_style_t style;
162 double tolerance;
164 /* TODO */
165 #if 0
166 /* A cached path tessellation should be re-usable with different rotations
167 * and translations but not for different scales.
169 * one idea is to track the diagonal lenghts of a unit rectangle
170 * transformed through the original ctm use to tessellate the geometry
171 * so we can check what the lengths are for any new ctm to know if
172 * this geometry is compatible.
174 #endif
176 CoglPrimitive *prim;
177 } cairo_cogl_path_stroke_meta_t;
179 static cairo_surface_t *
180 _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
181 cairo_bool_t ignore_alpha,
182 CoglFramebuffer *framebuffer,
183 CoglTexture *texture);
185 static cairo_int_status_t
186 _cairo_cogl_surface_fill (void *abstract_surface,
187 cairo_operator_t op,
188 const cairo_pattern_t *source,
189 const cairo_path_fixed_t *path,
190 cairo_fill_rule_t fill_rule,
191 double tolerance,
192 cairo_antialias_t antialias,
193 const cairo_clip_t *clip);
195 static void
196 _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface);
198 cairo_private extern const cairo_surface_backend_t _cairo_cogl_surface_backend;
200 slim_hidden_proto (cairo_cogl_device_create);
201 slim_hidden_proto (cairo_cogl_surface_create);
202 slim_hidden_proto (cairo_cogl_surface_get_framebuffer);
203 slim_hidden_proto (cairo_cogl_surface_get_texture);
204 slim_hidden_proto (cairo_cogl_surface_end_frame);
206 static cairo_cogl_device_t *
207 to_device (cairo_device_t *device)
209 return (cairo_cogl_device_t *)device;
212 /* moves trap points such that they become the actual corners of the trapezoid */
213 static void
214 _sanitize_trap (cairo_trapezoid_t *t)
216 cairo_trapezoid_t s = *t;
218 #define FIX(lr, tb, p) \
219 if (t->lr.p.y != t->tb) { \
220 t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \
221 t->lr.p.y = s.tb; \
223 FIX (left, top, p1);
224 FIX (left, bottom, p2);
225 FIX (right, top, p1);
226 FIX (right, bottom, p2);
229 static cairo_status_t
230 _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
232 GError *error = NULL;
234 if (surface->framebuffer)
235 return CAIRO_STATUS_SUCCESS;
237 surface->framebuffer = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (surface->texture));
238 if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) {
239 g_error_free (error);
240 cogl_object_unref (surface->framebuffer);
241 surface->framebuffer = NULL;
242 return CAIRO_STATUS_NO_MEMORY;
245 cogl_push_framebuffer (surface->framebuffer);
246 cogl_ortho (0, surface->width,
247 surface->height, 0,
248 -1, 100);
249 cogl_pop_framebuffer ();
251 return CAIRO_STATUS_SUCCESS;
254 static cairo_surface_t *
255 _cairo_cogl_surface_create_similar (void *abstract_surface,
256 cairo_content_t content,
257 int width,
258 int height)
260 cairo_cogl_surface_t *reference_surface = abstract_surface;
261 cairo_cogl_surface_t *surface;
262 CoglTexture *texture;
263 cairo_status_t status;
265 texture = cogl_texture_new_with_size (width, height,
266 COGL_TEXTURE_NO_SLICING,
267 (content & CAIRO_CONTENT_COLOR) ?
268 COGL_PIXEL_FORMAT_BGRA_8888_PRE :
269 COGL_PIXEL_FORMAT_A_8);
270 if (!texture)
271 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
273 surface = (cairo_cogl_surface_t *)
274 _cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
275 (content & CAIRO_CONTENT_ALPHA) == 0,
276 NULL,
277 texture);
278 if (unlikely (surface->base.status))
279 return &surface->base;
281 status = _cairo_cogl_surface_ensure_framebuffer (surface);
282 if (unlikely (status)) {
283 cairo_surface_destroy (&surface->base);
284 return _cairo_surface_create_in_error (status);
287 return &surface->base;
290 static cairo_bool_t
291 _cairo_cogl_surface_get_extents (void *abstract_surface,
292 cairo_rectangle_int_t *extents)
294 cairo_cogl_surface_t *surface = abstract_surface;
296 extents->x = 0;
297 extents->y = 0;
298 extents->width = surface->width;
299 extents->height = surface->height;
301 return TRUE;
304 static void
305 _cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
307 GList *l;
309 for (l = surface->journal->head; l; l = l->next) {
310 cairo_cogl_journal_entry_t *entry = l->data;
312 if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE) {
313 cairo_cogl_journal_prim_entry_t *prim_entry =
314 (cairo_cogl_journal_prim_entry_t *)entry;
315 cogl_object_unref (prim_entry->primitive);
316 } else if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH) {
317 cairo_cogl_journal_path_entry_t *path_entry =
318 (cairo_cogl_journal_path_entry_t *)entry;
319 cogl_object_unref (path_entry->path);
323 g_queue_free (surface->journal);
324 surface->journal = NULL;
327 #ifdef FILL_WITH_COGL_PATH
328 static void
329 _cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface,
330 CoglPipeline *pipeline,
331 CoglPath *path)
333 cairo_cogl_journal_path_entry_t *entry;
335 if (unlikely (surface->journal == NULL))
336 surface->journal = g_queue_new ();
338 /* FIXME: Instead of a GList here we should stack allocate the journal
339 * entries so it would be cheaper to allocate and they can all be freed in
340 * one go after flushing! */
341 entry = g_slice_new (cairo_cogl_journal_path_entry_t);
342 entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH;
344 entry->pipeline = cogl_object_ref (pipeline);
345 entry->path = cogl_object_ref (path);
347 g_queue_push_tail (surface->journal, entry);
349 #ifdef DISABLE_BATCHING
350 _cairo_cogl_journal_flush (surface);
351 #endif
353 #endif /* FILL_WITH_COGL_PATH */
355 static void
356 _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
357 CoglPipeline *pipeline,
358 CoglPrimitive *primitive,
359 cairo_matrix_t *transform)
361 cairo_cogl_journal_prim_entry_t *entry;
363 if (unlikely (surface->journal == NULL))
364 surface->journal = g_queue_new ();
366 /* FIXME: Instead of a GList here we should stack allocate the journal
367 * entries so it would be cheaper to allocate and they can all be freed in
368 * one go after flushing! */
369 entry = g_slice_new (cairo_cogl_journal_prim_entry_t);
370 entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE;
372 entry->pipeline = cogl_object_ref (pipeline);
374 if (transform) {
375 entry->transform = *transform;
376 entry->has_transform = TRUE;
377 } else
378 entry->has_transform = FALSE;
380 entry->primitive = cogl_object_ref (primitive);
382 g_queue_push_tail (surface->journal, entry);
384 #ifdef DISABLE_BATCHING
385 _cairo_cogl_journal_flush (surface);
386 #endif
389 static void
390 _cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface,
391 CoglPipeline *pipeline,
392 float x,
393 float y,
394 float width,
395 float height,
396 int n_layers,
397 cairo_matrix_t *ctm)
399 cairo_cogl_journal_rect_entry_t *entry;
401 if (unlikely (surface->journal == NULL))
402 surface->journal = g_queue_new ();
404 /* FIXME: Instead of a GList here we should stack allocate the journal
405 * entries so it would be cheaper to allocate and they can all be freed in
406 * one go after flushing! */
407 entry = g_slice_new (cairo_cogl_journal_rect_entry_t);
408 entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE;
410 entry->pipeline = cogl_object_ref (pipeline);
412 entry->x = x;
413 entry->y = y;
414 entry->width = width;
415 entry->height = height;
416 entry->ctm = *ctm;
418 entry->n_layers = n_layers;
420 g_queue_push_tail (surface->journal, entry);
422 #ifdef DISABLE_BATCHING
423 _cairo_cogl_journal_flush (surface);
424 #endif
427 static void
428 _cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface,
429 const cairo_clip_t *clip)
431 cairo_cogl_journal_clip_entry_t *entry;
433 if (unlikely (surface->journal == NULL))
434 surface->journal = g_queue_new ();
436 /* FIXME: Instead of a GList here we should stack allocate the journal
437 * entries so it would be cheaper to allocate and they can all be freed in
438 * one go after flushing! */
439 entry = g_slice_new (cairo_cogl_journal_clip_entry_t);
440 entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP;
441 entry->clip = _cairo_clip_copy (clip);
443 g_queue_push_tail (surface->journal, entry);
446 static void
447 _cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
449 GList *l;
451 if (!surface->journal) {
452 assert (surface->last_clip == NULL);
453 return;
456 if (surface->buffer_stack && surface->buffer_stack_offset) {
457 cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
458 cogl_object_unref (surface->buffer_stack);
459 surface->buffer_stack = NULL;
462 for (l = surface->journal->head; l; l = l->next) {
463 cairo_cogl_journal_entry_t *entry = l->data;
464 gsize entry_size;
466 switch (entry->type)
468 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
469 cairo_cogl_journal_clip_entry_t *clip_entry =
470 (cairo_cogl_journal_clip_entry_t *)entry;
471 _cairo_clip_destroy (clip_entry->clip);
472 entry_size = sizeof (cairo_cogl_journal_clip_entry_t);
473 break;
475 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
476 cairo_cogl_journal_rect_entry_t *rect_entry =
477 (cairo_cogl_journal_rect_entry_t *)entry;
478 cogl_object_unref (rect_entry->pipeline);
479 entry_size = sizeof (cairo_cogl_journal_rect_entry_t);
480 break;
482 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
483 cairo_cogl_journal_prim_entry_t *prim_entry =
484 (cairo_cogl_journal_prim_entry_t *)entry;
485 cogl_object_unref (prim_entry->pipeline);
486 cogl_object_unref (prim_entry->primitive);
487 entry_size = sizeof (cairo_cogl_journal_prim_entry_t);
488 break;
490 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
491 cairo_cogl_journal_path_entry_t *path_entry =
492 (cairo_cogl_journal_path_entry_t *)entry;
493 cogl_object_unref (path_entry->pipeline);
494 cogl_object_unref (path_entry->path);
495 entry_size = sizeof (cairo_cogl_journal_path_entry_t);
496 break;
498 default:
499 assert (0); /* not reached! */
500 entry_size = 0; /* avoid compiler warning */
502 g_slice_free1 (entry_size, entry);
505 g_queue_clear (surface->journal);
507 if (surface->last_clip) {
508 _cairo_clip_destroy (surface->last_clip);
509 surface->last_clip = NULL;
513 static CoglAttributeBuffer *
514 _cairo_cogl_surface_allocate_buffer_space (cairo_cogl_surface_t *surface,
515 size_t size,
516 size_t *offset,
517 void **pointer)
519 /* XXX: In the Cogl journal we found it more efficient to have a pool of
520 * buffers that we re-cycle but for now we simply thow away our stack
521 * buffer each time we flush. */
522 if (unlikely (surface->buffer_stack &&
523 (surface->buffer_stack_size - surface->buffer_stack_offset) < size)) {
524 cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
525 cogl_object_unref (surface->buffer_stack);
526 surface->buffer_stack = NULL;
527 surface->buffer_stack_size *= 2;
530 if (unlikely (surface->buffer_stack_size < size))
531 surface->buffer_stack_size = size * 2;
533 if (unlikely (surface->buffer_stack == NULL)) {
534 surface->buffer_stack = cogl_attribute_buffer_new (surface->buffer_stack_size, NULL);
535 surface->buffer_stack_pointer =
536 cogl_buffer_map (COGL_BUFFER (surface->buffer_stack),
537 COGL_BUFFER_ACCESS_WRITE,
538 COGL_BUFFER_MAP_HINT_DISCARD);
539 surface->buffer_stack_offset = 0;
542 *pointer = surface->buffer_stack_pointer + surface->buffer_stack_offset;
543 *offset = surface->buffer_stack_offset;
545 surface->buffer_stack_offset += size;
546 return cogl_object_ref (surface->buffer_stack);
550 static CoglAttributeBuffer *
551 _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
552 cairo_traps_t *traps,
553 size_t *offset,
554 gboolean one_shot)
556 CoglAttributeBuffer *buffer;
557 int n_traps = traps->num_traps;
558 int i;
559 CoglVertexP2 *triangles;
561 if (one_shot) {
562 buffer = _cairo_cogl_surface_allocate_buffer_space (surface,
563 n_traps * sizeof (CoglVertexP2) * 6,
564 offset,
565 (void **)&triangles);
566 if (!buffer)
567 return NULL;
568 } else {
569 buffer = cogl_attribute_buffer_new (n_traps * sizeof (CoglVertexP2) * 6, NULL);
570 if (!buffer)
571 return NULL;
572 triangles = cogl_buffer_map (COGL_BUFFER (buffer),
573 COGL_BUFFER_ACCESS_WRITE,
574 COGL_BUFFER_MAP_HINT_DISCARD);
575 if (!triangles)
576 return NULL;
577 *offset = 0;
580 /* XXX: This is can be very expensive. I'm not sure a.t.m if it's
581 * predominantly the bandwidth required or the cost of the fixed_to_float
582 * conversions but either way we should try using an index buffer to
583 * reduce the amount we upload by 1/3 (offset by allocating and uploading
584 * indices though) sadly though my experience with the intel mesa drivers
585 * is that slow paths can easily be hit when starting to use indices.
587 for (i = 0; i < n_traps; i++)
589 CoglVertexP2 *p = &triangles[i * 6];
590 cairo_trapezoid_t *trap = &traps->traps[i];
592 p[0].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x);
593 p[0].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y);
595 p[1].x = _cairo_cogl_util_fixed_to_float (trap->left.p2.x);
596 p[1].y = _cairo_cogl_util_fixed_to_float (trap->left.p2.y);
598 p[2].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x);
599 p[2].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y);
601 p[3].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x);
602 p[3].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y);
604 p[4].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x);
605 p[4].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y);
607 p[5].x = _cairo_cogl_util_fixed_to_float (trap->right.p1.x);
608 p[5].y = _cairo_cogl_util_fixed_to_float (trap->right.p1.y);
611 if (!one_shot)
612 cogl_buffer_unmap (COGL_BUFFER (buffer));
614 return buffer;
617 /* Used for solid fills, in this case we just need a mesh made of
618 * a single (2-component) position attribute. */
619 static CoglPrimitive *
620 _cairo_cogl_traps_to_composite_prim_p2 (cairo_cogl_surface_t *surface,
621 cairo_traps_t *traps,
622 gboolean one_shot)
624 size_t offset;
625 CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot);
626 CoglAttribute *pos = cogl_attribute_new (buffer,
627 "cogl_position_in",
628 sizeof (CoglVertexP2),
629 offset,
631 COGL_ATTRIBUTE_TYPE_FLOAT);
632 CoglPrimitive *prim;
634 /* The attribute will have taken a reference on the buffer */
635 cogl_object_unref (buffer);
637 prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
638 traps->num_traps * 6, pos, NULL);
640 /* The primitive will now keep the attribute alive... */
641 cogl_object_unref (pos);
643 return prim;
646 /* Used for surface fills, in this case we need a mesh made of a single
647 * (2-component) position attribute + we also alias the same attribute as
648 * (2-component) texture coordinates */
649 static CoglPrimitive *
650 _cairo_cogl_traps_to_composite_prim_p2t2 (cairo_cogl_surface_t *surface,
651 cairo_traps_t *traps,
652 gboolean one_shot)
654 size_t offset;
655 CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot);
656 CoglAttribute *pos = cogl_attribute_new (buffer,
657 "cogl_position_in",
658 sizeof (CoglVertexP2),
659 offset,
661 COGL_ATTRIBUTE_TYPE_FLOAT);
662 CoglAttribute *tex_coords = cogl_attribute_new (buffer,
663 "cogl_tex_coord0_in",
664 sizeof (CoglVertexP2),
667 COGL_ATTRIBUTE_TYPE_FLOAT);
668 CoglPrimitive *prim;
670 /* The attributes will have taken references on the buffer */
671 cogl_object_unref (buffer);
673 prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
674 traps->num_traps * 6, pos, tex_coords, NULL);
676 /* The primitive will now keep the attributes alive... */
677 cogl_object_unref (pos);
678 cogl_object_unref (tex_coords);
680 return prim;
683 static CoglPrimitive *
684 _cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface,
685 cairo_traps_t *traps,
686 int n_layers,
687 gboolean one_shot)
689 int n_traps = traps->num_traps;
690 int i;
692 /* XXX: Ideally we would skip tessellating to traps entirely since
693 * given their representation, conversion to triangles is quite expensive.
695 * This simplifies the conversion to triangles by making the end points of
696 * the two side lines actually just correspond to the corners of the
697 * traps.
699 for (i = 0; i < n_traps; i++)
700 _sanitize_trap (&traps->traps[i]);
702 if (n_layers == 0)
703 return _cairo_cogl_traps_to_composite_prim_p2 (surface, traps, one_shot);
704 else {
705 assert (n_layers == 1);
706 return _cairo_cogl_traps_to_composite_prim_p2t2 (surface, traps, one_shot);
710 static cairo_int_status_t
711 _cairo_cogl_fill_to_primitive (cairo_cogl_surface_t *surface,
712 const cairo_path_fixed_t *path,
713 cairo_fill_rule_t fill_rule,
714 double tolerance,
715 int n_layers,
716 cairo_bool_t one_shot,
717 CoglPrimitive **primitive,
718 size_t *size)
720 cairo_traps_t traps;
721 cairo_int_status_t status;
723 _cairo_traps_init (&traps);
724 status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps);
725 if (unlikely (status))
726 goto BAIL;
728 if (traps.num_traps == 0) {
729 status = CAIRO_INT_STATUS_NOTHING_TO_DO;
730 goto BAIL;
733 *size = traps.num_traps * sizeof (CoglVertexP2) * 6;
735 *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot);
736 if (!*primitive) {
737 status = CAIRO_INT_STATUS_NO_MEMORY;
738 goto BAIL;
741 BAIL:
742 _cairo_traps_fini (&traps);
743 return status;
746 static void
747 _cairo_cogl_clip_push_box (const cairo_box_t *box)
749 if (_cairo_box_is_pixel_aligned (box)) {
750 cairo_rectangle_int_t rect;
751 _cairo_box_round_to_rectangle (box, &rect);
752 cogl_clip_push_window_rectangle (rect.x, rect.y,
753 rect.width, rect.height);
754 } else {
755 double x1, y1, x2, y2;
756 _cairo_box_to_doubles (box, &x1, &y1, &x2, &y2);
757 cogl_clip_push_rectangle (x1, y1, x2, y2);
761 static void
762 _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
764 GList *l;
765 int clip_stack_depth = 0;
766 int i;
768 if (!surface->journal)
769 return;
771 if (surface->buffer_stack && surface->buffer_stack_offset) {
772 cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
773 cogl_object_unref (surface->buffer_stack);
774 surface->buffer_stack = NULL;
777 cogl_set_framebuffer (surface->framebuffer);
779 cogl_push_matrix ();
781 for (l = surface->journal->head; l; l = l->next) {
782 cairo_cogl_journal_entry_t *entry = l->data;
784 switch (entry->type)
786 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
787 cairo_cogl_journal_clip_entry_t *clip_entry =
788 (cairo_cogl_journal_clip_entry_t *)entry;
789 cairo_clip_path_t *path;
790 #if 0
791 cairo_bool_t checked_for_primitives = FALSE;
792 cairo_cogl_clip_primitives_t *clip_primitives;
793 #endif
795 for (i = 0; i < clip_stack_depth; i++)
796 cogl_clip_pop ();
797 clip_stack_depth = 0;
799 for (path = clip_entry->clip->path, i = 0; path; path = path->prev, i++) {
800 cairo_rectangle_int_t extents;
801 cairo_int_status_t status;
802 CoglPrimitive *prim;
803 size_t prim_size;
805 _cairo_path_fixed_approximate_clip_extents (&path->path, &extents);
807 /* TODO - maintain a fifo of the last 10 used clips with cached
808 * primitives to see if we can avoid tessellating the path and
809 * uploading the vertices...
811 #if 0
812 if (!checked_for_primitives) {
813 clip_primitives = find_clip_primitives (clip);
814 checked_for_primitives = TRUE;
816 if (clip_primitives)
817 prim = clip_primitives->primitives[i];
818 #endif
819 status = _cairo_cogl_fill_to_primitive (surface,
820 &path->path,
821 path->fill_rule,
822 path->tolerance,
824 TRUE,
825 &prim,
826 &prim_size);
827 if (unlikely (status)) {
828 g_warning ("Failed to get primitive for clip path while flushing journal");
829 continue;
831 clip_stack_depth++;
832 cogl_clip_push_primitive (prim,
833 extents.x, extents.y,
834 extents.x + extents.width,
835 extents.y + extents.height);
836 cogl_object_unref (prim);
839 for (i = 0; i < clip_entry->clip->num_boxes; i++) {
840 clip_stack_depth++;
841 _cairo_cogl_clip_push_box (&clip_entry->clip->boxes[i]);
844 surface->n_clip_updates_per_frame++;
845 break;
847 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
848 cairo_cogl_journal_rect_entry_t *rect_entry =
849 (cairo_cogl_journal_rect_entry_t *)entry;
850 float tex_coords[8];
851 float x1 = rect_entry->x;
852 float y1 = rect_entry->y;
853 float x2 = rect_entry->x + rect_entry->width;
854 float y2 = rect_entry->y + rect_entry->height;
855 cairo_matrix_t *ctm = &rect_entry->ctm;
856 float ctmfv[16] = {
857 ctm->xx, ctm->yx, 0, 0,
858 ctm->xy, ctm->yy, 0, 0,
859 0, 0, 1, 0,
860 ctm->x0, ctm->y0, 0, 1
862 CoglMatrix transform;
864 cogl_matrix_init_from_array (&transform, ctmfv);
866 if (rect_entry->n_layers) {
867 g_assert (rect_entry->n_layers <= 2);
868 tex_coords[0] = x1;
869 tex_coords[1] = y1;
870 tex_coords[2] = x2;
871 tex_coords[3] = y2;
872 if (rect_entry->n_layers > 1)
873 memcpy (&tex_coords[4], tex_coords, sizeof (float) * 4);
876 cogl_set_source (rect_entry->pipeline);
877 cogl_push_matrix ();
878 cogl_transform (&transform);
879 cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2,
880 tex_coords, 4 * rect_entry->n_layers);
881 cogl_pop_matrix ();
882 break;
884 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
885 cairo_cogl_journal_prim_entry_t *prim_entry =
886 (cairo_cogl_journal_prim_entry_t *)entry;
887 CoglMatrix transform;
889 cogl_push_matrix ();
890 if (prim_entry->has_transform) {
891 cairo_matrix_t *ctm = &prim_entry->transform;
892 float ctmfv[16] = {
893 ctm->xx, ctm->yx, 0, 0,
894 ctm->xy, ctm->yy, 0, 0,
895 0, 0, 1, 0,
896 ctm->x0, ctm->y0, 0, 1
898 cogl_matrix_init_from_array (&transform, ctmfv);
899 cogl_transform (&transform);
900 } else {
901 cogl_matrix_init_identity (&transform);
902 cogl_set_modelview_matrix (&transform);
905 cogl_set_source (prim_entry->pipeline);
906 cogl_primitive_draw (prim_entry->primitive);
907 cogl_pop_matrix ();
908 break;
910 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
911 cairo_cogl_journal_path_entry_t *path_entry =
912 (cairo_cogl_journal_path_entry_t *)entry;
914 cogl_set_source (path_entry->pipeline);
915 cogl_path_fill (path_entry->path);
916 break;
918 default:
919 assert (0); /* not reached! */
923 cogl_pop_matrix ();
925 for (i = 0; i < clip_stack_depth; i++)
926 cogl_clip_pop ();
928 _cairo_cogl_journal_discard (surface);
931 static cairo_status_t
932 _cairo_cogl_surface_flush (void *abstract_surface,
933 unsigned flags)
935 cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
937 if (flags)
938 return CAIRO_STATUS_SUCCESS;
940 _cairo_cogl_journal_flush (surface);
942 return CAIRO_STATUS_SUCCESS;
945 static cairo_status_t
946 _cairo_cogl_surface_finish (void *abstract_surface)
948 cairo_cogl_surface_t *surface = abstract_surface;
950 if (surface->texture)
951 cogl_object_unref (surface->texture);
953 if (surface->framebuffer)
954 cogl_object_unref (surface->framebuffer);
956 if (surface->journal)
957 _cairo_cogl_journal_free (surface);
959 /*XXX wtf */
960 cairo_device_release (surface->base.device);
962 return CAIRO_STATUS_SUCCESS;
965 static CoglPixelFormat
966 get_cogl_format_from_cairo_format (cairo_format_t cairo_format);
968 /* XXX: We often use RGBA format for onscreen framebuffers so make sure
969 * to handle CAIRO_FORMAT_INVALID sensibly */
970 static cairo_format_t
971 get_cairo_format_from_cogl_format (CoglPixelFormat format)
973 switch ((int)format)
975 case COGL_PIXEL_FORMAT_A_8:
976 return CAIRO_FORMAT_A8;
977 case COGL_PIXEL_FORMAT_RGB_565:
978 return CAIRO_FORMAT_RGB16_565;
980 case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
981 case COGL_PIXEL_FORMAT_ARGB_8888_PRE:
982 case COGL_PIXEL_FORMAT_RGBA_8888_PRE:
983 /* Note: this is ambiguous since CAIRO_FORMAT_RGB24
984 * would also map to the same CoglPixelFormat */
985 return CAIRO_FORMAT_ARGB32;
987 default:
988 g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d\n",
989 format,
990 format & COGL_A_BIT,
991 format & COGL_BGR_BIT,
992 format & COGL_PREMULT_BIT,
993 format & ~(COGL_A_BIT | COGL_BGR_BIT | COGL_PREMULT_BIT));
994 return CAIRO_FORMAT_INVALID;
998 static CoglPixelFormat
999 get_cogl_format_from_cairo_format (cairo_format_t cairo_format)
1001 switch (cairo_format)
1003 case CAIRO_FORMAT_ARGB32:
1004 case CAIRO_FORMAT_RGB24:
1005 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
1006 return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
1007 #else
1008 return COGL_PIXEL_FORMAT_ARGB_8888_PRE;
1009 #endif
1010 case CAIRO_FORMAT_A8:
1011 return COGL_PIXEL_FORMAT_A_8;
1012 case CAIRO_FORMAT_RGB16_565:
1013 return COGL_PIXEL_FORMAT_RGB_565;
1014 case CAIRO_FORMAT_INVALID:
1015 case CAIRO_FORMAT_A1:
1016 case CAIRO_FORMAT_RGB30:
1017 return 0;
1020 g_warn_if_reached ();
1021 return 0;
1024 static cairo_status_t
1025 _cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t *surface,
1026 cairo_rectangle_int_t *interest,
1027 cairo_image_surface_t **image_out)
1029 cairo_image_surface_t *image;
1030 cairo_status_t status;
1031 cairo_format_t cairo_format;
1032 CoglPixelFormat cogl_format;
1034 /* TODO: Add cogl_texture_get_region() API so we don't have to ensure the
1035 * surface is bound to an fbo to read back pixels */
1036 status = _cairo_cogl_surface_ensure_framebuffer (surface);
1037 if (unlikely (status))
1038 return status;
1040 cairo_format = get_cairo_format_from_cogl_format (surface->cogl_format);
1041 if (cairo_format == CAIRO_FORMAT_INVALID) {
1042 cairo_format = CAIRO_FORMAT_ARGB32;
1043 cogl_format = get_cogl_format_from_cairo_format (cairo_format);
1044 } else {
1045 cogl_format = cogl_framebuffer_get_color_format (surface->framebuffer);
1048 image = (cairo_image_surface_t *)
1049 cairo_image_surface_create (cairo_format, surface->width, surface->height);
1050 if (image->base.status)
1051 return image->base.status;
1053 /* TODO: Add cogl_framebuffer_read_pixels() API */
1054 cogl_push_framebuffer (surface->framebuffer);
1055 cogl_read_pixels (0, 0, surface->width, surface->height,
1056 COGL_READ_PIXELS_COLOR_BUFFER,
1057 cogl_format,
1058 image->data);
1059 cogl_pop_framebuffer ();
1061 *image_out = image;
1063 return CAIRO_STATUS_SUCCESS;
1066 static cairo_status_t
1067 _cairo_cogl_surface_acquire_source_image (void *abstract_surface,
1068 cairo_image_surface_t **image_out,
1069 void **image_extra)
1071 cairo_cogl_surface_t *surface = abstract_surface;
1072 cairo_status_t status;
1074 if (surface->texture) {
1075 cairo_format_t format = get_cairo_format_from_cogl_format (surface->cogl_format);
1076 cairo_image_surface_t *image = (cairo_image_surface_t *)
1077 cairo_image_surface_create (format, surface->width, surface->height);
1078 if (image->base.status)
1079 return image->base.status;
1081 cogl_texture_get_data (surface->texture,
1082 cogl_texture_get_format (surface->texture),
1084 image->data);
1086 image->base.is_clear = FALSE;
1087 *image_out = image;
1088 } else {
1089 cairo_rectangle_int_t extents = {
1090 0, 0, surface->width, surface->height
1092 status = _cairo_cogl_surface_read_rect_to_image_surface (surface, &extents,
1093 image_out);
1094 if (unlikely (status))
1095 return status;
1098 *image_extra = NULL;
1100 return CAIRO_STATUS_SUCCESS;
1103 static void
1104 _cairo_cogl_surface_release_source_image (void *abstract_surface,
1105 cairo_image_surface_t *image,
1106 void *image_extra)
1108 cairo_surface_destroy (&image->base);
1111 static cairo_status_t
1112 _cairo_cogl_surface_clear (cairo_cogl_surface_t *surface,
1113 const cairo_color_t *color)
1115 /* Anything batched in the journal up until now is redundant... */
1116 _cairo_cogl_journal_discard (surface);
1118 /* XXX: we currently implicitly clear the depth and stencil buffer here
1119 * but since we use the framebuffer_discard extension when available I
1120 * suppose this doesn't matter too much.
1122 * The main concern is that we want to avoid re-loading an external z
1123 * buffer at the start of each frame, but also many gpu architectures have
1124 * optimizations for how they handle the depth/stencil buffers and can get
1125 * upset if they aren't cleared together at the start of the frame.
1127 * FIXME: we need a way to assert that the clip stack currently isn't
1128 * using the stencil buffer before clearing it here!
1130 cogl_framebuffer_clear4f (surface->framebuffer,
1131 COGL_BUFFER_BIT_COLOR |
1132 COGL_BUFFER_BIT_DEPTH |
1133 COGL_BUFFER_BIT_STENCIL,
1134 color->red * color->alpha,
1135 color->green * color->alpha,
1136 color->blue * color->alpha,
1137 color->alpha);
1138 return CAIRO_STATUS_SUCCESS;
1141 cairo_status_t
1142 _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
1143 cairo_fixed_t x,
1144 cairo_fixed_t y,
1145 cairo_fixed_t width,
1146 cairo_fixed_t height)
1148 cairo_status_t status;
1150 status = _cairo_path_fixed_move_to (path, x, y);
1151 if (unlikely (status))
1152 return status;
1154 status = _cairo_path_fixed_rel_line_to (path, width, 0);
1155 if (unlikely (status))
1156 return status;
1158 status = _cairo_path_fixed_rel_line_to (path, 0, height);
1159 if (unlikely (status))
1160 return status;
1162 status = _cairo_path_fixed_rel_line_to (path, -width, 0);
1163 if (unlikely (status))
1164 return status;
1166 status = _cairo_path_fixed_close_path (path);
1167 if (unlikely (status))
1168 return status;
1170 return CAIRO_STATUS_SUCCESS;
1173 static cairo_int_status_t
1174 _cairo_cogl_surface_paint (void *abstract_surface,
1175 cairo_operator_t op,
1176 const cairo_pattern_t *source,
1177 const cairo_clip_t *clip)
1179 cairo_cogl_surface_t *surface;
1180 cairo_path_fixed_t path;
1181 cairo_status_t status;
1182 cairo_matrix_t identity;
1184 if (clip == NULL) {
1185 if (op == CAIRO_OPERATOR_CLEAR)
1186 return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
1187 else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
1188 (op == CAIRO_OPERATOR_SOURCE ||
1189 (op == CAIRO_OPERATOR_OVER && (((cairo_surface_t *)abstract_surface)->is_clear || _cairo_pattern_is_opaque_solid (source))))) {
1190 return _cairo_cogl_surface_clear (abstract_surface,
1191 &((cairo_solid_pattern_t *) source)->color);
1195 /* fallback to handling the paint in terms of a fill... */
1197 surface = abstract_surface;
1199 _cairo_path_fixed_init (&path);
1201 status = _cairo_cogl_path_fixed_rectangle (&path, 0, 0, surface->width, surface->height);
1202 if (unlikely (status))
1203 goto BAIL;
1205 #ifdef NEED_COGL_CONTEXT
1206 /* XXX: in cairo-cogl-context.c we set some sideband data on the
1207 * surface before issuing a fill so we need to do that here too... */
1208 surface->user_path = &path;
1209 cairo_matrix_init_identity (&identity);
1210 surface->ctm = &identity;
1211 surface->ctm_inverse = &identity;
1212 surface->path_is_rectangle = TRUE;
1213 surface->path_rectangle_x = 0;
1214 surface->path_rectangle_y = 0;
1215 surface->path_rectangle_width = surface->width;
1216 surface->path_rectangle_height = surface->height;
1217 #endif
1219 status = _cairo_cogl_surface_fill (abstract_surface,
1221 source,
1222 &path,
1223 CAIRO_FILL_RULE_WINDING,
1225 CAIRO_ANTIALIAS_DEFAULT,
1226 clip);
1227 BAIL:
1228 _cairo_path_fixed_fini (&path);
1229 return status;
1232 static CoglPipelineWrapMode
1233 get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
1235 switch (extend_mode)
1237 case CAIRO_EXTEND_NONE:
1238 return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
1239 case CAIRO_EXTEND_PAD:
1240 return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
1241 case CAIRO_EXTEND_REPEAT:
1242 return COGL_PIPELINE_WRAP_MODE_REPEAT;
1243 case CAIRO_EXTEND_REFLECT:
1244 /* TODO: return COGL_PIPELINE_WRAP_MODE_MIRROR; */
1245 return CAIRO_EXTEND_REPEAT;
1247 assert (0); /* not reached */
1248 return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
1251 #if 0
1252 /* Given an arbitrary texture, check if it's already a pot texture and simply
1253 * return it back if so. If not create a new pot texture, scale the old to
1254 * fill it, unref the old and return a pointer to the new pot texture. */
1255 static cairo_int_status_t
1256 _cairo_cogl_get_pot_texture (CoglContext *context,
1257 CoglTexture *texture,
1258 CoglTexture **pot_texture)
1260 int width = cogl_texture_get_width (texture);
1261 int height = cogl_texture_get_height (texture);
1262 int pot_width;
1263 int pot_height;
1264 CoglHandle offscreen = NULL;
1265 CoglTexture2D *pot = NULL;
1266 GError *error;
1268 pot_width = _cairo_cogl_util_next_p2 (width);
1269 pot_height = _cairo_cogl_util_next_p2 (height);
1271 if (pot_width == width && pot_height == height)
1272 return CAIRO_INT_STATUS_SUCCESS;
1274 for (;;) {
1275 error = NULL;
1276 pot = cogl_texture_2d_new_with_size (context,
1277 pot_width,
1278 pot_height,
1279 cogl_texture_get_format (texture),
1280 &error);
1281 if (pot)
1282 break;
1283 else
1284 g_error_free (error);
1286 if (pot_width > pot_height)
1287 pot_width >>= 1;
1288 else
1289 pot_height >>= 1;
1291 if (!pot_width || !pot_height)
1292 break;
1295 *pot_texture = COGL_TEXTURE (pot);
1297 if (!pot)
1298 return CAIRO_INT_STATUS_NO_MEMORY;
1300 /* Use the GPU to do a bilinear filtered scale from npot to pot... */
1301 offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (pot));
1302 error = NULL;
1303 if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (offscreen), &error)) {
1304 /* NB: if we don't pass an error then Cogl is allowed to simply abort
1305 * automatically. */
1306 g_error_free (error);
1307 cogl_object_unref (pot);
1308 *pot_texture = NULL;
1309 return CAIRO_INT_STATUS_NO_MEMORY;
1312 cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen));
1313 cogl_set_source_texture (texture);
1314 cogl_rectangle (-1, 1, 1, -1);
1315 cogl_pop_framebuffer ();
1317 cogl_object_unref (offscreen);
1319 #endif
1321 /* NB: a reference for the texture is transferred to the caller which should
1322 * be unrefed */
1323 static CoglTexture *
1324 _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t *reference_surface,
1325 cairo_surface_t *abstract_surface)
1327 cairo_image_surface_t *image;
1328 cairo_image_surface_t *acquired_image = NULL;
1329 void *image_extra;
1330 CoglPixelFormat format;
1331 cairo_image_surface_t *image_clone = NULL;
1332 CoglTexture2D *texture;
1333 GError *error = NULL;
1334 cairo_surface_t *clone;
1336 if (abstract_surface->device == reference_surface->base.device) {
1337 cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
1338 _cairo_cogl_surface_flush (surface, 0);
1339 return surface->texture ? cogl_object_ref (surface->texture) : NULL;
1342 if (abstract_surface->type == CAIRO_SURFACE_TYPE_COGL) {
1343 if (_cairo_surface_is_subsurface (abstract_surface)) {
1344 cairo_cogl_surface_t *surface;
1346 surface = (cairo_cogl_surface_t *)
1347 _cairo_surface_subsurface_get_target (abstract_surface);
1348 if (surface->base.device == reference_surface->base.device)
1349 return surface->texture ? cogl_object_ref (surface->texture) : NULL;
1353 clone = _cairo_surface_has_snapshot (abstract_surface, &_cairo_cogl_surface_backend);
1354 if (clone) {
1355 cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)clone;
1356 return surface->texture ? cogl_object_ref (surface->texture) : NULL;
1359 g_warning ("Uploading image surface to texture");
1361 if (_cairo_surface_is_image (abstract_surface)) {
1362 image = (cairo_image_surface_t *)abstract_surface;
1363 } else {
1364 cairo_status_t status = _cairo_surface_acquire_source_image (abstract_surface,
1365 &acquired_image, &image_extra);
1366 if (unlikely (status)) {
1367 g_warning ("acquire_source_image failed: %s [%d]\n",
1368 cairo_status_to_string (status), status);
1369 return NULL;
1371 image = acquired_image;
1374 format = get_cogl_format_from_cairo_format (image->format);
1375 if (!format)
1377 image_clone = _cairo_image_surface_coerce (image);
1378 if (unlikely (image_clone->base.status)) {
1379 g_warning ("image_surface_coerce failed");
1380 texture = NULL;
1381 goto BAIL;
1384 format = get_cogl_format_from_cairo_format (image_clone->format);
1385 assert (format);
1388 texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context,
1389 image->width,
1390 image->height,
1391 format, /* incoming */
1392 format, /* desired */
1393 image->stride,
1394 image->data,
1395 &error);
1396 if (!texture) {
1397 g_warning ("Failed to allocate texture: %s", error->message);
1398 g_error_free (error);
1399 goto BAIL;
1402 clone = _cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
1403 reference_surface->ignore_alpha,
1404 NULL, COGL_TEXTURE (texture));
1406 _cairo_surface_attach_snapshot (abstract_surface, clone, NULL);
1408 /* Attaching the snapshot will take a reference on the clone surface... */
1409 cairo_surface_destroy (clone);
1411 BAIL:
1412 if (image_clone)
1413 cairo_surface_destroy (&image_clone->base);
1414 if (acquired_image)
1415 _cairo_surface_release_source_image (abstract_surface, acquired_image, image_extra);
1417 return COGL_TEXTURE (texture);
1420 /* NB: a reference for the texture is transferred to the caller which should
1421 * be unrefed */
1422 static CoglTexture *
1423 _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
1424 cairo_cogl_surface_t *destination,
1425 const cairo_rectangle_int_t *extents,
1426 const cairo_rectangle_int_t *sample,
1427 cairo_cogl_texture_attributes_t *attributes)
1429 CoglTexture *texture = NULL;
1431 switch ((int)pattern->type)
1433 case CAIRO_PATTERN_TYPE_SURFACE: {
1434 cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface;
1435 texture = _cairo_cogl_acquire_surface_texture (destination, surface);
1436 if (!texture)
1437 return NULL;
1439 /* XXX: determine if it would have no effect to change the
1440 * extend mode to EXTEND_PAD instead since we can simply map
1441 * EXTEND_PAD to CLAMP_TO_EDGE without needing fragment shader
1442 * tricks or extra border texels. */
1443 #if 0
1444 /* TODO: We still need to consider HW such as SGX which doesn't have
1445 * full support for NPOT textures. */
1446 if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_REFLECT) {
1447 _cairo_cogl_get_pot_texture ();
1449 #endif
1451 cairo_matrix_init_identity (&attributes->matrix);
1453 /* Convert from un-normalized source coordinates in backend
1454 * coordinates to normalized texture coordinates */
1455 cairo_matrix_scale (&attributes->matrix,
1456 1.0f / cogl_texture_get_width (texture),
1457 1.0f / cogl_texture_get_height (texture));
1459 /* XXX: need to multiply in the pattern->matrix */
1461 attributes->extend = pattern->extend;
1462 attributes->filter = CAIRO_FILTER_BILINEAR;
1463 attributes->has_component_alpha = pattern->has_component_alpha;
1465 attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend);
1466 attributes->t_wrap = attributes->s_wrap;
1468 return texture;
1470 case CAIRO_PATTERN_TYPE_RADIAL:
1471 case CAIRO_PATTERN_TYPE_MESH: {
1472 cairo_surface_t *surface;
1473 cairo_matrix_t texture_matrix;
1475 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
1476 extents->width, extents->height);
1477 if (_cairo_surface_offset_paint (surface,
1478 extents->x, extents->y,
1479 CAIRO_OPERATOR_SOURCE,
1480 pattern, NULL)) {
1481 cairo_surface_destroy (surface);
1482 return NULL;
1485 texture = _cairo_cogl_acquire_surface_texture (destination, surface);
1486 if (!texture)
1487 goto BAIL;
1489 cairo_matrix_init_identity (&texture_matrix);
1491 /* Convert from un-normalized source coordinates in backend
1492 * coordinates to normalized texture coordinates */
1493 cairo_matrix_scale (&texture_matrix,
1494 1.0f / cogl_texture_get_width (texture),
1495 1.0f / cogl_texture_get_height (texture));
1497 cairo_matrix_translate (&texture_matrix, -extents->x, -extents->y);
1499 attributes->matrix = texture_matrix;
1500 attributes->extend = pattern->extend;
1501 attributes->filter = CAIRO_FILTER_NEAREST;
1502 attributes->has_component_alpha = pattern->has_component_alpha;
1504 /* any pattern extend modes have already been dealt with... */
1505 attributes->s_wrap = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
1506 attributes->t_wrap = attributes->s_wrap;
1508 BAIL:
1509 cairo_surface_destroy (surface);
1511 return texture;
1513 case CAIRO_PATTERN_TYPE_LINEAR: {
1514 cairo_linear_pattern_t *linear_pattern = (cairo_linear_pattern_t *)pattern;
1515 cairo_cogl_linear_gradient_t *gradient;
1516 cairo_cogl_linear_texture_entry_t *linear_texture;
1517 cairo_int_status_t status;
1518 float a, b;
1519 float dist;
1520 float scale;
1521 float angle;
1523 status = _cairo_cogl_get_linear_gradient (to_device(destination->base.device),
1524 pattern->extend,
1525 linear_pattern->base.n_stops,
1526 linear_pattern->base.stops,
1527 &gradient);
1528 if (unlikely (status))
1529 return NULL;
1531 linear_texture = _cairo_cogl_linear_gradient_texture_for_extend (gradient, pattern->extend);
1533 attributes->extend = pattern->extend;
1534 attributes->filter = CAIRO_FILTER_BILINEAR;
1535 attributes->has_component_alpha = pattern->has_component_alpha;
1536 attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend);
1537 attributes->t_wrap = COGL_PIPELINE_WRAP_MODE_REPEAT;
1539 cairo_matrix_init_identity (&attributes->matrix);
1541 a = linear_pattern->pd2.x - linear_pattern->pd1.x;
1542 b = linear_pattern->pd2.y - linear_pattern->pd1.y;
1543 dist = sqrtf (a*a + b*b);
1544 scale = 1.0f / dist;
1545 angle = - atan2f (b, a);
1547 cairo_matrix_rotate (&attributes->matrix, angle);
1548 cairo_matrix_scale (&attributes->matrix, scale, scale);
1550 cairo_matrix_translate (&attributes->matrix,
1551 -linear_pattern->pd1.x,
1552 -linear_pattern->pd1.y);
1554 /* XXX: this caught me out: cairo doesn't follow the standard
1555 * maths convention for multiplying two matrices A x B - cairo
1556 * does B x A so the final matrix is as if A's transforms were
1557 * applied first.
1559 cairo_matrix_multiply (&attributes->matrix,
1560 &pattern->matrix,
1561 &attributes->matrix);
1563 return cogl_object_ref (linear_texture->texture);
1565 default:
1566 g_warning ("Un-supported source type");
1567 return NULL;
1571 static void
1572 set_layer_texture_with_attributes (CoglPipeline *pipeline,
1573 int layer_index,
1574 CoglTexture *texture,
1575 cairo_cogl_texture_attributes_t *attributes)
1577 cogl_pipeline_set_layer_texture (pipeline, layer_index, texture);
1579 if (!_cairo_matrix_is_identity (&attributes->matrix)) {
1580 cairo_matrix_t *m = &attributes->matrix;
1581 float texture_matrixfv[16] = {
1582 m->xx, m->yx, 0, 0,
1583 m->xy, m->yy, 0, 0,
1584 0, 0, 1, 0,
1585 m->x0, m->y0, 0, 1
1587 CoglMatrix texture_matrix;
1588 cogl_matrix_init_from_array (&texture_matrix, texture_matrixfv);
1589 cogl_pipeline_set_layer_matrix (pipeline, layer_index, &texture_matrix);
1592 if (attributes->s_wrap != attributes->t_wrap) {
1593 cogl_pipeline_set_layer_wrap_mode_s (pipeline, layer_index, attributes->s_wrap);
1594 cogl_pipeline_set_layer_wrap_mode_t (pipeline, layer_index, attributes->t_wrap);
1595 } else
1596 cogl_pipeline_set_layer_wrap_mode (pipeline, layer_index, attributes->s_wrap);
1599 static CoglPipeline *
1600 get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
1601 const cairo_pattern_t *source,
1602 cairo_operator_t op,
1603 cairo_cogl_surface_t *destination,
1604 cairo_composite_rectangles_t *extents)
1606 cairo_cogl_template_type template_type;
1607 CoglPipeline *pipeline;
1609 switch ((int)source->type)
1611 case CAIRO_PATTERN_TYPE_SOLID:
1612 template_type = mask ?
1613 CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID : CAIRO_COGL_TEMPLATE_TYPE_SOLID;
1614 break;
1615 case CAIRO_PATTERN_TYPE_SURFACE:
1616 case CAIRO_PATTERN_TYPE_LINEAR:
1617 case CAIRO_PATTERN_TYPE_RADIAL:
1618 case CAIRO_PATTERN_TYPE_MESH:
1619 template_type = mask ?
1620 CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE : CAIRO_COGL_TEMPLATE_TYPE_TEXTURE;
1621 break;
1622 default:
1623 g_warning ("Un-supported source type");
1624 return NULL;
1627 pipeline = cogl_pipeline_copy (to_device(destination->base.device)->template_pipelines[op][template_type]);
1629 if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
1630 cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source;
1631 cogl_pipeline_set_color4f (pipeline,
1632 solid_pattern->color.red * solid_pattern->color.alpha,
1633 solid_pattern->color.green * solid_pattern->color.alpha,
1634 solid_pattern->color.blue * solid_pattern->color.alpha,
1635 solid_pattern->color.alpha);
1636 } else {
1637 cairo_cogl_texture_attributes_t attributes;
1638 CoglTexture *texture =
1639 _cairo_cogl_acquire_pattern_texture (source, destination,
1640 &extents->bounded,
1641 &extents->source_sample_area,
1642 &attributes);
1643 if (!texture)
1644 goto BAIL;
1645 set_layer_texture_with_attributes (pipeline, 0, texture, &attributes);
1646 cogl_object_unref (texture);
1649 if (mask) {
1650 if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
1651 cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)mask;
1652 CoglColor color;
1653 cogl_color_init_from_4f (&color,
1654 solid_pattern->color.red * solid_pattern->color.alpha,
1655 solid_pattern->color.green * solid_pattern->color.alpha,
1656 solid_pattern->color.blue * solid_pattern->color.alpha,
1657 solid_pattern->color.alpha);
1658 cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
1659 } else {
1660 cairo_cogl_texture_attributes_t attributes;
1661 CoglTexture *texture =
1662 _cairo_cogl_acquire_pattern_texture (mask, destination,
1663 &extents->bounded,
1664 &extents->mask_sample_area,
1665 &attributes);
1666 if (!texture)
1667 goto BAIL;
1668 set_layer_texture_with_attributes (pipeline, 1, texture, &attributes);
1669 cogl_object_unref (texture);
1673 return pipeline;
1675 BAIL:
1676 cogl_object_unref (pipeline);
1677 return NULL;
1680 #if 0
1681 CoglPrimitive *
1682 _cairo_cogl_rectangle_new_p2t2t2 (float x,
1683 float y,
1684 float width,
1685 float height)
1687 CoglVertexP2 vertices[] = {
1688 {x, y}, {x, y + height}, {x + width, y + height},
1689 {x, y}, {x + width, y + height}, {x + width, y}
1691 CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (sizeof (vertices));
1692 CoglAttribute *pos = cogl_attribute_new (buffer,
1693 "cogl_position_in",
1694 sizeof (CoglVertexP2),
1697 COGL_ATTRIBUTE_TYPE_FLOAT);
1698 CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
1699 "cogl_tex_coord0_in",
1700 sizeof (CoglVertexP2),
1703 COGL_ATTRIBUTE_TYPE_FLOAT);
1704 CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
1705 "cogl_tex_coord0_in",
1706 sizeof (CoglVertexP2),
1709 COGL_ATTRIBUTE_TYPE_FLOAT);
1710 CoglPrimitive *prim;
1712 cogl_buffer_set_data (COGL_BUFFER (buffer), 0, vertices, sizeof (vertices));
1714 /* The attributes will now keep the buffer alive... */
1715 cogl_object_unref (buffer);
1717 prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
1718 6, pos, tex_coords, NULL);
1720 /* The primitive will now keep the attribute alive... */
1721 cogl_object_unref (pos);
1723 return prim;
1725 #endif
1727 static void
1728 _cairo_cogl_log_clip (cairo_cogl_surface_t *surface,
1729 const cairo_clip_t *clip)
1731 if (!_cairo_clip_equal (clip, surface->last_clip)) {
1732 _cairo_cogl_journal_log_clip (surface, clip);
1733 _cairo_clip_destroy (surface->last_clip);
1734 surface->last_clip = _cairo_clip_copy (clip);
1738 static void
1739 _cairo_cogl_maybe_log_clip (cairo_cogl_surface_t *surface,
1740 cairo_composite_rectangles_t *composite)
1742 cairo_clip_t *clip = composite->clip;
1744 if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
1745 clip = NULL;
1747 if (clip == NULL) {
1748 if (_cairo_composite_rectangles_can_reduce_clip (composite,
1749 surface->last_clip))
1750 return;
1753 _cairo_cogl_log_clip (surface, clip);
1756 static cairo_bool_t
1757 is_operator_supported (cairo_operator_t op)
1759 switch ((int)op) {
1760 case CAIRO_OPERATOR_SOURCE:
1761 case CAIRO_OPERATOR_OVER:
1762 case CAIRO_OPERATOR_IN:
1763 case CAIRO_OPERATOR_DEST_OVER:
1764 case CAIRO_OPERATOR_DEST_IN:
1765 case CAIRO_OPERATOR_ADD:
1766 return TRUE;
1768 default:
1769 return FALSE;
1773 static cairo_int_status_t
1774 _cairo_cogl_surface_mask (void *abstract_surface,
1775 cairo_operator_t op,
1776 const cairo_pattern_t *source,
1777 const cairo_pattern_t *mask,
1778 const cairo_clip_t *clip)
1780 cairo_cogl_surface_t *surface = abstract_surface;
1781 cairo_composite_rectangles_t extents;
1782 cairo_status_t status;
1783 CoglPipeline *pipeline;
1784 cairo_matrix_t identity;
1786 /* XXX: Use this to smoke test the acquire_source/dest_image fallback
1787 * paths... */
1788 //return CAIRO_INT_STATUS_UNSUPPORTED;
1790 if (!is_operator_supported (op))
1791 return CAIRO_INT_STATUS_UNSUPPORTED;
1793 status = _cairo_composite_rectangles_init_for_mask (&extents,
1794 &surface->base,
1795 op, source, mask, clip);
1796 if (unlikely (status))
1797 return status;
1799 pipeline = get_source_mask_operator_destination_pipeline (mask, source,
1800 op, surface, &extents);
1801 if (!pipeline){
1802 status = CAIRO_INT_STATUS_UNSUPPORTED;
1803 goto BAIL;
1806 _cairo_cogl_maybe_log_clip (surface, &extents);
1808 cairo_matrix_init_identity (&identity);
1809 _cairo_cogl_journal_log_rectangle (surface, pipeline,
1810 extents.bounded.x,
1811 extents.bounded.y,
1812 extents.bounded.width,
1813 extents.bounded.height,
1815 &identity);
1817 /* The journal will take a reference on the pipeline and clip_path... */
1818 cogl_object_unref (pipeline);
1820 BAIL:
1821 return status;
1824 static int
1825 _cairo_cogl_source_n_layers (const cairo_pattern_t *source)
1827 switch ((int)source->type)
1829 case CAIRO_PATTERN_TYPE_SOLID:
1830 return 0;
1831 case CAIRO_PATTERN_TYPE_LINEAR:
1832 case CAIRO_PATTERN_TYPE_RADIAL:
1833 case CAIRO_PATTERN_TYPE_MESH:
1834 case CAIRO_PATTERN_TYPE_SURFACE:
1835 return 1;
1836 default:
1837 g_warning ("Unsupported source type");
1838 return 0;
1842 static cairo_bool_t
1843 _cairo_cogl_path_fill_meta_equal (const void *key_a, const void *key_b)
1845 const cairo_cogl_path_fill_meta_t *meta0 = key_a;
1846 const cairo_cogl_path_fill_meta_t *meta1 = key_b;
1848 return _cairo_path_fixed_equal (meta0->user_path, meta1->user_path);
1851 static cairo_bool_t
1852 _cairo_cogl_stroke_style_equal (const cairo_stroke_style_t *a,
1853 const cairo_stroke_style_t *b)
1855 if (a->line_width == b->line_width &&
1856 a->line_cap == b->line_cap &&
1857 a->line_join == b->line_join &&
1858 a->miter_limit == b->miter_limit &&
1859 a->num_dashes == b->num_dashes &&
1860 a->dash_offset == b->dash_offset)
1862 unsigned int i;
1863 for (i = 0; i < a->num_dashes; i++) {
1864 if (a->dash[i] != b->dash[i])
1865 return FALSE;
1868 return TRUE;
1871 static cairo_bool_t
1872 _cairo_cogl_path_stroke_meta_equal (const void *key_a, const void *key_b)
1874 const cairo_cogl_path_stroke_meta_t *meta0 = key_a;
1875 const cairo_cogl_path_stroke_meta_t *meta1 = key_b;
1877 return _cairo_cogl_stroke_style_equal (&meta0->style, &meta1->style) &&
1878 _cairo_path_fixed_equal (meta0->user_path, meta1->user_path);
1881 static cairo_cogl_path_stroke_meta_t *
1882 _cairo_cogl_path_stroke_meta_reference (cairo_cogl_path_stroke_meta_t *meta)
1884 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
1886 _cairo_reference_count_inc (&meta->ref_count);
1888 return meta;
1891 static void
1892 _cairo_cogl_path_stroke_meta_destroy (cairo_cogl_path_stroke_meta_t *meta)
1894 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
1896 if (! _cairo_reference_count_dec_and_test (&meta->ref_count))
1897 return;
1899 _cairo_path_fixed_fini (meta->user_path);
1900 free (meta->user_path);
1902 _cairo_stroke_style_fini (&meta->style);
1904 if (meta->prim)
1905 cogl_object_unref (meta->prim);
1907 free (meta);
1910 static cairo_cogl_path_stroke_meta_t *
1911 _cairo_cogl_path_stroke_meta_lookup (cairo_cogl_device_t *ctx,
1912 unsigned long hash,
1913 cairo_path_fixed_t *user_path,
1914 const cairo_stroke_style_t *style,
1915 double tolerance)
1917 cairo_cogl_path_stroke_meta_t *ret;
1918 cairo_cogl_path_stroke_meta_t lookup;
1920 lookup.cache_entry.hash = hash;
1921 lookup.user_path = user_path;
1922 lookup.style = *style;
1923 lookup.tolerance = tolerance;
1925 ret = _cairo_cache_lookup (&ctx->path_stroke_staging_cache, &lookup.cache_entry);
1926 if (!ret)
1927 ret = _cairo_cache_lookup (&ctx->path_stroke_prim_cache, &lookup.cache_entry);
1928 return ret;
1931 static void
1932 _cairo_cogl_path_stroke_meta_set_prim_size (cairo_cogl_surface_t *surface,
1933 cairo_cogl_path_stroke_meta_t *meta,
1934 size_t size)
1936 /* now that we know the meta structure is associated with a primitive
1937 * we promote it from the staging cache into the primitive cache.
1940 /* XXX: _cairo_cache borks if you try and remove an entry that's already
1941 * been evicted so we explicitly look it up first... */
1942 if (_cairo_cache_lookup (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry)) {
1943 _cairo_cogl_path_stroke_meta_reference (meta);
1944 _cairo_cache_remove (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry);
1947 meta->cache_entry.size = size;
1948 if (_cairo_cache_insert (&to_device(surface->base.device)->path_stroke_prim_cache, &meta->cache_entry) !=
1949 CAIRO_STATUS_SUCCESS)
1950 _cairo_cogl_path_stroke_meta_destroy (meta);
1953 static unsigned int
1954 _cairo_cogl_stroke_style_hash (unsigned int hash,
1955 const cairo_stroke_style_t *style)
1957 unsigned int i;
1958 hash = _cairo_hash_bytes (hash, &style->line_width, sizeof (style->line_width));
1959 hash = _cairo_hash_bytes (hash, &style->line_cap, sizeof (style->line_cap));
1960 hash = _cairo_hash_bytes (hash, &style->line_join, sizeof (style->line_join));
1961 hash = _cairo_hash_bytes (hash, &style->miter_limit, sizeof (style->miter_limit));
1962 hash = _cairo_hash_bytes (hash, &style->num_dashes, sizeof (style->num_dashes));
1963 hash = _cairo_hash_bytes (hash, &style->dash_offset, sizeof (style->dash_offset));
1964 for (i = 0; i < style->num_dashes; i++)
1965 hash = _cairo_hash_bytes (hash, &style->dash[i], sizeof (double));
1966 return hash;
1969 static cairo_cogl_path_stroke_meta_t *
1970 _cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t *surface,
1971 const cairo_stroke_style_t *style,
1972 double tolerance)
1974 unsigned long hash;
1975 cairo_cogl_path_stroke_meta_t *meta = NULL;
1976 cairo_path_fixed_t *meta_path = NULL;
1977 cairo_status_t status;
1979 if (!surface->user_path)
1980 return NULL;
1982 hash = _cairo_path_fixed_hash (surface->user_path);
1983 hash = _cairo_cogl_stroke_style_hash (hash, style);
1984 hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance));
1986 meta = _cairo_cogl_path_stroke_meta_lookup (to_device(surface->base.device), hash,
1987 surface->user_path, style, tolerance);
1988 if (meta)
1989 return meta;
1991 meta = calloc (1, sizeof (cairo_cogl_path_stroke_meta_t));
1992 if (!meta)
1993 goto BAIL;
1994 CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
1995 meta->cache_entry.hash = hash;
1996 meta->counter = 0;
1997 meta_path = malloc (sizeof (cairo_path_fixed_t));
1998 if (!meta_path)
1999 goto BAIL;
2000 /* FIXME: we should add a ref-counted wrapper for our user_paths
2001 * so we don't have to keep copying them here! */
2002 status = _cairo_path_fixed_init_copy (meta_path, surface->user_path);
2003 if (unlikely (status))
2004 goto BAIL;
2005 meta->user_path = meta_path;
2006 meta->ctm_inverse = *surface->ctm_inverse;
2008 status = _cairo_stroke_style_init_copy (&meta->style, style);
2009 if (unlikely (status)) {
2010 _cairo_path_fixed_fini (meta_path);
2011 goto BAIL;
2013 meta->tolerance = tolerance;
2015 return meta;
2017 BAIL:
2018 free (meta_path);
2019 free (meta);
2020 return NULL;
2023 static cairo_int_status_t
2024 _cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t *surface,
2025 const cairo_path_fixed_t *path,
2026 const cairo_stroke_style_t *style,
2027 const cairo_matrix_t *ctm,
2028 const cairo_matrix_t *ctm_inverse,
2029 double tolerance,
2030 int n_layers,
2031 cairo_bool_t one_shot,
2032 CoglPrimitive **primitive,
2033 size_t *size)
2035 cairo_traps_t traps;
2036 cairo_int_status_t status;
2038 _cairo_traps_init (&traps);
2040 status = _cairo_path_fixed_stroke_polygon_to_traps (path, style,
2041 ctm, ctm_inverse,
2042 tolerance,
2043 &traps);
2044 if (unlikely (status))
2045 goto BAIL;
2047 if (traps.num_traps == 0) {
2048 status = CAIRO_INT_STATUS_NOTHING_TO_DO;
2049 goto BAIL;
2052 *size = traps.num_traps * sizeof (CoglVertexP2) * 6;
2054 //g_print ("new stroke prim\n");
2055 *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot);
2056 if (!*primitive) {
2057 status = CAIRO_INT_STATUS_NO_MEMORY;
2058 goto BAIL;
2061 BAIL:
2062 _cairo_traps_fini (&traps);
2063 return status;
2066 static cairo_int_status_t
2067 _cairo_cogl_surface_stroke (void *abstract_surface,
2068 cairo_operator_t op,
2069 const cairo_pattern_t *source,
2070 const cairo_path_fixed_t *path,
2071 const cairo_stroke_style_t *style,
2072 const cairo_matrix_t *ctm,
2073 const cairo_matrix_t *ctm_inverse,
2074 double tolerance,
2075 cairo_antialias_t antialias,
2076 const cairo_clip_t *clip)
2078 cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
2079 cairo_composite_rectangles_t extents;
2080 CoglPipeline *pipeline;
2081 cairo_status_t status;
2082 #ifdef ENABLE_PATH_CACHE
2083 cairo_cogl_path_stroke_meta_t *meta = NULL;
2084 cairo_matrix_t transform_matrix;
2085 #endif
2086 cairo_matrix_t *transform = NULL;
2087 gboolean one_shot = TRUE;
2088 CoglPrimitive *prim = NULL;
2089 cairo_bool_t new_prim = FALSE;
2091 if (! is_operator_supported (op))
2092 return CAIRO_INT_STATUS_UNSUPPORTED;
2094 /* FIXME - support unbounded operators */
2095 if (!_cairo_operator_bounded_by_mask (op)) {
2096 /* Currently IN this is the only unbounded operator we aim to support
2097 * in cairo-cogl. */
2098 assert (op == CAIRO_OPERATOR_IN);
2099 g_warning ("FIXME: handle stroking with unbounded operators!");
2100 return CAIRO_INT_STATUS_UNSUPPORTED;
2103 status = _cairo_composite_rectangles_init_for_stroke (&extents,
2104 &surface->base,
2105 op, source, path,
2106 style,
2107 ctm,
2108 clip);
2109 if (unlikely (status))
2110 return status;
2112 #ifdef ENABLE_PATH_CACHE
2113 /* FIXME: we are currently leaking the meta state if we don't reach
2114 * the cache_insert at the end. */
2115 meta = _cairo_cogl_get_path_stroke_meta (surface, style, tolerance);
2116 if (meta) {
2117 prim = meta->prim;
2118 if (prim) {
2119 cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
2120 transform = &transform_matrix;
2121 } else if (meta->counter++ > 10)
2122 one_shot = FALSE;
2124 #endif
2126 if (!prim) {
2127 int n_layers = _cairo_cogl_source_n_layers (source);
2128 size_t prim_size = 0;
2129 status = _cairo_cogl_stroke_to_primitive (surface, path, style,
2130 ctm, ctm_inverse, tolerance,
2131 n_layers, one_shot,
2132 &prim, &prim_size);
2133 if (unlikely (status))
2134 return status;
2135 new_prim = TRUE;
2136 #if defined (ENABLE_PATH_CACHE)
2137 if (meta) {
2138 meta->prim = cogl_object_ref (prim);
2139 _cairo_cogl_path_stroke_meta_set_prim_size (surface, meta, prim_size);
2141 #endif
2144 pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
2145 op, surface, &extents);
2146 if (!pipeline)
2147 return CAIRO_INT_STATUS_UNSUPPORTED;
2149 _cairo_cogl_maybe_log_clip (surface, &extents);
2151 _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
2153 /* The journal will take a reference on the pipeline and primitive... */
2154 cogl_object_unref (pipeline);
2155 if (new_prim)
2156 cogl_object_unref (prim);
2158 return CAIRO_INT_STATUS_SUCCESS;
2161 static cairo_cogl_path_fill_meta_t *
2162 _cairo_cogl_path_fill_meta_reference (cairo_cogl_path_fill_meta_t *meta)
2164 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
2166 _cairo_reference_count_inc (&meta->ref_count);
2168 return meta;
2171 static void
2172 _cairo_cogl_path_fill_meta_destroy (cairo_cogl_path_fill_meta_t *meta)
2174 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
2176 if (! _cairo_reference_count_dec_and_test (&meta->ref_count))
2177 return;
2179 _cairo_path_fixed_fini (meta->user_path);
2180 free (meta->user_path);
2182 if (meta->prim)
2183 cogl_object_unref (meta->prim);
2185 free (meta);
2188 static cairo_cogl_path_fill_meta_t *
2189 _cairo_cogl_path_fill_meta_lookup (cairo_cogl_device_t *ctx,
2190 unsigned long hash,
2191 cairo_path_fixed_t *user_path)
2193 cairo_cogl_path_fill_meta_t *ret;
2194 cairo_cogl_path_fill_meta_t lookup;
2196 lookup.cache_entry.hash = hash;
2197 lookup.user_path = user_path;
2199 ret = _cairo_cache_lookup (&ctx->path_fill_staging_cache, &lookup.cache_entry);
2200 if (!ret)
2201 ret = _cairo_cache_lookup (&ctx->path_fill_prim_cache, &lookup.cache_entry);
2202 return ret;
2205 static void
2206 _cairo_cogl_path_fill_meta_set_prim_size (cairo_cogl_surface_t *surface,
2207 cairo_cogl_path_fill_meta_t *meta,
2208 size_t size)
2210 /* now that we know the meta structure is associated with a primitive
2211 * we promote it from the staging cache into the primitive cache.
2214 /* XXX: _cairo_cache borks if you try and remove an entry that's already
2215 * been evicted so we explicitly look it up first... */
2216 if (_cairo_cache_lookup (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry)) {
2217 _cairo_cogl_path_fill_meta_reference (meta);
2218 _cairo_cache_remove (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry);
2221 meta->cache_entry.size = size;
2222 if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_prim_cache, &meta->cache_entry) !=
2223 CAIRO_STATUS_SUCCESS)
2224 _cairo_cogl_path_fill_meta_destroy (meta);
2227 static cairo_cogl_path_fill_meta_t *
2228 _cairo_cogl_get_path_fill_meta (cairo_cogl_surface_t *surface)
2230 unsigned long hash;
2231 cairo_cogl_path_fill_meta_t *meta = NULL;
2232 cairo_path_fixed_t *meta_path = NULL;
2233 cairo_status_t status;
2235 if (!surface->user_path)
2236 return NULL;
2238 hash = _cairo_path_fixed_hash (surface->user_path);
2240 meta = _cairo_cogl_path_fill_meta_lookup (to_device(surface->base.device),
2241 hash, surface->user_path);
2242 if (meta)
2243 return meta;
2245 meta = calloc (1, sizeof (cairo_cogl_path_fill_meta_t));
2246 if (!meta)
2247 goto BAIL;
2248 meta->cache_entry.hash = hash;
2249 meta->counter = 0;
2250 CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
2251 meta_path = malloc (sizeof (cairo_path_fixed_t));
2252 if (!meta_path)
2253 goto BAIL;
2254 /* FIXME: we should add a ref-counted wrapper for our user_paths
2255 * so we don't have to keep copying them here! */
2256 status = _cairo_path_fixed_init_copy (meta_path, surface->user_path);
2257 if (unlikely (status))
2258 goto BAIL;
2259 meta->user_path = meta_path;
2260 meta->ctm_inverse = *surface->ctm_inverse;
2262 /* To start with - until we associate a CoglPrimitive with the meta
2263 * structure - we keep the meta in a staging structure until we
2264 * see whether it actually gets re-used. */
2265 meta->cache_entry.size = 1;
2266 if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry) !=
2267 CAIRO_STATUS_SUCCESS)
2268 _cairo_cogl_path_fill_meta_destroy (meta);
2270 return meta;
2272 BAIL:
2273 free (meta_path);
2274 free (meta);
2275 return NULL;
2278 static cairo_int_status_t
2279 _cairo_cogl_surface_fill (void *abstract_surface,
2280 cairo_operator_t op,
2281 const cairo_pattern_t *source,
2282 const cairo_path_fixed_t *path,
2283 cairo_fill_rule_t fill_rule,
2284 double tolerance,
2285 cairo_antialias_t antialias,
2286 const cairo_clip_t *clip)
2288 cairo_cogl_surface_t *surface = abstract_surface;
2289 cairo_composite_rectangles_t extents;
2290 cairo_status_t status;
2291 #ifdef ENABLE_PATH_CACHE
2292 cairo_cogl_path_fill_meta_t *meta = NULL;
2293 cairo_matrix_t transform_matrix;
2294 #endif
2295 cairo_matrix_t *transform = NULL;
2296 cairo_bool_t one_shot = TRUE;
2297 CoglPrimitive *prim = NULL;
2298 cairo_bool_t new_prim = FALSE;
2299 CoglPipeline *pipeline;
2301 if (! is_operator_supported (op))
2302 return CAIRO_INT_STATUS_UNSUPPORTED;
2304 /* FIXME - support unbounded operators */
2305 if (!_cairo_operator_bounded_by_mask (op)) {
2306 /* Currently IN this is the only unbounded operator we aim to support
2307 * in cairo-cogl. */
2308 assert (op == CAIRO_OPERATOR_IN);
2309 g_warning ("FIXME: handle filling with unbounded operators!");
2310 return CAIRO_INT_STATUS_UNSUPPORTED;
2313 status = _cairo_composite_rectangles_init_for_fill (&extents,
2314 &surface->base,
2315 op, source, path,
2316 clip);
2317 if (unlikely (status))
2318 return status;
2320 #ifndef FILL_WITH_COGL_PATH
2321 #ifdef ENABLE_PATH_CACHE
2322 meta = _cairo_cogl_get_path_fill_meta (surface);
2323 if (meta) {
2324 prim = meta->prim;
2325 if (prim) {
2326 cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
2327 transform = &transform_matrix;
2328 } else if (meta->counter++ > 10)
2329 one_shot = FALSE;
2331 #endif /* ENABLE_PATH_CACHE */
2333 if (!prim) {
2334 int n_layers = _cairo_cogl_source_n_layers (source);
2335 size_t prim_size;
2336 status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule, tolerance,
2337 one_shot, n_layers, &prim, &prim_size);
2338 if (unlikely (status))
2339 return status;
2340 new_prim = TRUE;
2341 #ifdef ENABLE_PATH_CACHE
2342 if (meta) {
2343 meta->prim = cogl_object_ref (prim);
2344 _cairo_cogl_path_fill_meta_set_prim_size (surface, meta, prim_size);
2346 #endif /* ENABLE_PATH_CACHE */
2349 #endif /* !FILL_WITH_COGL_PATH */
2351 pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
2352 op, surface, &extents);
2353 if (!pipeline)
2354 return CAIRO_INT_STATUS_UNSUPPORTED;
2356 _cairo_cogl_maybe_log_clip (surface, &extents);
2358 #ifndef FILL_WITH_COGL_PATH
2359 _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
2360 /* The journal will take a reference on the prim */
2361 if (new_prim)
2362 cogl_object_unref (prim);
2363 #else
2364 CoglPath * cogl_path = _cairo_cogl_util_path_from_cairo (path, fill_rule, tolerance);
2365 _cairo_cogl_journal_log_path (surface, pipeline, cogl_path);
2366 cogl_object_unref (cogl_path);
2367 #endif
2369 /* The journal will take a reference on the pipeline... */
2370 cogl_object_unref (pipeline);
2372 return CAIRO_INT_STATUS_SUCCESS;
2375 cairo_int_status_t
2376 _cairo_cogl_surface_fill_rectangle (void *abstract_surface,
2377 cairo_operator_t op,
2378 const cairo_pattern_t *source,
2379 double x,
2380 double y,
2381 double width,
2382 double height,
2383 cairo_matrix_t *ctm,
2384 const cairo_clip_t *clip)
2386 cairo_cogl_surface_t *surface = abstract_surface;
2387 CoglPipeline *pipeline;
2389 if (! is_operator_supported (op))
2390 return CAIRO_INT_STATUS_UNSUPPORTED;
2392 /* FIXME - support unbounded operators */
2393 if (!_cairo_operator_bounded_by_mask (op)) {
2394 /* Currently IN this is the only unbounded operator we aim to support
2395 * in cairo-cogl. */
2396 assert (op == CAIRO_OPERATOR_IN);
2397 g_warning ("FIXME: handle filling with unbounded operators!");
2398 return CAIRO_INT_STATUS_UNSUPPORTED;
2401 /* FIXME */
2402 #if 0
2403 status = _cairo_composite_rectangles_init_for_fill_rectangle (&extents,
2404 &surface->base,
2405 op, source, path,
2406 clip);
2407 if (unlikely (status))
2408 return status;
2409 #endif
2411 if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
2412 double x1 = x;
2413 double y1 = y;
2414 double x2 = x1 + width;
2415 double y2 = y1 + height;
2417 pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
2418 op, surface, NULL);
2419 if (!pipeline)
2420 return CAIRO_INT_STATUS_UNSUPPORTED;
2422 _cairo_cogl_log_clip (surface, clip);
2424 _cairo_cogl_journal_log_rectangle (surface,
2425 pipeline,
2426 x1, y1, x2, y2,
2428 ctm);
2429 return CAIRO_INT_STATUS_SUCCESS;
2430 } else
2431 return CAIRO_INT_STATUS_UNSUPPORTED;
2433 /* TODO:
2434 * We need to acquire the textures here, look at the corresponding
2435 * attributes and see if this can be trivially handled by logging
2436 * a textured rectangle only needing simple scaling or translation
2437 * of texture coordinates.
2439 * At this point we should also aim to remap the default
2440 * EXTEND_NONE mode to EXTEND_PAD which is more efficient if we
2441 * know it makes no difference either way since we can map that to
2442 * CLAMP_TO_EDGE.
2446 static cairo_int_status_t
2447 _cairo_cogl_surface_show_glyphs (void *surface,
2448 cairo_operator_t op,
2449 const cairo_pattern_t *source,
2450 cairo_glyph_t *glyphs,
2451 int num_glyphs,
2452 cairo_scaled_font_t *scaled_font,
2453 const cairo_clip_t *clip)
2455 return CAIRO_INT_STATUS_UNSUPPORTED;
2458 const cairo_surface_backend_t _cairo_cogl_surface_backend = {
2459 CAIRO_SURFACE_TYPE_COGL,
2460 _cairo_cogl_surface_finish,
2461 #ifdef NEED_COGL_CONTEXT
2462 _cairo_cogl_context_create,
2463 #else
2464 _cairo_default_context_create,
2465 #endif
2467 _cairo_cogl_surface_create_similar,
2468 NULL, /* create similar image */
2469 NULL, /* map to image */
2470 NULL, /* unmap image */
2472 _cairo_surface_default_source,
2473 _cairo_cogl_surface_acquire_source_image,
2474 _cairo_cogl_surface_release_source_image,
2475 NULL, /* snapshot */
2477 NULL, /* copy_page */
2478 NULL, /* show_page */
2480 _cairo_cogl_surface_get_extents,
2481 NULL, /* get_font_options */
2483 _cairo_cogl_surface_flush, /* flush */
2484 NULL, /* mark_dirty_rectangle */
2486 _cairo_cogl_surface_paint,
2487 _cairo_cogl_surface_mask,
2488 _cairo_cogl_surface_stroke,
2489 _cairo_cogl_surface_fill,
2490 NULL, /* fill_stroke*/
2491 _cairo_surface_fallback_glyphs,
2494 static cairo_surface_t *
2495 _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
2496 cairo_bool_t ignore_alpha,
2497 CoglFramebuffer *framebuffer,
2498 CoglTexture *texture)
2500 cairo_cogl_surface_t *surface;
2501 cairo_status_t status;
2503 status = cairo_device_acquire (&dev->base);
2504 if (unlikely (status))
2505 return _cairo_surface_create_in_error (status);
2507 surface = malloc (sizeof (cairo_cogl_surface_t));
2508 if (unlikely (surface == NULL))
2509 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2511 surface->ignore_alpha = ignore_alpha;
2513 surface->framebuffer = framebuffer;
2514 if (framebuffer) {
2515 surface->width = cogl_framebuffer_get_width (framebuffer);
2516 surface->height = cogl_framebuffer_get_height (framebuffer);
2517 surface->cogl_format = cogl_framebuffer_get_color_format (framebuffer);
2518 cogl_object_ref (framebuffer);
2521 /* FIXME: If texture == NULL and we are given an offscreen framebuffer
2522 * then we want a way to poke inside the framebuffer to get a texture */
2523 surface->texture = texture;
2524 if (texture) {
2525 if (!framebuffer) {
2526 surface->width = cogl_texture_get_width (texture);
2527 surface->height = cogl_texture_get_height (texture);
2528 surface->cogl_format = cogl_texture_get_format (texture);
2530 cogl_object_ref (texture);
2533 assert(surface->width && surface->height);
2535 surface->journal = NULL;
2537 surface->buffer_stack = NULL;
2538 surface->buffer_stack_size = 4096;
2540 surface->last_clip = NULL;
2542 surface->n_clip_updates_per_frame = 0;
2544 _cairo_surface_init (&surface->base,
2545 &_cairo_cogl_surface_backend,
2546 &dev->base,
2547 CAIRO_CONTENT_COLOR_ALPHA);
2549 return &surface->base;
2552 cairo_surface_t *
2553 cairo_cogl_surface_create (cairo_device_t *abstract_device,
2554 CoglFramebuffer *framebuffer)
2556 cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
2558 if (abstract_device == NULL)
2559 return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
2561 if (abstract_device->status)
2562 return _cairo_surface_create_in_error (abstract_device->status);
2564 if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
2565 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
2567 return _cairo_cogl_surface_create_full (dev, FALSE, framebuffer, NULL);
2569 slim_hidden_def (cairo_cogl_surface_create);
2571 CoglFramebuffer *
2572 cairo_cogl_surface_get_framebuffer (cairo_surface_t *abstract_surface)
2574 cairo_cogl_surface_t *surface;
2576 if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
2577 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
2578 return NULL;
2581 surface = (cairo_cogl_surface_t *) abstract_surface;
2583 return surface->framebuffer;
2585 slim_hidden_def (cairo_cogl_surface_get_framebuffer);
2587 CoglTexture *
2588 cairo_cogl_surface_get_texture (cairo_surface_t *abstract_surface)
2590 cairo_cogl_surface_t *surface;
2592 if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
2593 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
2594 return NULL;
2597 surface = (cairo_cogl_surface_t *) abstract_surface;
2599 return surface->texture;
2601 slim_hidden_def (cairo_cogl_surface_get_texture);
2603 static cairo_status_t
2604 _cairo_cogl_device_flush (void *device)
2606 cairo_status_t status;
2608 status = cairo_device_acquire (device);
2609 if (unlikely (status))
2610 return status;
2612 /* XXX: we don't need to flush Cogl here, we just need to flush
2613 * any batching we do of compositing primitives. */
2615 cairo_device_release (device);
2617 return CAIRO_STATUS_SUCCESS;
2620 static void
2621 _cairo_cogl_device_finish (void *device)
2623 cairo_status_t status;
2625 status = cairo_device_acquire (device);
2626 if (unlikely (status))
2627 return;
2629 /* XXX: Drop references to external resources */
2631 cairo_device_release (device);
2634 static void
2635 _cairo_cogl_device_destroy (void *device)
2637 cairo_cogl_device_t *dev = device;
2639 /* FIXME: Free stuff! */
2641 g_free (dev);
2644 static const cairo_device_backend_t _cairo_cogl_device_backend = {
2645 CAIRO_DEVICE_TYPE_COGL,
2647 NULL, /* lock */
2648 NULL, /* unlock */
2650 _cairo_cogl_device_flush,
2651 _cairo_cogl_device_finish,
2652 _cairo_cogl_device_destroy,
2655 static cairo_bool_t
2656 set_blend (CoglPipeline *pipeline, const char *blend_string)
2658 GError *error = NULL;
2659 if (!cogl_pipeline_set_blend (pipeline, blend_string, &error)) {
2660 g_warning ("Unsupported blend string with current gpu/driver: %s", blend_string);
2661 g_error_free (error);
2662 return FALSE;
2664 return TRUE;
2667 static cairo_bool_t
2668 _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
2670 cairo_bool_t status = FALSE;
2672 switch ((int)op)
2674 case CAIRO_OPERATOR_SOURCE:
2675 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)");
2676 break;
2677 case CAIRO_OPERATOR_OVER:
2678 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))");
2679 break;
2680 case CAIRO_OPERATOR_IN:
2681 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], 0)");
2682 break;
2683 case CAIRO_OPERATOR_DEST_OVER:
2684 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)");
2685 break;
2686 case CAIRO_OPERATOR_DEST_IN:
2687 status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])");
2688 break;
2689 case CAIRO_OPERATOR_ADD:
2690 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR)");
2691 break;
2694 return status;
2697 static void
2698 create_templates_for_op (cairo_cogl_device_t *dev, cairo_operator_t op)
2700 CoglPipeline *base = cogl_pipeline_new ();
2701 CoglPipeline *pipeline;
2702 CoglColor color;
2704 if (!_cairo_cogl_setup_op_state (base, op)) {
2705 cogl_object_unref (base);
2706 return;
2709 dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base;
2711 pipeline = cogl_pipeline_copy (base);
2712 cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
2713 dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_TEXTURE] = pipeline;
2715 pipeline = cogl_pipeline_copy (base);
2716 cogl_pipeline_set_layer_combine (pipeline, 1,
2717 "RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
2718 NULL);
2719 cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
2720 cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
2721 dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID] = pipeline;
2723 pipeline = cogl_pipeline_copy (base);
2724 cogl_pipeline_set_layer_combine (pipeline, 1,
2725 "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
2726 NULL);
2727 cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
2728 dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE] = pipeline;
2731 cairo_device_t *
2732 cairo_cogl_device_create (CoglContext *cogl_context)
2734 cairo_cogl_device_t *dev = g_new0 (cairo_cogl_device_t, 1);
2735 cairo_status_t status;
2737 dev->backend_vtable_initialized = FALSE;
2739 dev->cogl_context = cogl_context;
2741 dev->dummy_texture = cogl_texture_new_with_size (1, 1,
2742 COGL_TEXTURE_NO_SLICING,
2743 COGL_PIXEL_FORMAT_ANY);
2744 if (!dev->dummy_texture)
2745 goto ERROR;
2747 memset (dev->template_pipelines, 0, sizeof (dev->template_pipelines));
2748 create_templates_for_op (dev, CAIRO_OPERATOR_SOURCE);
2749 create_templates_for_op (dev, CAIRO_OPERATOR_OVER);
2750 create_templates_for_op (dev, CAIRO_OPERATOR_IN);
2751 create_templates_for_op (dev, CAIRO_OPERATOR_DEST_OVER);
2752 create_templates_for_op (dev, CAIRO_OPERATOR_DEST_IN);
2753 create_templates_for_op (dev, CAIRO_OPERATOR_ADD);
2755 status = _cairo_cache_init (&dev->linear_cache,
2756 _cairo_cogl_linear_gradient_equal,
2757 NULL,
2758 (cairo_destroy_func_t) _cairo_cogl_linear_gradient_destroy,
2759 CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE);
2760 if (unlikely (status))
2761 return _cairo_device_create_in_error(status);
2763 status = _cairo_cache_init (&dev->path_fill_staging_cache,
2764 _cairo_cogl_path_fill_meta_equal,
2765 NULL,
2766 (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy,
2767 1000);
2769 status = _cairo_cache_init (&dev->path_stroke_staging_cache,
2770 _cairo_cogl_path_stroke_meta_equal,
2771 NULL,
2772 (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
2773 1000);
2775 status = _cairo_cache_init (&dev->path_fill_prim_cache,
2776 _cairo_cogl_path_fill_meta_equal,
2777 NULL,
2778 (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy,
2779 CAIRO_COGL_PATH_META_CACHE_SIZE);
2781 status = _cairo_cache_init (&dev->path_stroke_prim_cache,
2782 _cairo_cogl_path_stroke_meta_equal,
2783 NULL,
2784 (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
2785 CAIRO_COGL_PATH_META_CACHE_SIZE);
2787 _cairo_device_init (&dev->base, &_cairo_cogl_device_backend);
2788 return &dev->base;
2790 ERROR:
2791 g_free (dev);
2792 return _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
2794 slim_hidden_def (cairo_cogl_device_create);
2796 void
2797 cairo_cogl_surface_end_frame (cairo_surface_t *abstract_surface)
2799 cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
2800 cairo_surface_flush (abstract_surface);
2802 //g_print ("n_clip_update_per_frame = %d\n", surface->n_clip_updates_per_frame);
2803 surface->n_clip_updates_per_frame = 0;
2805 slim_hidden_def (cairo_cogl_surface_end_frame);