1 /* cairo - a vector graphics library with display and print output
2 *
3 * Copyright © 2011 Intel Corporation.
4 *
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.
12 *
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
18 *
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/
23 *
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.
27 *
28 * Contributor(s):
29 * Robert Bragg <robert@linux.intel.com>
30 */
31 #include "cairoint.h"
32
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-recording-surface-inline.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-surface-subsurface-inline.h"
47 #include "cairo-surface-fallback-private.h"
48 #include "cairo-surface-offset-private.h"
49
50 #include "cairo-cogl.h"
51
52 #include <cogl/cogl2-experimental.h>
53 #include <glib.h>
54
55 #define CAIRO_COGL_DEBUG 0
56 //#define DISABLE_BATCHING
57 #define MAX_JOURNAL_SIZE 100
58
59 #if CAIRO_COGL_DEBUG && __GNUC__
60 #define UNSUPPORTED(reason) ({ \
61 g_warning ("cairo-cogl: hit unsupported operation: %s", reason); \
62 CAIRO_INT_STATUS_UNSUPPORTED; \
63 })
64 #else
65 #define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
66 #endif
67
68 #define CAIRO_COGL_PATH_META_CACHE_SIZE (1024 * 1024)
69
70 typedef struct _cairo_cogl_texture_attributes {
71 /* nabbed from cairo_surface_attributes_t... */
72 cairo_matrix_t matrix;
73 cairo_extend_t extend;
74 cairo_bool_t has_component_alpha;
75
76 CoglPipelineFilter filter;
77 CoglPipelineWrapMode s_wrap;
78 CoglPipelineWrapMode t_wrap;
79 } cairo_cogl_texture_attributes_t;
80
81 typedef struct _cairo_cogl_pipeline {
82 int n_layers;
83 cairo_operator_t op;
84
85 /* These are not always the same as the operator's, as sometimes
86 * this is a pre-render for a different operator */
87 cairo_bool_t src_bounded;
88 cairo_bool_t mask_bounded;
89
90 cairo_bool_t has_src_tex_clip;
91 cairo_path_fixed_t src_tex_clip;
92 cairo_bool_t has_mask_tex_clip;
93 cairo_path_fixed_t mask_tex_clip;
94 cairo_rectangle_int_t unbounded_extents;
95
96 CoglPipeline *pipeline;
97 } cairo_cogl_pipeline_t;
98
99 typedef enum _cairo_cogl_journal_entry_type {
100 CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE,
101 CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE,
102 CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP
103 } cairo_cogl_journal_entry_type_t;
104
105 typedef struct _cairo_cogl_journal_entry {
106 cairo_cogl_journal_entry_type_t type;
107 } cairo_cogl_journal_entry_t;
108
109 typedef struct _cairo_cogl_journal_clip_entry {
110 cairo_cogl_journal_entry_t base;
111 cairo_clip_t *clip;
112 } cairo_cogl_journal_clip_entry_t;
113
114 typedef struct _cairo_cogl_journal_rect_entry {
115 cairo_cogl_journal_entry_t base;
116 cairo_cogl_pipeline_t *pipeline;
117
118 float x;
119 float y;
120 float width;
121 float height;
122 cairo_matrix_t ctm;
123 } cairo_cogl_journal_rect_entry_t;
124
125 typedef struct _cairo_cogl_journal_prim_entry {
126 cairo_cogl_journal_entry_t base;
127 cairo_cogl_pipeline_t *pipeline;
128
129 CoglPrimitive *primitive;
130 cairo_matrix_t transform;
131 } cairo_cogl_journal_prim_entry_t;
132
133 typedef struct _cairo_cogl_path_fill_meta {
134 cairo_cache_entry_t base;
135 cairo_path_fixed_t path;
136 cairo_fill_rule_t fill_rule;
137 double tolerance;
138
139 CoglPrimitive *prim;
140
141 cairo_freelist_t *freelist;
142 } cairo_cogl_path_fill_meta_t;
143
144 typedef struct _cairo_cogl_path_stroke_meta {
145 cairo_cache_entry_t base;
146 cairo_path_fixed_t path;
147 cairo_stroke_style_t style;
148 double tolerance;
149
150 CoglPrimitive *prim;
151
152 cairo_freelist_t *freelist;
153 } cairo_cogl_path_stroke_meta_t;
154
155 static cairo_surface_t *
156 _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
157 cairo_content_t content,
158 CoglFramebuffer *framebuffer,
159 CoglTexture *texture);
160
161 static void
162 _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface);
163
164 cairo_private extern const cairo_surface_backend_t _cairo_cogl_surface_backend;
165
166 slim_hidden_proto (cairo_cogl_device_create);
167 slim_hidden_proto (cairo_cogl_surface_create_for_fb);
168 slim_hidden_proto (cairo_cogl_onscreen_surface_create);
169 slim_hidden_proto (cairo_cogl_offscreen_surface_create);
170 slim_hidden_proto (cairo_cogl_surface_get_framebuffer);
171 slim_hidden_proto (cairo_cogl_surface_get_texture);
172 slim_hidden_proto (cairo_cogl_surface_end_frame);
173 slim_hidden_proto (cairo_cogl_surface_synchronize);
174
175 static cairo_cogl_device_t *
to_device(cairo_device_t * device)176 to_device (cairo_device_t *device)
177 {
178 return (cairo_cogl_device_t *)device;
179 }
180
181 /* moves trap points such that they become the actual corners of the trapezoid */
182 static void
_sanitize_trap(cairo_trapezoid_t * t)183 _sanitize_trap (cairo_trapezoid_t *t)
184 {
185 cairo_trapezoid_t s = *t;
186
187 #define FIX(lr, tb, p) \
188 if (t->lr.p.y != t->tb) { \
189 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); \
190 t->lr.p.y = s.tb; \
191 }
192 FIX (left, top, p1);
193 FIX (left, bottom, p2);
194 FIX (right, top, p1);
195 FIX (right, bottom, p2);
196 }
197
198 static cairo_status_t
_cairo_cogl_surface_ensure_framebuffer(cairo_cogl_surface_t * surface)199 _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
200 {
201 CoglError *error = NULL;
202
203 if (surface->framebuffer)
204 return CAIRO_STATUS_SUCCESS;
205
206 surface->framebuffer =
207 cogl_offscreen_new_with_texture (surface->texture);
208 if (unlikely (!cogl_framebuffer_allocate (surface->framebuffer,
209 &error)))
210 {
211 g_warning ("Could not create framebuffer for surface: %s",
212 error->message);
213 cogl_error_free (error);
214 cogl_object_unref (surface->framebuffer);
215 surface->framebuffer = NULL;
216 return CAIRO_STATUS_NO_MEMORY;
217 }
218
219 cogl_framebuffer_orthographic (surface->framebuffer, 0, 0,
220 cogl_texture_get_width (surface->texture),
221 cogl_texture_get_height (surface->texture),
222 -1, 100);
223
224 return CAIRO_STATUS_SUCCESS;
225 }
226
227 static cairo_bool_t
_cairo_cogl_surface_get_extents(void * abstract_surface,cairo_rectangle_int_t * extents)228 _cairo_cogl_surface_get_extents (void *abstract_surface,
229 cairo_rectangle_int_t *extents)
230 {
231 cairo_cogl_surface_t *surface = abstract_surface;
232
233 extents->x = 0;
234 extents->y = 0;
235 extents->width = surface->width;
236 extents->height = surface->height;
237
238 return TRUE;
239 }
240
241 /* Taken from cairo-surface-clipper.c */
242 static cairo_status_t
_cairo_path_fixed_add_box(cairo_path_fixed_t * path,const cairo_box_t * box)243 _cairo_path_fixed_add_box (cairo_path_fixed_t *path,
244 const cairo_box_t *box)
245 {
246 cairo_status_t status;
247
248 status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y);
249 if (unlikely (status))
250 return status;
251
252 status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y);
253 if (unlikely (status))
254 return status;
255
256 status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y);
257 if (unlikely (status))
258 return status;
259
260 status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y);
261 if (unlikely (status))
262 return status;
263
264 return _cairo_path_fixed_close_path (path);
265 }
266
267 static void
_cairo_cogl_journal_discard(cairo_cogl_surface_t * surface)268 _cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
269 {
270 GList *l;
271
272 if (!surface->journal) {
273 assert (surface->last_clip == NULL);
274 return;
275 }
276
277 for (l = surface->journal->head; l; l = l->next) {
278 cairo_cogl_journal_entry_t *entry = l->data;
279 gsize entry_size;
280
281 switch (entry->type)
282 {
283 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
284 cairo_cogl_journal_clip_entry_t *clip_entry =
285 (cairo_cogl_journal_clip_entry_t *)entry;
286 _cairo_clip_destroy (clip_entry->clip);
287 entry_size = sizeof (cairo_cogl_journal_clip_entry_t);
288 break;
289 }
290 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
291 cairo_cogl_journal_rect_entry_t *rect_entry =
292 (cairo_cogl_journal_rect_entry_t *)entry;
293 cogl_object_unref (rect_entry->pipeline->pipeline);
294 if (rect_entry->pipeline->has_src_tex_clip)
295 _cairo_path_fixed_fini (&rect_entry->pipeline->src_tex_clip);
296 if (rect_entry->pipeline->has_mask_tex_clip)
297 _cairo_path_fixed_fini (&rect_entry->pipeline->mask_tex_clip);
298 g_free (rect_entry->pipeline);
299 entry_size = sizeof (cairo_cogl_journal_rect_entry_t);
300 break;
301 }
302 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
303 cairo_cogl_journal_prim_entry_t *prim_entry =
304 (cairo_cogl_journal_prim_entry_t *)entry;
305 cogl_object_unref (prim_entry->pipeline->pipeline);
306 if (prim_entry->primitive)
307 cogl_object_unref (prim_entry->primitive);
308 if (prim_entry->pipeline->has_src_tex_clip)
309 _cairo_path_fixed_fini (&prim_entry->pipeline->src_tex_clip);
310 if (prim_entry->pipeline->has_mask_tex_clip)
311 _cairo_path_fixed_fini (&prim_entry->pipeline->mask_tex_clip);
312 g_free (prim_entry->pipeline);
313 entry_size = sizeof (cairo_cogl_journal_prim_entry_t);
314 break;
315 }
316 default:
317 assert (0); /* not reached! */
318 entry_size = 0; /* avoid compiler warning */
319 }
320 g_slice_free1 (entry_size, entry);
321 }
322
323 g_queue_clear (surface->journal);
324
325 if (surface->last_clip) {
326 _cairo_clip_destroy (surface->last_clip);
327 surface->last_clip = NULL;
328 }
329 }
330
331 static void
_cairo_cogl_journal_free(cairo_cogl_surface_t * surface)332 _cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
333 {
334 _cairo_cogl_journal_discard (surface);
335 g_queue_free (surface->journal);
336 surface->journal = NULL;
337 }
338
339 static void
_cairo_cogl_journal_log_primitive(cairo_cogl_surface_t * surface,cairo_cogl_pipeline_t * pipeline,CoglPrimitive * primitive,cairo_matrix_t * transform)340 _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
341 cairo_cogl_pipeline_t *pipeline,
342 CoglPrimitive *primitive,
343 cairo_matrix_t *transform)
344 {
345 cairo_cogl_journal_prim_entry_t *entry;
346
347 if (unlikely (surface->journal == NULL))
348 surface->journal = g_queue_new ();
349
350 /* FIXME: Instead of a GList here we should stack allocate the journal
351 * entries so it would be cheaper to allocate and they can all be freed in
352 * one go after flushing! */
353 entry = g_slice_new (cairo_cogl_journal_prim_entry_t);
354 entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE;
355
356 entry->pipeline = pipeline;
357
358 entry->primitive = primitive;
359 if (primitive)
360 cogl_object_ref (primitive);
361
362 entry->transform = *transform;
363
364 g_queue_push_tail (surface->journal, entry);
365
366 #ifdef DISABLE_BATCHING
367 _cairo_cogl_journal_flush (surface);
368 #else
369 /* To avoid consuming too much memory, flush the journal if it gets
370 * to a certain size */
371 if (g_queue_get_length (surface->journal) > MAX_JOURNAL_SIZE)
372 _cairo_cogl_journal_flush (surface);
373 #endif
374 }
375
376 static void
_cairo_cogl_journal_log_rectangle(cairo_cogl_surface_t * surface,cairo_cogl_pipeline_t * pipeline,float x,float y,float width,float height,cairo_matrix_t * ctm)377 _cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface,
378 cairo_cogl_pipeline_t *pipeline,
379 float x,
380 float y,
381 float width,
382 float height,
383 cairo_matrix_t *ctm)
384 {
385 cairo_cogl_journal_rect_entry_t *entry;
386
387 if (unlikely (surface->journal == NULL))
388 surface->journal = g_queue_new ();
389
390 /* FIXME: Instead of a GList here we should stack allocate the journal
391 * entries so it would be cheaper to allocate and they can all be freed in
392 * one go after flushing! */
393 entry = g_slice_new (cairo_cogl_journal_rect_entry_t);
394 entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE;
395
396 entry->pipeline = pipeline;
397
398 entry->x = x;
399 entry->y = y;
400 entry->width = width;
401 entry->height = height;
402 entry->ctm = *ctm;
403
404 g_queue_push_tail (surface->journal, entry);
405
406 #ifdef DISABLE_BATCHING
407 _cairo_cogl_journal_flush (surface);
408 #else
409 /* To avoid consuming too much memory, flush the journal if it gets
410 * to a certain size */
411 if (g_queue_get_length (surface->journal) > MAX_JOURNAL_SIZE)
412 _cairo_cogl_journal_flush (surface);
413 #endif
414 }
415
416 static void
_cairo_cogl_journal_log_clip(cairo_cogl_surface_t * surface,const cairo_clip_t * clip)417 _cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface,
418 const cairo_clip_t *clip)
419 {
420 cairo_cogl_journal_clip_entry_t *entry;
421
422 if (unlikely (surface->journal == NULL))
423 surface->journal = g_queue_new ();
424
425 /* FIXME: Instead of a GList here we should stack allocate the journal
426 * entries so it would be cheaper to allocate and they can all be freed in
427 * one go after flushing! */
428 entry = g_slice_new (cairo_cogl_journal_clip_entry_t);
429 entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP;
430 entry->clip = _cairo_clip_copy (clip);
431
432 g_queue_push_tail (surface->journal, entry);
433 }
434
435 static CoglAttributeBuffer *
_cairo_cogl_device_allocate_buffer_space(cairo_cogl_device_t * dev,size_t size,size_t * offset,void ** pointer)436 _cairo_cogl_device_allocate_buffer_space (cairo_cogl_device_t *dev,
437 size_t size,
438 size_t *offset,
439 void **pointer)
440 {
441 /* XXX: In the Cogl journal we found it more efficient to have a pool of
442 * buffers that we re-cycle but for now we simply throw away our stack
443 * buffer each time we flush. */
444 if (unlikely (dev->buffer_stack &&
445 (dev->buffer_stack_size - dev->buffer_stack_offset) < size)) {
446 cogl_buffer_unmap (dev->buffer_stack);
447 cogl_object_unref (dev->buffer_stack);
448 dev->buffer_stack = NULL;
449 dev->buffer_stack_size *= 2;
450 }
451
452 if (unlikely (dev->buffer_stack_size < size))
453 dev->buffer_stack_size = size * 2;
454
455 if (unlikely (dev->buffer_stack == NULL)) {
456 dev->buffer_stack =
457 cogl_attribute_buffer_new (dev->cogl_context,
458 dev->buffer_stack_size,
459 NULL);
460 dev->buffer_stack_pointer =
461 cogl_buffer_map (dev->buffer_stack,
462 COGL_BUFFER_ACCESS_WRITE,
463 COGL_BUFFER_MAP_HINT_DISCARD);
464 dev->buffer_stack_offset = 0;
465 }
466
467 *pointer = dev->buffer_stack_pointer + dev->buffer_stack_offset;
468 *offset = dev->buffer_stack_offset;
469
470 dev->buffer_stack_offset += size;
471 return cogl_object_ref (dev->buffer_stack);
472 }
473
474
475 static CoglAttributeBuffer *
_cairo_cogl_traps_to_triangles_buffer(cairo_cogl_surface_t * surface,cairo_traps_t * traps,size_t * offset,cairo_bool_t one_shot)476 _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
477 cairo_traps_t *traps,
478 size_t *offset,
479 cairo_bool_t one_shot)
480 {
481 CoglAttributeBuffer *buffer;
482 int n_traps = traps->num_traps;
483 int i;
484 CoglVertexP2 *triangles;
485 cairo_cogl_device_t *dev = to_device(surface->base.device);
486
487 if (one_shot) {
488 buffer =
489 _cairo_cogl_device_allocate_buffer_space (dev,
490 n_traps * sizeof (CoglVertexP2) * 6,
491 offset,
492 (void **)&triangles);
493 if (!buffer)
494 return NULL;
495 } else {
496 buffer =
497 cogl_attribute_buffer_new (dev->cogl_context,
498 n_traps * sizeof (CoglVertexP2) * 6,
499 NULL);
500 if (!buffer)
501 return NULL;
502 triangles = cogl_buffer_map (buffer,
503 COGL_BUFFER_ACCESS_WRITE,
504 COGL_BUFFER_MAP_HINT_DISCARD);
505 if (!triangles)
506 return NULL;
507 *offset = 0;
508 }
509
510 /* XXX: This is can be very expensive. I'm not sure a.t.m if it's
511 * predominantly the bandwidth required or the cost of the fixed_to_float
512 * conversions but either way we should try using an index buffer to
513 * reduce the amount we upload by 1/3 (offset by allocating and uploading
514 * indices though) sadly though my experience with the intel mesa drivers
515 * is that slow paths can easily be hit when starting to use indices.
516 */
517 for (i = 0; i < n_traps; i++)
518 {
519 CoglVertexP2 *p = &triangles[i * 6];
520 cairo_trapezoid_t *trap = &traps->traps[i];
521
522 p[0].x = _cairo_fixed_to_double (trap->left.p1.x);
523 p[0].y = _cairo_fixed_to_double (trap->left.p1.y);
524
525 p[1].x = _cairo_fixed_to_double (trap->left.p2.x);
526 p[1].y = _cairo_fixed_to_double (trap->left.p2.y);
527
528 p[2].x = _cairo_fixed_to_double (trap->right.p2.x);
529 p[2].y = _cairo_fixed_to_double (trap->right.p2.y);
530
531 p[3].x = p[0].x;
532 p[3].y = p[0].y;
533
534 p[4].x = p[2].x;
535 p[4].y = p[2].y;
536
537 p[5].x = _cairo_fixed_to_double (trap->right.p1.x);
538 p[5].y = _cairo_fixed_to_double (trap->right.p1.y);
539 }
540
541 if (!one_shot)
542 cogl_buffer_unmap (buffer);
543
544 return buffer;
545 }
546
547 static CoglPrimitive *
_cairo_cogl_traps_to_composite_prim(cairo_cogl_surface_t * surface,cairo_traps_t * traps,cairo_bool_t one_shot)548 _cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface,
549 cairo_traps_t *traps,
550 cairo_bool_t one_shot)
551 {
552 int n_traps = traps->num_traps;
553 size_t offset;
554 CoglAttributeBuffer *buffer;
555 CoglPrimitive *prim;
556 CoglAttribute *attributes[3];
557 const char *attrib_names[3] = {"cogl_position_in",
558 "cogl_tex_coord0_in",
559 "cogl_tex_coord1_in"};
560 int i;
561
562 /* XXX: Ideally we would skip tessellating to traps entirely since
563 * given their representation, conversion to triangles is quite expensive.
564 *
565 * This simplifies the conversion to triangles by making the end points of
566 * the two side lines actually just correspond to the corners of the
567 * traps.
568 */
569 for (i = 0; i < n_traps; i++)
570 _sanitize_trap (&traps->traps[i]);
571
572 buffer = _cairo_cogl_traps_to_triangles_buffer (surface,
573 traps,
574 &offset,
575 one_shot);
576
577 /* We create the largest used number of texture coordinate
578 * attributes here to ensure that when cached, they can be reused
579 * with any pipeline that we may associate with a given path. If
580 * the corresponding layer for a texture coordinates attribute
581 * doesn't exist, cogl simply ignores it. Since all of the
582 * attributes are aliasing the same attribute buffer, the overhead
583 * of specifying them is minimal. */
584 for (i = 0; i < 3; i++) {
585 attributes[i] = cogl_attribute_new (buffer,
586 attrib_names[i],
587 sizeof (CoglVertexP2),
588 offset,
589 2,
590 COGL_ATTRIBUTE_TYPE_FLOAT);
591 }
592
593 /* The attributes will have taken references on the buffer */
594 cogl_object_unref (buffer);
595
596 prim =
597 cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
598 n_traps * 6, attributes, 3);
599
600 /* The primitive will now keep the attributes alive... */
601 for (i = 0; i < 3; i++)
602 cogl_object_unref (attributes[i]);
603
604 return prim;
605 }
606
607 /* In order to facilitate path caching, we transform the input path
608 * into a form that will make all translations and rotations of a given
609 * path identical, thereby allowing them to be identified with
610 * conventional path hashing and equivalence functions. A
611 * transformation matrix is also output so that the path can be
612 * transformed back into its original form during rendering. */
613 static cairo_int_status_t
_cairo_cogl_get_untransformed_path(cairo_path_fixed_t * copy,const cairo_path_fixed_t * orig,cairo_matrix_t * transform_out)614 _cairo_cogl_get_untransformed_path (cairo_path_fixed_t *copy,
615 const cairo_path_fixed_t *orig,
616 cairo_matrix_t *transform_out)
617 {
618 cairo_matrix_t transform;
619 cairo_int_status_t status;
620
621 if (orig->buf.base.num_points < 1)
622 return CAIRO_INT_STATUS_NOTHING_TO_DO;
623
624 status = _cairo_path_fixed_init_copy (copy, orig);
625 if (unlikely (status))
626 return status;
627
628 /* First the path is translated so that its first point lies on the
629 * origin. */
630 cairo_matrix_init_translate (&transform,
631 -_cairo_fixed_to_double(orig->buf.points[0].x),
632 -_cairo_fixed_to_double(orig->buf.points[0].y));
633
634 /* Then the path is rotated so that its second point lies on the
635 * x axis. */
636 if (orig->buf.base.num_points > 1) {
637 double x = _cairo_fixed_to_double(orig->buf.points[1].x) -
638 _cairo_fixed_to_double(orig->buf.points[0].x);
639 double y = _cairo_fixed_to_double(orig->buf.points[1].y) -
640 _cairo_fixed_to_double(orig->buf.points[0].y);
641 double hyp = sqrt (x * x + y * y);
642
643 transform.xx = x / hyp;
644 transform.yy = x / hyp;
645 transform.xy = -y / hyp;
646 transform.yx = y / hyp;
647 }
648
649 _cairo_path_fixed_transform (copy, &transform);
650
651 *transform_out = transform;
652 status = cairo_matrix_invert (transform_out);
653 if (unlikely (status)) {
654 _cairo_path_fixed_fini (copy);
655 return status;
656 }
657
658 return CAIRO_INT_STATUS_SUCCESS;
659 }
660
661 static void
_cairo_cogl_path_fill_meta_destroy(cairo_cogl_path_fill_meta_t * meta)662 _cairo_cogl_path_fill_meta_destroy (cairo_cogl_path_fill_meta_t *meta)
663 {
664 _cairo_path_fixed_fini (&meta->path);
665 cogl_object_unref (meta->prim);
666
667 _cairo_freelist_free (meta->freelist, meta);
668 }
669
670 static cairo_bool_t
_cairo_cogl_path_fill_meta_equal(const void * key_a,const void * key_b)671 _cairo_cogl_path_fill_meta_equal (const void *key_a, const void *key_b)
672 {
673 const cairo_cogl_path_fill_meta_t *meta0 = key_a;
674 const cairo_cogl_path_fill_meta_t *meta1 = key_b;
675
676 if (meta0->fill_rule != meta1->fill_rule)
677 return FALSE;
678
679 if (meta0->tolerance != meta1->tolerance)
680 return FALSE;
681
682 if (!_cairo_path_fixed_equal (&meta0->path, &meta1->path))
683 return FALSE;
684
685 return TRUE;
686 }
687
688 static cairo_int_status_t
_cairo_cogl_fill_to_primitive(cairo_cogl_surface_t * surface,const cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_bool_t one_shot,CoglPrimitive ** primitive,cairo_matrix_t * transform)689 _cairo_cogl_fill_to_primitive (cairo_cogl_surface_t *surface,
690 const cairo_path_fixed_t *path,
691 cairo_fill_rule_t fill_rule,
692 double tolerance,
693 cairo_bool_t one_shot,
694 CoglPrimitive **primitive,
695 cairo_matrix_t *transform)
696 {
697 cairo_traps_t traps;
698 cairo_int_status_t status;
699 cairo_cogl_path_fill_meta_t meta;
700 cairo_cogl_path_fill_meta_t *acquired_meta;
701 cairo_cogl_path_fill_meta_t *insert_meta = NULL;
702 cairo_cogl_device_t *dev = to_device (surface->base.device);
703 unsigned long hash;
704
705 *primitive = NULL;
706
707 status = _cairo_cogl_get_untransformed_path (&meta.path,
708 path,
709 transform);
710 if (unlikely (status))
711 return status;
712
713 hash = _cairo_path_fixed_hash (&meta.path);
714 hash = _cairo_hash_bytes (hash, &fill_rule, sizeof (fill_rule));
715 hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance));
716 meta.base.hash = hash;
717 meta.tolerance = tolerance;
718 meta.fill_rule = fill_rule;
719
720 acquired_meta = _cairo_cache_lookup (&dev->path_fill_prim_cache,
721 &meta.base);
722
723 if (acquired_meta) {
724 // g_print ("fill cache hit");
725 *primitive = cogl_object_ref (acquired_meta->prim);
726 _cairo_path_fixed_fini (&meta.path);
727 return CAIRO_STATUS_SUCCESS;
728 }
729
730 _cairo_traps_init (&traps);
731 status = _cairo_path_fixed_fill_to_traps (&meta.path,
732 fill_rule,
733 tolerance,
734 &traps);
735 if (unlikely (status))
736 goto BAIL;
737
738 if (traps.num_traps == 0) {
739 status = CAIRO_INT_STATUS_NOTHING_TO_DO;
740 goto BAIL;
741 }
742
743 *primitive = _cairo_cogl_traps_to_composite_prim (surface,
744 &traps,
745 one_shot);
746 if (unlikely (!*primitive)) {
747 status = CAIRO_INT_STATUS_NO_MEMORY;
748 goto BAIL;
749 }
750
751 insert_meta =
752 _cairo_freelist_alloc (&dev->path_fill_meta_freelist);
753 if (unlikely (!insert_meta)) {
754 status = CAIRO_INT_STATUS_NO_MEMORY;
755 goto BAIL;
756 }
757
758 insert_meta->base.hash = meta.base.hash;
759 insert_meta->base.size =
760 traps.num_traps * sizeof (CoglVertexP2) * 6;
761 insert_meta->tolerance = tolerance;
762 insert_meta->fill_rule = fill_rule;
763 insert_meta->prim = cogl_object_ref (*primitive);
764 insert_meta->freelist = &dev->path_fill_meta_freelist;
765
766 status = _cairo_path_fixed_init_copy (&insert_meta->path,
767 &meta.path);
768 if (unlikely (status))
769 goto BAIL;
770
771 if (unlikely (_cairo_cache_insert (&dev->path_fill_prim_cache,
772 &insert_meta->base)))
773 {
774 g_warning ("Fill primitive cache insertion unsuccessful");
775 goto BAIL;
776 }
777
778 _cairo_path_fixed_fini (&meta.path);
779 _cairo_traps_fini (&traps);
780
781 return status;
782
783 BAIL:
784 if (*primitive) {
785 cogl_object_unref (*primitive);
786 *primitive = NULL;
787 }
788 if (insert_meta)
789 _cairo_cogl_path_fill_meta_destroy (insert_meta);
790 _cairo_path_fixed_fini (&meta.path);
791 _cairo_traps_fini (&traps);
792
793 return status;
794 }
795
796 static cairo_bool_t
_cairo_cogl_stroke_style_equal(const cairo_stroke_style_t * a,const cairo_stroke_style_t * b)797 _cairo_cogl_stroke_style_equal (const cairo_stroke_style_t *a,
798 const cairo_stroke_style_t *b)
799 {
800 if (a->line_width == b->line_width &&
801 a->line_cap == b->line_cap &&
802 a->line_join == b->line_join &&
803 a->miter_limit == b->miter_limit &&
804 a->num_dashes == b->num_dashes &&
805 a->dash_offset == b->dash_offset)
806 {
807 unsigned int i;
808 for (i = 0; i < a->num_dashes; i++) {
809 if (a->dash[i] != b->dash[i])
810 return FALSE;
811 }
812 }
813 return TRUE;
814 }
815
816 static cairo_bool_t
_cairo_cogl_path_stroke_meta_equal(const void * key_a,const void * key_b)817 _cairo_cogl_path_stroke_meta_equal (const void *key_a,
818 const void *key_b)
819 {
820 const cairo_cogl_path_stroke_meta_t *meta0 = key_a;
821 const cairo_cogl_path_stroke_meta_t *meta1 = key_b;
822
823 if (meta0->tolerance != meta1->tolerance)
824 return FALSE;
825
826 if (!_cairo_cogl_stroke_style_equal (&meta0->style, &meta1->style))
827 return FALSE;
828
829 if (!_cairo_path_fixed_equal (&meta0->path, &meta1->path))
830 return FALSE;
831
832 return TRUE;
833 }
834
835 static void
_cairo_cogl_path_stroke_meta_destroy(cairo_cogl_path_stroke_meta_t * meta)836 _cairo_cogl_path_stroke_meta_destroy (cairo_cogl_path_stroke_meta_t *meta)
837 {
838 _cairo_stroke_style_fini (&meta->style);
839 _cairo_path_fixed_fini (&meta->path);
840 cogl_object_unref (meta->prim);
841
842 _cairo_freelist_free (meta->freelist, meta);
843 }
844
845 static unsigned long
_cairo_cogl_stroke_style_hash(unsigned long hash,const cairo_stroke_style_t * style)846 _cairo_cogl_stroke_style_hash (unsigned long hash,
847 const cairo_stroke_style_t *style)
848 {
849 unsigned int i;
850 hash = _cairo_hash_bytes (hash, &style->line_width, sizeof (style->line_width));
851 hash = _cairo_hash_bytes (hash, &style->line_cap, sizeof (style->line_cap));
852 hash = _cairo_hash_bytes (hash, &style->line_join, sizeof (style->line_join));
853 hash = _cairo_hash_bytes (hash, &style->miter_limit, sizeof (style->miter_limit));
854 hash = _cairo_hash_bytes (hash, &style->num_dashes, sizeof (style->num_dashes));
855 hash = _cairo_hash_bytes (hash, &style->dash_offset, sizeof (style->dash_offset));
856 for (i = 0; i < style->num_dashes; i++)
857 hash = _cairo_hash_bytes (hash, &style->dash[i], sizeof (double));
858 return hash;
859 }
860
861 static cairo_int_status_t
_cairo_cogl_stroke_to_primitive(cairo_cogl_surface_t * surface,const cairo_path_fixed_t * path,const cairo_stroke_style_t * style,double tolerance,cairo_bool_t one_shot,CoglPrimitive ** primitive,cairo_matrix_t * transform)862 _cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t *surface,
863 const cairo_path_fixed_t *path,
864 const cairo_stroke_style_t *style,
865 double tolerance,
866 cairo_bool_t one_shot,
867 CoglPrimitive **primitive,
868 cairo_matrix_t *transform)
869 {
870 cairo_traps_t traps;
871 cairo_int_status_t status;
872 cairo_cogl_path_stroke_meta_t meta;
873 cairo_cogl_path_stroke_meta_t *acquired_meta;
874 cairo_cogl_path_stroke_meta_t *insert_meta = NULL;
875 cairo_matrix_t identity;
876 cairo_cogl_device_t *dev = to_device (surface->base.device);
877 unsigned long hash;
878
879 *primitive = NULL;
880
881 status = _cairo_cogl_get_untransformed_path (&meta.path,
882 path,
883 transform);
884 if (unlikely (status))
885 return status;
886
887 hash = _cairo_path_fixed_hash (&meta.path);
888 hash = _cairo_cogl_stroke_style_hash (hash, style);
889 hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance));
890 meta.base.hash = hash;
891 meta.tolerance = tolerance;
892
893 status = _cairo_stroke_style_init_copy (&meta.style, style);
894 if (unlikely (status)) {
895 _cairo_path_fixed_fini (&meta.path);
896 return status;
897 }
898
899 acquired_meta = _cairo_cache_lookup (&dev->path_stroke_prim_cache,
900 &meta.base);
901
902 if (acquired_meta) {
903 // g_print ("stroke cache hit");
904 *primitive = cogl_object_ref (acquired_meta->prim);
905 _cairo_path_fixed_fini (&meta.path);
906 return CAIRO_STATUS_SUCCESS;
907 }
908
909 _cairo_traps_init (&traps);
910
911 cairo_matrix_init_identity (&identity);
912 status = _cairo_path_fixed_stroke_polygon_to_traps (&meta.path,
913 style,
914 &identity,
915 &identity,
916 tolerance,
917 &traps);
918 if (unlikely (status))
919 goto BAIL;
920
921 if (traps.num_traps == 0) {
922 status = CAIRO_INT_STATUS_NOTHING_TO_DO;
923 goto BAIL;
924 }
925
926 *primitive = _cairo_cogl_traps_to_composite_prim (surface,
927 &traps,
928 one_shot);
929 if (unlikely (!*primitive)) {
930 status = CAIRO_INT_STATUS_NO_MEMORY;
931 goto BAIL;
932 }
933
934 insert_meta =
935 _cairo_freelist_alloc (&dev->path_stroke_meta_freelist);
936 if (unlikely (!insert_meta)) {
937 status = CAIRO_INT_STATUS_NO_MEMORY;
938 goto BAIL;
939 }
940
941 insert_meta->base.hash = meta.base.hash;
942 insert_meta->base.size =
943 traps.num_traps * sizeof (CoglVertexP2) * 6;
944 insert_meta->tolerance = tolerance;
945 insert_meta->prim = cogl_object_ref (*primitive);
946 insert_meta->freelist = &dev->path_stroke_meta_freelist;
947
948 status = _cairo_stroke_style_init_copy (&insert_meta->style,
949 style);
950 if (unlikely (status)) {
951 _cairo_stroke_style_fini (&insert_meta->style);
952 free (insert_meta);
953 insert_meta = NULL;
954 goto BAIL;
955 }
956
957 status = _cairo_path_fixed_init_copy (&insert_meta->path,
958 &meta.path);
959 if (unlikely (status))
960 goto BAIL;
961
962 if (unlikely (_cairo_cache_insert (&dev->path_stroke_prim_cache,
963 &insert_meta->base)))
964 {
965 g_warning ("Stroke primitive cache insertion unsuccessful");
966 goto BAIL;
967 }
968
969 _cairo_path_fixed_fini (&meta.path);
970 _cairo_stroke_style_fini (&meta.style);
971 _cairo_traps_fini (&traps);
972
973 return status;
974
975 BAIL:
976 if (*primitive) {
977 cogl_object_unref (*primitive);
978 *primitive = NULL;
979 }
980 if (insert_meta)
981 _cairo_cogl_path_stroke_meta_destroy (insert_meta);
982 _cairo_path_fixed_fini (&meta.path);
983 _cairo_stroke_style_fini (&meta.style);
984 _cairo_traps_fini (&traps);
985
986 return status;
987 }
988
989 static void
_cairo_cogl_set_path_prim_clip(cairo_cogl_surface_t * surface,cairo_path_fixed_t * path,int * clip_stack_depth,cairo_fill_rule_t fill_rule,double tolerance)990 _cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface,
991 cairo_path_fixed_t *path,
992 int *clip_stack_depth,
993 cairo_fill_rule_t fill_rule,
994 double tolerance)
995 {
996 cairo_rectangle_int_t extents;
997 cairo_int_status_t status;
998 CoglPrimitive *prim;
999 cairo_matrix_t transform;
1000 CoglMatrix matrix;
1001 double x1, y1, x2, y2;
1002
1003 status = _cairo_cogl_fill_to_primitive (surface,
1004 path,
1005 fill_rule,
1006 tolerance,
1007 FALSE,
1008 &prim,
1009 &transform);
1010 if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
1011 /* If the clip is of zero fill area, set all clipped */
1012 cogl_framebuffer_push_scissor_clip (surface->framebuffer,
1013 0, 0, 0, 0);
1014 (*clip_stack_depth)++;
1015 return;
1016 } else if (unlikely (status)) {
1017 g_warning ("Failed to get primitive for clip path while "
1018 "flushing journal");
1019 goto BAIL;
1020 }
1021
1022 float transformfv[16] = {
1023 transform.xx, transform.yx, 0, 0,
1024 transform.xy, transform.yy, 0, 0,
1025 0, 0, 1, 0,
1026 transform.x0, transform.y0, 0, 1
1027 };
1028
1029 cogl_matrix_init_from_array (&matrix, transformfv);
1030
1031 cogl_framebuffer_push_matrix (surface->framebuffer);
1032 cogl_framebuffer_transform (surface->framebuffer, &matrix);
1033
1034 _cairo_path_fixed_approximate_clip_extents (path, &extents);
1035
1036 /* The extents need to be transformed by the inverse of the
1037 * modelview matrix because they are in terms of un-transformed
1038 * device coordinates and the bounds coordinates will be
1039 * transformed by the modelview matrix */
1040 status = cairo_matrix_invert (&transform);
1041 if (unlikely (status)) {
1042 g_warning ("Could not apply clip due to invalid matrix from "
1043 "path transformation");
1044 goto BAIL;
1045 }
1046
1047 x1 = extents.x;
1048 y1 = extents.y;
1049 x2 = extents.x + extents.width;
1050 y2 = extents.y + extents.height;
1051 _cairo_matrix_transform_bounding_box (&transform,
1052 &x1, &y1, &x2, &y2,
1053 NULL);
1054
1055 cogl_framebuffer_push_primitive_clip (surface->framebuffer, prim,
1056 x1, y1, x2, y2);
1057 (*clip_stack_depth)++;
1058
1059 cogl_framebuffer_pop_matrix (surface->framebuffer);
1060
1061 BAIL:
1062 if (prim)
1063 cogl_object_unref (prim);
1064 }
1065
1066 /* This is the way in which we handle CAIRO_EXTEND_NONE set on the
1067 * source or mask pattern surfaces, as well as unbounded operators.
1068 * First, we limit the rendering area to the region which will not be
1069 * sampled from beyond the source or mask textures with additional clip
1070 * paths, which were created when we obtained the original pipeline.
1071 * The region will also be limited by the drawing area due to the fact
1072 * we are drawing with the original primitive's vertices.
1073 *
1074 * In order to handle unbounded operators, we do a second rendering pass
1075 * for places outside of such region. We limit the rending to outside
1076 * this region by using a depth buffer to preserve all places where
1077 * rendering took place during the first pass. For this region, we also
1078 * have to remove the CAIRO_EXTEND_NONE clips if the operator is not
1079 * bound by their respective contents. Because OpenGL sets all vertex
1080 * z-values to 0.0 if none are supplied in the attributes data (we only
1081 * supply x and y values), it will update the region in the buffer to a
1082 * value over the default clearing value of 1.0. Given that the default
1083 * test function is GL_LESS, we don't have to set z attributes on the
1084 * vertices of the second rendering pass either, as 0.0 will never be
1085 * less than 0.0. If cogl ever adds a method to clip out a primitive
1086 * instead of just clipping it in, we may be able to use a more
1087 * efficient method using the stencil buffer. */
1088 static void
_cairo_cogl_apply_tex_clips(cairo_cogl_surface_t * surface,int * clip_stack_depth,cairo_cogl_pipeline_t * pipeline)1089 _cairo_cogl_apply_tex_clips (cairo_cogl_surface_t *surface,
1090 int *clip_stack_depth,
1091 cairo_cogl_pipeline_t *pipeline)
1092 {
1093 CoglDepthState depth_state;
1094 CoglBool cogl_status;
1095
1096 /* Enable the depth test if it will be needed */
1097 if ((!pipeline->mask_bounded && pipeline->has_mask_tex_clip) ||
1098 (!pipeline->src_bounded && pipeline->has_src_tex_clip))
1099 {
1100 cogl_depth_state_init (&depth_state);
1101 cogl_depth_state_set_test_enabled (&depth_state, TRUE);
1102 cogl_status = cogl_pipeline_set_depth_state (pipeline->pipeline,
1103 &depth_state,
1104 NULL);
1105 if (unlikely (cogl_status != TRUE))
1106 g_warning ("Error setting depth state for unbounded render");
1107
1108 /* Clear the depth buffer to 1.0. The color values are unused
1109 * placeholders. */
1110 cogl_framebuffer_clear4f (surface->framebuffer,
1111 COGL_BUFFER_BIT_DEPTH,
1112 0.0, 0.0, 0.0, 0.0);
1113 }
1114
1115 if (pipeline->mask_bounded && !pipeline->src_bounded) {
1116 /* Push mask clip first so later we can pop the source clip
1117 * and still be bound by the mask clip */
1118 if (pipeline->has_mask_tex_clip)
1119 _cairo_cogl_set_path_prim_clip (surface,
1120 &pipeline->mask_tex_clip,
1121 clip_stack_depth,
1122 CAIRO_FILL_RULE_WINDING,
1123 0.0);
1124 if (pipeline->has_src_tex_clip)
1125 _cairo_cogl_set_path_prim_clip (surface,
1126 &pipeline->src_tex_clip,
1127 clip_stack_depth,
1128 CAIRO_FILL_RULE_WINDING,
1129 0.0);
1130 } else {
1131 if (pipeline->has_src_tex_clip)
1132 _cairo_cogl_set_path_prim_clip (surface,
1133 &pipeline->src_tex_clip,
1134 clip_stack_depth,
1135 CAIRO_FILL_RULE_WINDING,
1136 0.0);
1137 if (pipeline->has_mask_tex_clip)
1138 _cairo_cogl_set_path_prim_clip (surface,
1139 &pipeline->mask_tex_clip,
1140 clip_stack_depth,
1141 CAIRO_FILL_RULE_WINDING,
1142 0.0);
1143 }
1144 }
1145
1146 /* Get the pipeline for the second pass */
1147 static CoglPipeline *
_cairo_cogl_setup_unbounded_area_pipeline(cairo_cogl_surface_t * surface,cairo_operator_t op)1148 _cairo_cogl_setup_unbounded_area_pipeline (cairo_cogl_surface_t *surface,
1149 cairo_operator_t op)
1150 {
1151 CoglPipeline *unbounded_pipeline;
1152 CoglDepthState depth_state;
1153 CoglBool cogl_status;
1154 cairo_cogl_device_t *dev = to_device(surface->base.device);
1155
1156 /* If a template pipeline exists for any given operator, the
1157 * corresponding solid template pipeline always exists */
1158 unbounded_pipeline =
1159 cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
1160 cogl_pipeline_set_color4f (unbounded_pipeline, 0.0, 0.0, 0.0, 0.0);
1161
1162 /* Enable depth test on second-pass pipeline */
1163 cogl_depth_state_init (&depth_state);
1164 cogl_depth_state_set_test_enabled (&depth_state, TRUE);
1165 cogl_status = cogl_pipeline_set_depth_state (unbounded_pipeline,
1166 &depth_state,
1167 NULL);
1168 if (unlikely (cogl_status != TRUE))
1169 g_warning ("Error setting depth state for unbounded render");
1170
1171 return unbounded_pipeline;
1172 }
1173
1174 static void
_cairo_cogl_unbounded_render(cairo_cogl_surface_t * surface,int * clip_stack_depth,cairo_cogl_pipeline_t * pipeline,cairo_bool_t * needs_vertex_render)1175 _cairo_cogl_unbounded_render (cairo_cogl_surface_t *surface,
1176 int *clip_stack_depth,
1177 cairo_cogl_pipeline_t *pipeline,
1178 cairo_bool_t *needs_vertex_render)
1179 {
1180 /* We will need a second rendering of the original vertices if we
1181 * still need to be bounded by the mask but had a source tex clip */
1182 *needs_vertex_render = FALSE;
1183
1184 /* Pop all unbounded tex clips. Do not pop clips the operator is
1185 * bounded by, so that we can still be bounded by them during the
1186 * second pass (vertex render or extents render). */
1187 if (pipeline->mask_bounded && pipeline->src_bounded) {
1188 /* We don't need a second pass if it will just be in the same
1189 * region as the first */
1190 } else if (pipeline->src_bounded) {
1191 if (pipeline->has_mask_tex_clip) {
1192 cogl_framebuffer_pop_clip (surface->framebuffer);
1193 (*clip_stack_depth)--;
1194 }
1195 } else if (pipeline->mask_bounded) {
1196 if (pipeline->has_src_tex_clip) {
1197 cogl_framebuffer_pop_clip (surface->framebuffer);
1198 (*clip_stack_depth)--;
1199 *needs_vertex_render = TRUE;
1200 }
1201 } else {
1202 if (pipeline->has_src_tex_clip) {
1203 cogl_framebuffer_pop_clip (surface->framebuffer);
1204 (*clip_stack_depth)--;
1205 }
1206 if (pipeline->has_mask_tex_clip) {
1207 cogl_framebuffer_pop_clip (surface->framebuffer);
1208 (*clip_stack_depth)--;
1209 }
1210 }
1211
1212 /* If an operator is unbounded by the mask, we need to render the
1213 * second transparent pass within the full unbounded extents */
1214 if (!pipeline->mask_bounded) {
1215 CoglPipeline *unbounded_pipeline;
1216
1217 /* Draw a transparent rectangle to cover the entire extents */
1218 unbounded_pipeline =
1219 _cairo_cogl_setup_unbounded_area_pipeline (surface,
1220 pipeline->op);
1221 cogl_framebuffer_draw_rectangle (surface->framebuffer,
1222 unbounded_pipeline,
1223 pipeline->unbounded_extents.x,
1224 pipeline->unbounded_extents.y,
1225 pipeline->unbounded_extents.x + pipeline->unbounded_extents.width,
1226 pipeline->unbounded_extents.y + pipeline->unbounded_extents.height);
1227 cogl_object_unref (unbounded_pipeline);
1228 }
1229 }
1230
1231 static void
_cairo_cogl_post_unbounded_render(cairo_cogl_surface_t * surface,int * clip_stack_depth,cairo_cogl_pipeline_t * pipeline)1232 _cairo_cogl_post_unbounded_render (cairo_cogl_surface_t *surface,
1233 int *clip_stack_depth,
1234 cairo_cogl_pipeline_t *pipeline)
1235 {
1236 CoglDepthState depth_state;
1237 CoglBool cogl_status;
1238
1239 /* Disable the depth test */
1240 if ((!pipeline->mask_bounded && pipeline->has_mask_tex_clip) ||
1241 (!pipeline->src_bounded && pipeline->has_src_tex_clip))
1242 {
1243 cogl_depth_state_init (&depth_state);
1244 cogl_depth_state_set_test_enabled (&depth_state, FALSE);
1245 cogl_status = cogl_pipeline_set_depth_state (pipeline->pipeline,
1246 &depth_state,
1247 NULL);
1248 if (unlikely (cogl_status != TRUE))
1249 g_warning ("Error setting depth state after unbounded render");
1250 }
1251
1252 /* Pop all bounded tex clips (those that were not popped before) */
1253 if (pipeline->src_bounded && pipeline->mask_bounded) {
1254 if (pipeline->has_src_tex_clip) {
1255 cogl_framebuffer_pop_clip (surface->framebuffer);
1256 (*clip_stack_depth)--;
1257 }
1258 if (pipeline->has_mask_tex_clip) {
1259 cogl_framebuffer_pop_clip (surface->framebuffer);
1260 (*clip_stack_depth)--;
1261 }
1262 } else if (pipeline->src_bounded) {
1263 if (pipeline->has_src_tex_clip) {
1264 cogl_framebuffer_pop_clip (surface->framebuffer);
1265 (*clip_stack_depth)--;
1266 }
1267 } else if (pipeline->mask_bounded) {
1268 if (pipeline->has_mask_tex_clip) {
1269 cogl_framebuffer_pop_clip (surface->framebuffer);
1270 (*clip_stack_depth)--;
1271 }
1272 } else {
1273 /* We have already popped all of the clips in the
1274 * unbounded_render function */
1275 }
1276 }
1277
1278 static void
_cairo_cogl_journal_flush(cairo_cogl_surface_t * surface)1279 _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
1280 {
1281 GList *l;
1282 cairo_cogl_device_t *dev;
1283 int clip_stack_depth = 0;
1284 int i;
1285
1286 if (!surface->journal)
1287 return;
1288
1289 dev = to_device(surface->base.device);
1290 if (dev->buffer_stack && dev->buffer_stack_offset) {
1291 cogl_buffer_unmap (dev->buffer_stack);
1292 cogl_object_unref (dev->buffer_stack);
1293 dev->buffer_stack = NULL;
1294 }
1295
1296 if (unlikely (_cairo_cogl_surface_ensure_framebuffer (surface))) {
1297 g_warning ("Could not get framebuffer for flushing journal");
1298 assert (0);
1299 }
1300
1301 cogl_framebuffer_push_matrix (surface->framebuffer);
1302
1303 for (l = surface->journal->head; l; l = l->next) {
1304 cairo_cogl_journal_entry_t *entry = l->data;
1305
1306 switch (entry->type)
1307 {
1308 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
1309 cairo_cogl_journal_clip_entry_t *clip_entry =
1310 (cairo_cogl_journal_clip_entry_t *)entry;
1311 cairo_clip_path_t *path;
1312
1313 for (i = 0; i < clip_stack_depth; i++)
1314 cogl_framebuffer_pop_clip (surface->framebuffer);
1315 clip_stack_depth = 0;
1316
1317 if (clip_entry->clip == NULL)
1318 continue; // there is no clip
1319
1320 for (path = clip_entry->clip->path, i = 0;
1321 path;
1322 path = path->prev, i++)
1323 {
1324 _cairo_cogl_set_path_prim_clip (surface,
1325 &path->path,
1326 &clip_stack_depth,
1327 path->fill_rule,
1328 path->tolerance);
1329 }
1330
1331 if (clip_entry->clip->num_boxes > 0) {
1332 cairo_path_fixed_t boxes_path;
1333
1334 _cairo_path_fixed_init (&boxes_path);
1335 for (int i = 0; i < clip_entry->clip->num_boxes; i++) {
1336 if (unlikely (_cairo_path_fixed_add_box (&boxes_path,
1337 &clip_entry->clip->boxes[i])))
1338 {
1339 g_warning ("Could not add all clip boxes while "
1340 "flushing journal");
1341 break;
1342 }
1343 }
1344
1345 _cairo_cogl_set_path_prim_clip (surface,
1346 &boxes_path,
1347 &clip_stack_depth,
1348 CAIRO_FILL_RULE_WINDING,
1349 0.0);
1350
1351 _cairo_path_fixed_fini (&boxes_path);
1352 }
1353
1354 surface->n_clip_updates_per_frame++;
1355 break;
1356 }
1357 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
1358 cairo_cogl_journal_rect_entry_t *rect_entry =
1359 (cairo_cogl_journal_rect_entry_t *)entry;
1360 float tex_coords[8];
1361 float x1 = rect_entry->x;
1362 float y1 = rect_entry->y;
1363 float x2 = rect_entry->x + rect_entry->width;
1364 float y2 = rect_entry->y + rect_entry->height;
1365 cairo_matrix_t *ctm = &rect_entry->ctm;
1366 float ctmfv[16] = {
1367 ctm->xx, ctm->yx, 0, 0,
1368 ctm->xy, ctm->yy, 0, 0,
1369 0, 0, 1, 0,
1370 ctm->x0, ctm->y0, 0, 1
1371 };
1372 CoglMatrix transform;
1373 cairo_bool_t needs_vertex_render;
1374 CoglPipeline *unbounded_pipeline;
1375
1376 cogl_matrix_init_from_array (&transform, ctmfv);
1377
1378 _cairo_cogl_apply_tex_clips (surface,
1379 &clip_stack_depth,
1380 rect_entry->pipeline);
1381
1382 if (rect_entry->pipeline->n_layers) {
1383 g_assert (rect_entry->pipeline->n_layers <= 2);
1384 tex_coords[0] = x1;
1385 tex_coords[1] = y1;
1386 tex_coords[2] = x2;
1387 tex_coords[3] = y2;
1388 if (rect_entry->pipeline->n_layers > 1)
1389 memcpy (&tex_coords[4], tex_coords, sizeof (float) * 4);
1390 }
1391
1392 cogl_framebuffer_push_matrix (surface->framebuffer);
1393 cogl_framebuffer_transform (surface->framebuffer, &transform);
1394 cogl_framebuffer_draw_multitextured_rectangle (surface->framebuffer,
1395 rect_entry->pipeline->pipeline,
1396 x1, y1,
1397 x2, y2,
1398 tex_coords,
1399 4 * rect_entry->pipeline->n_layers);
1400
1401 _cairo_cogl_unbounded_render (surface,
1402 &clip_stack_depth,
1403 rect_entry->pipeline,
1404 &needs_vertex_render);
1405 if (needs_vertex_render) {
1406 unbounded_pipeline =
1407 _cairo_cogl_setup_unbounded_area_pipeline (surface,
1408 rect_entry->pipeline->op);
1409 cogl_framebuffer_draw_multitextured_rectangle (surface->framebuffer,
1410 unbounded_pipeline,
1411 x1, y1,
1412 x2, y2,
1413 tex_coords,
1414 4 * rect_entry->pipeline->n_layers);
1415 cogl_object_unref (unbounded_pipeline);
1416 }
1417 _cairo_cogl_post_unbounded_render (surface,
1418 &clip_stack_depth,
1419 rect_entry->pipeline);
1420
1421 cogl_framebuffer_pop_matrix (surface->framebuffer);
1422 break;
1423 }
1424 case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
1425 cairo_cogl_journal_prim_entry_t *prim_entry =
1426 (cairo_cogl_journal_prim_entry_t *)entry;
1427 CoglMatrix transform;
1428 cairo_bool_t needs_vertex_render;
1429 CoglPipeline *unbounded_pipeline;
1430
1431 _cairo_cogl_apply_tex_clips (surface,
1432 &clip_stack_depth,
1433 prim_entry->pipeline);
1434
1435 cogl_framebuffer_push_matrix (surface->framebuffer);
1436 cairo_matrix_t *ctm = &prim_entry->transform;
1437 float ctmfv[16] = {
1438 ctm->xx, ctm->yx, 0, 0,
1439 ctm->xy, ctm->yy, 0, 0,
1440 0, 0, 1, 0,
1441 ctm->x0, ctm->y0, 0, 1
1442 };
1443 cogl_matrix_init_from_array (&transform, ctmfv);
1444 cogl_framebuffer_transform (surface->framebuffer, &transform);
1445
1446 /* If the primitive is NULL, it means we just draw the
1447 * unbounded rectangle */
1448 if (prim_entry->primitive)
1449 cogl_primitive_draw (prim_entry->primitive,
1450 surface->framebuffer,
1451 prim_entry->pipeline->pipeline);
1452
1453 _cairo_cogl_unbounded_render (surface,
1454 &clip_stack_depth,
1455 prim_entry->pipeline,
1456 &needs_vertex_render);
1457 if (needs_vertex_render && prim_entry->primitive) {
1458 unbounded_pipeline =
1459 _cairo_cogl_setup_unbounded_area_pipeline (surface,
1460 prim_entry->pipeline->op);
1461 cogl_primitive_draw (prim_entry->primitive,
1462 surface->framebuffer,
1463 unbounded_pipeline);
1464 cogl_object_unref (unbounded_pipeline);
1465 }
1466 _cairo_cogl_post_unbounded_render (surface,
1467 &clip_stack_depth,
1468 prim_entry->pipeline);
1469
1470 cogl_framebuffer_pop_matrix (surface->framebuffer);
1471 break;
1472 }
1473 default:
1474 assert (0); /* not reached! */
1475 }
1476 }
1477
1478 cogl_framebuffer_pop_matrix (surface->framebuffer);
1479
1480 for (i = 0; i < clip_stack_depth; i++)
1481 cogl_framebuffer_pop_clip (surface->framebuffer);
1482
1483 _cairo_cogl_journal_discard (surface);
1484 }
1485
1486 static cairo_status_t
_cairo_cogl_surface_flush(void * abstract_surface,unsigned flags)1487 _cairo_cogl_surface_flush (void *abstract_surface,
1488 unsigned flags)
1489 {
1490 cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
1491
1492 if (flags)
1493 return CAIRO_STATUS_SUCCESS;
1494
1495 _cairo_cogl_journal_flush (surface);
1496
1497 return CAIRO_STATUS_SUCCESS;
1498 }
1499
1500 static cairo_status_t
_cairo_cogl_surface_finish(void * abstract_surface)1501 _cairo_cogl_surface_finish (void *abstract_surface)
1502 {
1503 cairo_cogl_surface_t *surface = abstract_surface;
1504
1505 if (surface->texture)
1506 cogl_object_unref (surface->texture);
1507
1508 if (surface->framebuffer)
1509 cogl_object_unref (surface->framebuffer);
1510
1511 if (surface->journal)
1512 _cairo_cogl_journal_free (surface);
1513
1514 /*XXX wtf */
1515 cairo_device_release (surface->base.device);
1516
1517 return CAIRO_STATUS_SUCCESS;
1518 }
1519
1520 static CoglTextureComponents
get_components_from_cairo_content(cairo_content_t content)1521 get_components_from_cairo_content (cairo_content_t content)
1522 {
1523 /* We use RGBA for color-only surfaces due to the fact that cogl
1524 * does not provide a padded XRGB format, thereby making us use
1525 * RGBA formats to represent e.g. CAIRO_FORMAT_RGB24 and doing very
1526 * expensive format conversions on the cpu when images are read
1527 * back */
1528 return (content & CAIRO_CONTENT_COLOR) ?
1529 COGL_TEXTURE_COMPONENTS_RGBA :
1530 COGL_TEXTURE_COMPONENTS_A;
1531 }
1532
1533 static CoglPixelFormat
get_default_cogl_format_from_components(CoglTextureComponents components)1534 get_default_cogl_format_from_components (CoglTextureComponents components)
1535 {
1536 switch (components)
1537 {
1538 case COGL_TEXTURE_COMPONENTS_A:
1539 return COGL_PIXEL_FORMAT_A_8;
1540 case COGL_TEXTURE_COMPONENTS_RG:
1541 return COGL_PIXEL_FORMAT_RG_88;
1542 case COGL_TEXTURE_COMPONENTS_RGB:
1543 case COGL_TEXTURE_COMPONENTS_RGBA:
1544 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
1545 return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
1546 #else
1547 return COGL_PIXEL_FORMAT_ARGB_8888_PRE;
1548 #endif
1549 case COGL_TEXTURE_COMPONENTS_DEPTH:
1550 return COGL_PIXEL_FORMAT_DEPTH_32;
1551 default:
1552 return 0;
1553 }
1554 }
1555
1556 static CoglTextureComponents
get_components_from_cairo_format(cairo_format_t format)1557 get_components_from_cairo_format (cairo_format_t format)
1558 {
1559 switch (format)
1560 {
1561 case CAIRO_FORMAT_A1:
1562 case CAIRO_FORMAT_A8:
1563 return COGL_TEXTURE_COMPONENTS_A;
1564
1565 case CAIRO_FORMAT_RGB16_565:
1566 case CAIRO_FORMAT_RGB24:
1567 case CAIRO_FORMAT_RGB30:
1568 case CAIRO_FORMAT_RGB96F:
1569 return COGL_TEXTURE_COMPONENTS_RGB;
1570
1571 case CAIRO_FORMAT_ARGB32:
1572 case CAIRO_FORMAT_RGBA128F:
1573 return COGL_TEXTURE_COMPONENTS_RGBA;
1574
1575 case CAIRO_FORMAT_INVALID:
1576 default:
1577 g_warning("Cairo format unrepresentable by cogl");
1578 return 0;
1579 }
1580 }
1581
1582 static CoglPixelFormat
1583 get_cogl_format_from_cairo_format (cairo_format_t cairo_format);
1584
1585 /* XXX: We often use RGBA format for onscreen framebuffers so make sure
1586 * to handle CAIRO_FORMAT_INVALID sensibly */
1587 static cairo_format_t
get_cairo_format_from_cogl_format(CoglPixelFormat format)1588 get_cairo_format_from_cogl_format (CoglPixelFormat format)
1589 {
1590 switch ((int)format)
1591 {
1592 case COGL_PIXEL_FORMAT_A_8:
1593 return CAIRO_FORMAT_A8;
1594 case COGL_PIXEL_FORMAT_RGB_565:
1595 return CAIRO_FORMAT_RGB16_565;
1596 case COGL_PIXEL_FORMAT_RG_88:
1597 g_warning ("cairo cannot handle red-green textures");
1598 return CAIRO_FORMAT_INVALID;
1599 case COGL_PIXEL_FORMAT_DEPTH_32:
1600 g_warning ("cairo cannot handle depth textures");
1601 return CAIRO_FORMAT_INVALID;
1602
1603 case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
1604 case COGL_PIXEL_FORMAT_ARGB_8888_PRE:
1605 case COGL_PIXEL_FORMAT_RGBA_8888_PRE:
1606 /* Note: this is ambiguous since CAIRO_FORMAT_RGB24
1607 * would also map to the same CoglPixelFormat */
1608 return CAIRO_FORMAT_ARGB32;
1609
1610 default:
1611 g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d",
1612 format,
1613 format & COGL_A_BIT,
1614 format & COGL_BGR_BIT,
1615 format & COGL_PREMULT_BIT,
1616 format & ~(COGL_A_BIT | COGL_BGR_BIT | COGL_PREMULT_BIT));
1617 return CAIRO_FORMAT_INVALID;
1618 }
1619 }
1620
1621 static CoglPixelFormat
get_cogl_format_from_cairo_format(cairo_format_t cairo_format)1622 get_cogl_format_from_cairo_format (cairo_format_t cairo_format)
1623 {
1624 switch (cairo_format)
1625 {
1626 case CAIRO_FORMAT_ARGB32:
1627 case CAIRO_FORMAT_RGB24:
1628 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
1629 return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
1630 #else
1631 return COGL_PIXEL_FORMAT_ARGB_8888_PRE;
1632 #endif
1633 case CAIRO_FORMAT_A8:
1634 return COGL_PIXEL_FORMAT_A_8;
1635 case CAIRO_FORMAT_RGB16_565:
1636 return COGL_PIXEL_FORMAT_RGB_565;
1637 case CAIRO_FORMAT_INVALID:
1638 case CAIRO_FORMAT_A1:
1639 case CAIRO_FORMAT_RGB30:
1640 case CAIRO_FORMAT_RGB96F:
1641 case CAIRO_FORMAT_RGBA128F:
1642 return 0;
1643 }
1644
1645 g_warn_if_reached ();
1646 return 0;
1647 }
1648
1649 static cairo_surface_t *
_cairo_cogl_surface_create_similar(void * abstract_surface,cairo_content_t content,int width,int height)1650 _cairo_cogl_surface_create_similar (void *abstract_surface,
1651 cairo_content_t content,
1652 int width,
1653 int height)
1654 {
1655 cairo_cogl_surface_t *reference_surface = abstract_surface;
1656 cairo_cogl_surface_t *surface;
1657 CoglTexture *texture;
1658 cairo_status_t status;
1659 cairo_cogl_device_t *dev =
1660 to_device(reference_surface->base.device);
1661 int tex_width = width;
1662 int tex_height = height;
1663
1664 /* In the case of lack of NPOT texture support, we allocate texture
1665 * with dimensions of the next power of two */
1666 if (!dev->has_npots) {
1667 tex_width = pow (2, ceil (log2 (tex_width)));
1668 tex_height = pow (2, ceil (log2 (tex_height)));
1669 }
1670
1671 texture = cogl_texture_2d_new_with_size (dev->cogl_context,
1672 tex_width, tex_height);
1673 if (!texture)
1674 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1675
1676 cogl_texture_set_components (texture,
1677 get_components_from_cairo_content (content));
1678
1679 surface = (cairo_cogl_surface_t *)
1680 _cairo_cogl_surface_create_full (dev, content, NULL, texture);
1681 if (unlikely (surface->base.status))
1682 return &surface->base;
1683
1684 /* The surface will take a reference on the texture */
1685 cogl_object_unref (texture);
1686
1687 /* If we passed a texture with larger dimensions, we need to set
1688 * the surface dimensions */
1689 surface->width = width;
1690 surface->height = height;
1691
1692 status = _cairo_cogl_surface_ensure_framebuffer (surface);
1693 if (unlikely (status)) {
1694 cairo_surface_destroy (&surface->base);
1695 return _cairo_surface_create_in_error (status);
1696 }
1697
1698 return &surface->base;
1699 }
1700
1701 static cairo_status_t
_cairo_cogl_surface_read_rect_to_image_surface(cairo_cogl_surface_t * surface,cairo_rectangle_int_t * interest,cairo_image_surface_t ** image_out)1702 _cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t *surface,
1703 cairo_rectangle_int_t *interest,
1704 cairo_image_surface_t **image_out)
1705 {
1706 cairo_image_surface_t *image;
1707 cairo_status_t status;
1708 cairo_format_t cairo_format;
1709 CoglPixelFormat cogl_format;
1710
1711 status = _cairo_cogl_surface_ensure_framebuffer (surface);
1712 if (unlikely (status))
1713 return status;
1714
1715 if (surface->texture)
1716 {
1717 cogl_format =
1718 get_default_cogl_format_from_components (
1719 cogl_texture_get_components (surface->texture) );
1720 cairo_format = get_cairo_format_from_cogl_format (cogl_format);
1721 } else {
1722 cairo_format =
1723 _cairo_format_from_content (surface->base.content);
1724 cogl_format = get_cogl_format_from_cairo_format (cairo_format);
1725 }
1726
1727 image = (cairo_image_surface_t *)
1728 cairo_image_surface_create (cairo_format,
1729 surface->width,
1730 surface->height);
1731 if (image->base.status)
1732 return image->base.status;
1733
1734 cogl_framebuffer_read_pixels (surface->framebuffer, 0, 0,
1735 surface->width, surface->height,
1736 cogl_format, image->data);
1737
1738 *image_out = image;
1739
1740 return CAIRO_STATUS_SUCCESS;
1741 }
1742
1743 static cairo_status_t
_cairo_cogl_surface_acquire_source_image(void * abstract_surface,cairo_image_surface_t ** image_out,void ** image_extra)1744 _cairo_cogl_surface_acquire_source_image (void *abstract_surface,
1745 cairo_image_surface_t **image_out,
1746 void **image_extra)
1747 {
1748 cairo_cogl_surface_t *surface = abstract_surface;
1749 cairo_status_t status;
1750
1751 if (unlikely (_cairo_surface_flush (abstract_surface, 0)))
1752 g_warning ("Error flushing journal while acquiring image");
1753
1754 if (surface->texture) {
1755 CoglTextureComponents components =
1756 cogl_texture_get_components(surface->texture);
1757 CoglPixelFormat cogl_format =
1758 get_default_cogl_format_from_components (components);
1759 cairo_format_t cairo_format =
1760 get_cairo_format_from_cogl_format (cogl_format);
1761 if (cairo_format == CAIRO_FORMAT_INVALID) {
1762 cairo_format = CAIRO_FORMAT_ARGB32;
1763 cogl_format =
1764 get_cogl_format_from_cairo_format (cairo_format);
1765 }
1766
1767 /* We use the actual texture dimensions here instead, because
1768 * if we have a larger texture than the surface dimensions for
1769 * devices not supporting NPOT textures, the surface dimensions
1770 * will not be able to fit the data */
1771 cairo_image_surface_t *image = (cairo_image_surface_t *)
1772 cairo_image_surface_create (cairo_format,
1773 cogl_texture_get_width (surface->texture),
1774 cogl_texture_get_height (surface->texture));
1775 if (image->base.status)
1776 return image->base.status;
1777
1778 cogl_texture_get_data (surface->texture,
1779 cogl_format,
1780 0,
1781 image->data);
1782
1783 /* If the texture dimensions were different than the surface
1784 * dimensions, this will set them to the correct values.
1785 * Because the stride stays the same, it will still function
1786 * correctly */
1787 image->width = surface->width;
1788 image->height = surface->height;
1789
1790 image->base.is_clear = FALSE;
1791 *image_out = image;
1792 } else {
1793 cairo_rectangle_int_t extents = {
1794 0, 0, surface->width, surface->height
1795 };
1796 status = _cairo_cogl_surface_read_rect_to_image_surface (surface, &extents,
1797 image_out);
1798 if (unlikely (status))
1799 return status;
1800 }
1801
1802 *image_extra = NULL;
1803
1804 return CAIRO_STATUS_SUCCESS;
1805 }
1806
1807 static void
_cairo_cogl_surface_release_source_image(void * abstract_surface,cairo_image_surface_t * image,void * image_extra)1808 _cairo_cogl_surface_release_source_image (void *abstract_surface,
1809 cairo_image_surface_t *image,
1810 void *image_extra)
1811 {
1812 cairo_surface_destroy (&image->base);
1813 }
1814
1815 static cairo_status_t
_cairo_cogl_surface_clear(cairo_cogl_surface_t * surface,const cairo_color_t * color)1816 _cairo_cogl_surface_clear (cairo_cogl_surface_t *surface,
1817 const cairo_color_t *color)
1818 {
1819 /* Anything batched in the journal up until now is redundant... */
1820 _cairo_cogl_journal_discard (surface);
1821
1822 /* XXX: we currently implicitly clear the depth and stencil buffer here
1823 * but since we use the framebuffer_discard extension when available I
1824 * suppose this doesn't matter too much.
1825 *
1826 * The main concern is that we want to avoid re-loading an external z
1827 * buffer at the start of each frame, but also many gpu architectures have
1828 * optimizations for how they handle the depth/stencil buffers and can get
1829 * upset if they aren't cleared together at the start of the frame.
1830 *
1831 * FIXME: we need a way to assert that the clip stack currently isn't
1832 * using the stencil buffer before clearing it here!
1833 */
1834 cogl_framebuffer_clear4f (surface->framebuffer,
1835 COGL_BUFFER_BIT_COLOR |
1836 COGL_BUFFER_BIT_DEPTH |
1837 COGL_BUFFER_BIT_STENCIL,
1838 color->red * color->alpha,
1839 color->green * color->alpha,
1840 color->blue * color->alpha,
1841 color->alpha);
1842 return CAIRO_STATUS_SUCCESS;
1843 }
1844
1845 cairo_status_t
_cairo_cogl_path_fixed_rectangle(cairo_path_fixed_t * path,cairo_fixed_t x,cairo_fixed_t y,cairo_fixed_t width,cairo_fixed_t height)1846 _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
1847 cairo_fixed_t x,
1848 cairo_fixed_t y,
1849 cairo_fixed_t width,
1850 cairo_fixed_t height)
1851 {
1852 cairo_status_t status;
1853
1854 status = _cairo_path_fixed_move_to (path, x, y);
1855 if (unlikely (status))
1856 return status;
1857
1858 status = _cairo_path_fixed_rel_line_to (path, width, 0);
1859 if (unlikely (status))
1860 return status;
1861
1862 status = _cairo_path_fixed_rel_line_to (path, 0, height);
1863 if (unlikely (status))
1864 return status;
1865
1866 status = _cairo_path_fixed_rel_line_to (path, -width, 0);
1867 if (unlikely (status))
1868 return status;
1869
1870 status = _cairo_path_fixed_close_path (path);
1871 if (unlikely (status))
1872 return status;
1873
1874 return CAIRO_STATUS_SUCCESS;
1875 }
1876
1877
1878 static CoglPipelineWrapMode
get_cogl_wrap_mode_for_extend(cairo_extend_t extend_mode,cairo_cogl_device_t * dev)1879 get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode,
1880 cairo_cogl_device_t *dev)
1881 {
1882 switch (extend_mode)
1883 {
1884 case CAIRO_EXTEND_NONE:
1885 return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
1886 case CAIRO_EXTEND_PAD:
1887 return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
1888 case CAIRO_EXTEND_REPEAT:
1889 return COGL_PIPELINE_WRAP_MODE_REPEAT;
1890 case CAIRO_EXTEND_REFLECT:
1891 if (!dev->has_mirrored_repeat)
1892 /* If the hardware cannot support mirrored repeating, we
1893 * emulate it elsewhere */
1894 return COGL_PIPELINE_WRAP_MODE_REPEAT;
1895 else
1896 return COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT;
1897 }
1898 assert (0); /* not reached */
1899 return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
1900 }
1901
1902 static CoglPipelineFilter
get_cogl_filter_for_filter(cairo_filter_t filter)1903 get_cogl_filter_for_filter (cairo_filter_t filter)
1904 {
1905 switch (filter)
1906 {
1907 case CAIRO_FILTER_FAST:
1908 case CAIRO_FILTER_NEAREST:
1909 return COGL_PIPELINE_FILTER_NEAREST;
1910
1911 case CAIRO_FILTER_GOOD:
1912 case CAIRO_FILTER_BEST:
1913 case CAIRO_FILTER_BILINEAR:
1914 return COGL_PIPELINE_FILTER_LINEAR;
1915
1916 case CAIRO_FILTER_GAUSSIAN:
1917 default:
1918 g_warning("Invalid pattern filter");
1919 return COGL_PIPELINE_FILTER_NEAREST;
1920 }
1921 }
1922
1923 static void
_cairo_cogl_matrix_all_scale(cairo_matrix_t * matrix,double xscale,double yscale)1924 _cairo_cogl_matrix_all_scale (cairo_matrix_t *matrix,
1925 double xscale,
1926 double yscale)
1927 {
1928 /* Since cairo_matrix_scale does not scale the x0 and y0 components,
1929 * which is required for scaling translations to normalized
1930 * coordinates, use a custom solution here. */
1931 matrix->xx *= xscale;
1932 matrix->yx *= yscale;
1933 matrix->xy *= xscale;
1934 matrix->yy *= yscale;
1935 matrix->x0 *= xscale;
1936 matrix->y0 *= yscale;
1937 }
1938
1939 static CoglTexture *
_cairo_cogl_scale_texture(CoglContext * context,CoglTexture * texture_in,unsigned int new_width,unsigned int new_height,cairo_bool_t do_mirror_texture,cairo_bool_t always_new_texture)1940 _cairo_cogl_scale_texture (CoglContext *context,
1941 CoglTexture *texture_in,
1942 unsigned int new_width,
1943 unsigned int new_height,
1944 cairo_bool_t do_mirror_texture,
1945 cairo_bool_t always_new_texture)
1946 {
1947 CoglTexture *texture_out = NULL;
1948 CoglPipeline *copying_pipeline = NULL;
1949 CoglFramebuffer *fb = NULL;
1950 CoglError *error = NULL;
1951 unsigned int tex_width = new_width;
1952 unsigned int tex_height = new_height;
1953
1954 /* If the texture is already in the desired dimensions and we are
1955 * not mirroring it, copying it, or reading from different extents,
1956 * return it unmodified */
1957 if (!do_mirror_texture && !always_new_texture &&
1958 new_width == cogl_texture_get_width (texture_in) &&
1959 new_height == cogl_texture_get_height (texture_in))
1960 return texture_in;
1961
1962 if (do_mirror_texture) {
1963 tex_width *= 2;
1964 tex_height *= 2;
1965 }
1966
1967 texture_out =
1968 cogl_texture_2d_new_with_size (context, tex_width, tex_height);
1969 if (unlikely (!texture_out)) {
1970 g_warning ("Failed to get texture for scaling");
1971 goto BAIL;
1972 }
1973
1974 cogl_texture_set_components (texture_out,
1975 cogl_texture_get_components (texture_in));
1976
1977 fb = cogl_offscreen_new_with_texture (texture_out);
1978 if (unlikely (!cogl_framebuffer_allocate (fb, &error))) {
1979 g_warning ("Could not get framebuffer for texture scaling: %s",
1980 error->message);
1981 cogl_error_free (error);
1982 goto BAIL;
1983 }
1984
1985 cogl_framebuffer_orthographic (fb, 0, 0,
1986 tex_width, tex_height,
1987 -1, 100);
1988
1989 copying_pipeline = cogl_pipeline_new (context);
1990 cogl_pipeline_set_layer_texture (copying_pipeline, 0, texture_in);
1991 cogl_pipeline_set_layer_filters (copying_pipeline, 0,
1992 COGL_PIPELINE_FILTER_NEAREST,
1993 COGL_PIPELINE_FILTER_NEAREST);
1994
1995 if (do_mirror_texture) {
1996 /* Draw four rectangles to the new texture with the appropriate
1997 * reflection on each one */
1998
1999 const float rect_coordinates[32] = {
2000 /* Rectangle 1 */
2001 0, 0, 0.5 * tex_width, 0.5 * tex_height,
2002 0, 0, 1, 1,
2003
2004 /* Rectangle 2 */
2005 tex_width, 0, 0.5 * tex_width, 0.5 * tex_height,
2006 0, 0, 1, 1,
2007
2008 /* Rectangle 3 */
2009 0, tex_height, 0.5 * tex_width, 0.5 * tex_height,
2010 0, 0, 1, 1,
2011
2012 /* Rectangle 4 */
2013 tex_width, tex_height, 0.5 * tex_width, 0.5 * tex_height,
2014 0, 0, 1, 1
2015 };
2016
2017 cogl_framebuffer_draw_textured_rectangles (fb,
2018 copying_pipeline,
2019 rect_coordinates,
2020 4);
2021 } else {
2022 cogl_framebuffer_draw_textured_rectangle (fb,
2023 copying_pipeline,
2024 0, 0,
2025 tex_width,
2026 tex_height,
2027 0, 0, 1, 1);
2028 }
2029
2030 cogl_object_unref (fb);
2031 cogl_object_unref (copying_pipeline);
2032 cogl_object_unref (texture_in);
2033
2034 return texture_out;
2035
2036 BAIL:
2037 if (texture_out)
2038 cogl_object_unref (texture_out);
2039 if (fb)
2040 cogl_object_unref (fb);
2041 if (copying_pipeline)
2042 cogl_object_unref (copying_pipeline);
2043
2044 return NULL;
2045 }
2046
2047 /* NB: a reference for the texture is transferred to the caller which
2048 * should be unrefed */
2049 static CoglTexture *
_cairo_cogl_acquire_cogl_surface_texture(cairo_cogl_surface_t * reference_surface,cairo_surface_t * surface,const cairo_rectangle_int_t * surface_extents,const cairo_matrix_t * pattern_matrix,cairo_matrix_t * out_matrix,const cairo_bool_t need_mirrored_texture,cairo_bool_t * is_mirrored_texture)2050 _cairo_cogl_acquire_cogl_surface_texture (cairo_cogl_surface_t *reference_surface,
2051 cairo_surface_t *surface,
2052 const cairo_rectangle_int_t *surface_extents,
2053 const cairo_matrix_t *pattern_matrix,
2054 cairo_matrix_t *out_matrix,
2055 const cairo_bool_t need_mirrored_texture,
2056 cairo_bool_t *is_mirrored_texture)
2057 {
2058 CoglTexture *texture;
2059 cairo_surface_t *clone = NULL;
2060 cairo_cogl_surface_t *cogl_surface =
2061 (cairo_cogl_surface_t *)surface;
2062 cairo_bool_t do_mirror_texture;
2063 cairo_cogl_device_t *dev =
2064 to_device (reference_surface->base.device);
2065 double xscale, yscale;
2066 int new_width = surface_extents->width;
2067 int new_height = surface_extents->height;
2068
2069 if (surface_extents->x < 0 || surface_extents->y < 0 ||
2070 (surface_extents->x + surface_extents->width) >
2071 cogl_surface->width ||
2072 (surface_extents->y + surface_extents->height) >
2073 cogl_surface->height)
2074 return NULL;
2075
2076 *out_matrix = *pattern_matrix;
2077 *is_mirrored_texture = FALSE;
2078
2079 if (unlikely (_cairo_surface_flush (surface, 0))) {
2080 g_warning ("Error flushing source surface while getting "
2081 "pattern texture");
2082 goto BAIL;
2083 }
2084
2085 *is_mirrored_texture =
2086 need_mirrored_texture || cogl_surface->is_mirrored_snapshot;
2087 do_mirror_texture =
2088 need_mirrored_texture && !cogl_surface->is_mirrored_snapshot;
2089
2090 /* There seems to be a bug in which cogl isn't flushing its own
2091 * internal journal when reading from dependent sub-textures.
2092 * If this is ever fixed, the following block of code can be
2093 * removed. */
2094 {
2095 _cairo_cogl_surface_ensure_framebuffer (cogl_surface);
2096 cogl_framebuffer_finish (cogl_surface->framebuffer);
2097 }
2098 /* We copy the surface to a new texture, thereby making a
2099 * snapshot of it, as its contents may change between the time
2100 * we log the pipeline and when we flush the journal. The sub
2101 * texture itself cannot be used while drawing primitives, so we do
2102 * a copy to a 2d texture. */
2103 texture = cogl_sub_texture_new (dev->cogl_context,
2104 cogl_surface->texture,
2105 surface_extents->x,
2106 surface_extents->y,
2107 surface_extents->width,
2108 surface_extents->height);
2109 if (unlikely (!texture))
2110 goto BAIL;
2111
2112 /* If we do not support NPOT dimensions, scale the new texture to
2113 * the next power of two while copying */
2114 if (!dev->has_npots) {
2115 new_width = (int)pow (2, ceil (log2 (new_width)));
2116 new_height = (int)pow (2, ceil (log2 (new_height)));
2117 }
2118 texture = _cairo_cogl_scale_texture (dev->cogl_context,
2119 texture,
2120 new_width,
2121 new_height,
2122 do_mirror_texture,
2123 TRUE);
2124 if (unlikely (!texture))
2125 goto BAIL;
2126
2127 clone =
2128 _cairo_cogl_surface_create_full (dev,
2129 reference_surface->base.content,
2130 NULL,
2131 texture);
2132 if (unlikely (clone->status)) {
2133 g_warning ("Could not get clone surface for texture");
2134 goto BAIL;
2135 }
2136 _cairo_surface_attach_snapshot (surface, clone, NULL);
2137
2138 /* Attaching the snapshot will take a reference on the clone surface... */
2139 cairo_surface_destroy (clone);
2140 clone = NULL;
2141
2142 /* Convert from un-normalized source coordinates in backend
2143 * coordinates to normalized texture coordinates. */
2144 if (*is_mirrored_texture) {
2145 xscale = 0.5 / surface_extents->width;
2146 yscale = 0.5 / surface_extents->height;
2147 } else {
2148 xscale = 1.0 / surface_extents->width;
2149 yscale = 1.0 / surface_extents->height;
2150 }
2151 _cairo_cogl_matrix_all_scale (out_matrix, xscale, yscale);
2152
2153 return texture;
2154
2155 BAIL:
2156 if (texture)
2157 cogl_object_unref (texture);
2158 if (clone)
2159 cairo_surface_destroy (clone);
2160
2161 return NULL;
2162 }
2163
2164 /* NB: a reference for the texture is transferred to the caller which
2165 * should be unrefed */
2166 static CoglTexture *
_cairo_cogl_acquire_recording_surface_texture(cairo_cogl_surface_t * reference_surface,cairo_surface_t * surface,const cairo_rectangle_int_t * extents,const cairo_matrix_t * pattern_matrix,cairo_matrix_t * out_matrix,const cairo_bool_t need_mirrored_texture,cairo_bool_t * is_mirrored_texture)2167 _cairo_cogl_acquire_recording_surface_texture (cairo_cogl_surface_t *reference_surface,
2168 cairo_surface_t *surface,
2169 const cairo_rectangle_int_t *extents,
2170 const cairo_matrix_t *pattern_matrix,
2171 cairo_matrix_t *out_matrix,
2172 const cairo_bool_t need_mirrored_texture,
2173 cairo_bool_t *is_mirrored_texture)
2174 {
2175 CoglTexture *texture = NULL;
2176 cairo_surface_t *clone = NULL;
2177 cairo_cogl_device_t *dev =
2178 to_device (reference_surface->base.device);
2179 cairo_matrix_t transform;
2180 int tex_height, tex_width;
2181 double xscale, yscale;
2182
2183 *is_mirrored_texture = FALSE;
2184
2185 /* We will pre-transform all of the drawing by the pattern matrix
2186 * and confine it to the required extents, so no later transform
2187 * will be required */
2188 cairo_matrix_init_translate (out_matrix, -extents->x, -extents->y);
2189
2190 cairo_matrix_init_translate (&transform, extents->x, extents->y);
2191 cairo_matrix_multiply (&transform, &transform, pattern_matrix);
2192
2193 if (!dev->has_npots) {
2194 /* Record to a texture sized to the next power of two */
2195 tex_width = (int)pow (2, ceil (log2 (extents->width)));
2196 tex_height = (int)pow (2, ceil (log2 (extents->height)));
2197
2198 /* And scale accordingly */
2199 cairo_matrix_scale (&transform,
2200 (double)extents->width / (double)tex_width,
2201 (double)extents->height / (double)tex_height);
2202 } else {
2203 tex_width = extents->width;
2204 tex_height = extents->height;
2205 }
2206
2207 texture = cogl_texture_2d_new_with_size (dev->cogl_context,
2208 tex_width,
2209 tex_height);
2210 if (unlikely (!texture)) {
2211 g_warning ("Failed to create texture for replaying recording "
2212 "surface");
2213 goto BAIL;
2214 }
2215
2216 cogl_texture_set_components (texture,
2217 get_components_from_cairo_content (surface->content));
2218
2219 /* Do not attach this as a snapshot, as it only represents part of
2220 * the surface */
2221 clone =
2222 _cairo_cogl_surface_create_full (dev,
2223 reference_surface->base.content,
2224 NULL,
2225 texture);
2226 if (unlikely (_cairo_cogl_surface_ensure_framebuffer ((cairo_cogl_surface_t *)clone)))
2227 {
2228 g_warning ("Could not get framebuffer for replaying recording "
2229 "surface");
2230 goto BAIL;
2231 }
2232
2233 if (unlikely (_cairo_recording_surface_replay_with_clip (surface,
2234 &transform,
2235 clone,
2236 NULL)))
2237 {
2238 g_warning ("Could not replay recording surface");
2239 goto BAIL;
2240 }
2241 _cairo_cogl_journal_flush ((cairo_cogl_surface_t *)clone);
2242 cairo_surface_destroy (clone);
2243
2244 if (need_mirrored_texture) {
2245 /* Scale to the same image extents, but mirror the texture,
2246 * thereby making it larger */
2247 texture = _cairo_cogl_scale_texture (dev->cogl_context,
2248 texture,
2249 tex_width,
2250 tex_height,
2251 TRUE,
2252 FALSE);
2253 if (unlikely (!texture))
2254 goto BAIL;
2255
2256 *is_mirrored_texture = TRUE;
2257 }
2258
2259 /* Convert from un-normalized source coordinates in backend
2260 * coordinates to normalized texture coordinates. */
2261 if (*is_mirrored_texture) {
2262 xscale = 0.5 / extents->width;
2263 yscale = 0.5 / extents->height;
2264 } else {
2265 xscale = 1.0 / extents->width;
2266 yscale = 1.0 / extents->height;
2267 }
2268 _cairo_cogl_matrix_all_scale (out_matrix, xscale, yscale);
2269
2270 return texture;
2271
2272 BAIL:
2273 if (clone)
2274 cairo_surface_destroy (clone);
2275 if (texture)
2276 cogl_object_unref (texture);
2277
2278 return NULL;
2279 }
2280
2281 /* NB: a reference for the texture is transferred to the caller which
2282 * should be unrefed */
2283 static CoglTexture *
_cairo_cogl_acquire_generic_surface_texture(cairo_cogl_surface_t * reference_surface,cairo_surface_t * surface,const cairo_matrix_t * pattern_matrix,cairo_matrix_t * out_matrix,const cairo_bool_t need_mirrored_texture,cairo_bool_t * is_mirrored_texture)2284 _cairo_cogl_acquire_generic_surface_texture (cairo_cogl_surface_t *reference_surface,
2285 cairo_surface_t *surface,
2286 const cairo_matrix_t *pattern_matrix,
2287 cairo_matrix_t *out_matrix,
2288 const cairo_bool_t need_mirrored_texture,
2289 cairo_bool_t *is_mirrored_texture)
2290 {
2291 CoglTexture *texture = NULL;
2292 cairo_image_surface_t *image;
2293 cairo_image_surface_t *acquired_image = NULL;
2294 void *image_extra;
2295 cairo_image_surface_t *image_clone = NULL;
2296 CoglBitmap *bitmap;
2297 CoglError *error = NULL;
2298 cairo_surface_t *clone = NULL;
2299 CoglPixelFormat format;
2300 cairo_cogl_device_t *dev =
2301 to_device (reference_surface->base.device);
2302 ptrdiff_t stride;
2303 unsigned char *data;
2304 double xscale, yscale;
2305
2306 *out_matrix = *pattern_matrix;
2307 *is_mirrored_texture = FALSE;
2308
2309 if (_cairo_surface_is_image (surface)) {
2310 image = (cairo_image_surface_t *)surface;
2311 } else {
2312 cairo_status_t status =
2313 _cairo_surface_acquire_source_image (surface,
2314 &acquired_image,
2315 &image_extra);
2316 if (unlikely (status)) {
2317 g_warning ("acquire_source_image failed: %s [%d]",
2318 cairo_status_to_string (status), status);
2319 return NULL;
2320 }
2321 image = acquired_image;
2322 }
2323
2324 format = get_cogl_format_from_cairo_format (image->format);
2325 if (!format) {
2326 image_clone = _cairo_image_surface_coerce (image);
2327 if (unlikely (image_clone->base.status)) {
2328 g_warning ("image_surface_coerce failed");
2329 texture = NULL;
2330 goto BAIL;
2331 }
2332
2333 format =
2334 get_cogl_format_from_cairo_format (image_clone->format);
2335 assert (format);
2336
2337 image = image_clone;
2338 }
2339
2340 if (image->stride < 0) {
2341 /* If the stride is negative, this modifies the data pointer so
2342 * that all of the pixels are read into the texture, but
2343 * upside-down. We then invert the matrix so the texture is
2344 * read from the bottom up instead of from the top down. */
2345 stride = -image->stride;
2346 data = image->data - stride * (image->height - 1);
2347
2348 out_matrix->yx *= -1.0;
2349 out_matrix->yy *= -1.0;
2350 out_matrix->y0 += image->height;
2351 } else {
2352 stride = image->stride;
2353 data = image->data;
2354 }
2355
2356 bitmap = cogl_bitmap_new_for_data (dev->cogl_context,
2357 image->width,
2358 image->height,
2359 format, /* incoming */
2360 stride,
2361 data);
2362
2363 if (!dev->has_npots)
2364 texture =
2365 cogl_texture_2d_sliced_new_from_bitmap (bitmap,
2366 COGL_TEXTURE_MAX_WASTE);
2367 else
2368 texture = cogl_texture_2d_new_from_bitmap (bitmap);
2369
2370 /* The texture will have taken a reference on the bitmap */
2371 cogl_object_unref (bitmap);
2372
2373 cogl_texture_set_components (texture,
2374 get_components_from_cairo_format (image->format));
2375
2376 if (unlikely (!cogl_texture_allocate (texture, &error))) {
2377 g_warning ("Failed to allocate texture: %s", error->message);
2378 cogl_error_free (error);
2379 goto BAIL;
2380 }
2381
2382 if (need_mirrored_texture) {
2383 int new_width = image->width;
2384 int new_height = image->height;
2385
2386 /* If the device does not support npot textures, scale to the
2387 * next power of two as well */
2388 if (!dev->has_npots) {
2389 new_width = (int)pow (2, ceil (log2 (new_width)));
2390 new_height = (int)pow (2, ceil (log2 (new_height)));
2391 }
2392
2393 texture = _cairo_cogl_scale_texture (dev->cogl_context,
2394 texture,
2395 new_width,
2396 new_height,
2397 TRUE,
2398 FALSE);
2399 if (unlikely (!texture))
2400 goto BAIL;
2401
2402 *is_mirrored_texture = TRUE;
2403 } else if (!dev->has_npots) {
2404 /* We need to scale the texture up if the hardware does not
2405 * support npots */
2406
2407 /* Get dimensions for the next power of two */
2408 int new_width = (int)pow (2, ceil (log2 (image->width)));
2409 int new_height = (int)pow (2, ceil (log2 (image->height)));
2410
2411 texture = _cairo_cogl_scale_texture (dev->cogl_context,
2412 texture,
2413 new_width,
2414 new_height,
2415 FALSE,
2416 FALSE);
2417 if (unlikely (!texture))
2418 goto BAIL;
2419 }
2420
2421 clone =
2422 _cairo_cogl_surface_create_full (dev,
2423 reference_surface->base.content,
2424 NULL,
2425 texture);
2426 if (unlikely (clone->status)) {
2427 g_warning ("Unable to create clone surface for texture");
2428 goto BAIL;
2429 }
2430
2431 if (*is_mirrored_texture)
2432 ((cairo_cogl_surface_t *)clone)->is_mirrored_snapshot = TRUE;
2433
2434 if (_cairo_surface_is_subsurface (surface))
2435 _cairo_surface_subsurface_set_snapshot (surface, clone);
2436 else
2437 _cairo_surface_attach_snapshot (surface, clone, NULL);
2438
2439 /* Attaching the snapshot will take a reference on the clone surface... */
2440 cairo_surface_destroy (clone);
2441 clone = NULL;
2442
2443 /* Convert from un-normalized source coordinates in backend
2444 * coordinates to normalized texture coordinates. */
2445 if (*is_mirrored_texture) {
2446 xscale = 0.5 / image->width;
2447 yscale = 0.5 / image->height;
2448 } else {
2449 xscale = 1.0 / image->width;
2450 yscale = 1.0 / image->height;
2451 }
2452 _cairo_cogl_matrix_all_scale (out_matrix, xscale, yscale);
2453
2454 /* Release intermediate surface representations */
2455 if (image_clone) {
2456 cairo_surface_destroy (&image_clone->base);
2457 image_clone = NULL;
2458 }
2459 if (acquired_image) {
2460 _cairo_surface_release_source_image (surface,
2461 acquired_image,
2462 image_extra);
2463 acquired_image = NULL;
2464 }
2465
2466 return texture;
2467
2468 BAIL:
2469 if (clone)
2470 cairo_surface_destroy (clone);
2471 if (image_clone)
2472 cairo_surface_destroy (&image_clone->base);
2473 if (acquired_image)
2474 _cairo_surface_release_source_image (surface,
2475 acquired_image,
2476 image_extra);
2477 if (texture)
2478 cogl_object_unref (texture);
2479
2480 return NULL;
2481 }
2482
2483 static cairo_status_t
_cairo_cogl_create_tex_clip(cairo_path_fixed_t * tex_clip,cairo_matrix_t inverse,cairo_bool_t is_mirrored_texture)2484 _cairo_cogl_create_tex_clip (cairo_path_fixed_t *tex_clip,
2485 cairo_matrix_t inverse,
2486 cairo_bool_t is_mirrored_texture)
2487 {
2488 cairo_status_t status;
2489
2490 status = cairo_matrix_invert (&inverse);
2491 if (unlikely (status))
2492 return status;
2493
2494 if (is_mirrored_texture)
2495 status =
2496 _cairo_cogl_path_fixed_rectangle (tex_clip, 0, 0,
2497 _cairo_fixed_from_double (0.5),
2498 _cairo_fixed_from_double (0.5));
2499 else
2500 status = _cairo_cogl_path_fixed_rectangle (tex_clip, 0, 0,
2501 CAIRO_FIXED_ONE,
2502 CAIRO_FIXED_ONE);
2503 if (unlikely (status))
2504 return status;
2505
2506 _cairo_path_fixed_transform (tex_clip, &inverse);
2507
2508 return CAIRO_STATUS_SUCCESS;
2509 }
2510
2511 /* NB: a reference for the texture is transferred to the caller which should
2512 * be unrefed */
2513 static CoglTexture *
_cairo_cogl_acquire_pattern_texture(const cairo_pattern_t * pattern,cairo_cogl_surface_t * destination,const cairo_rectangle_int_t * extents,cairo_cogl_texture_attributes_t * attributes,cairo_path_fixed_t * tex_clip)2514 _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
2515 cairo_cogl_surface_t *destination,
2516 const cairo_rectangle_int_t *extents,
2517 cairo_cogl_texture_attributes_t *attributes,
2518 cairo_path_fixed_t *tex_clip)
2519 {
2520 CoglTexture *texture = NULL;
2521 cairo_cogl_device_t *dev = to_device (destination->base.device);
2522 cairo_bool_t is_mirrored_texture;
2523 cairo_bool_t need_mirrored_texture =
2524 (pattern->extend == CAIRO_EXTEND_REFLECT &&
2525 !dev->has_mirrored_repeat);
2526
2527 switch ((int)pattern->type)
2528 {
2529 case CAIRO_PATTERN_TYPE_SURFACE: {
2530 cairo_cogl_surface_t *clone;
2531 cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface;
2532
2533 clone = (cairo_cogl_surface_t *)
2534 _cairo_surface_has_snapshot (surface,
2535 &_cairo_cogl_surface_backend);
2536 if (clone && clone->texture)
2537 if ((!need_mirrored_texture) || clone->is_mirrored_snapshot)
2538 {
2539 texture = cogl_object_ref (clone->texture);
2540 attributes->matrix = pattern->matrix;
2541 is_mirrored_texture = clone->is_mirrored_snapshot;
2542
2543 /* Convert from un-normalized source coordinates in
2544 * backend coordinates to normalized texture
2545 * coordinates. */
2546 _cairo_cogl_matrix_all_scale (&attributes->matrix,
2547 1.0 / clone->width,
2548 1.0 / clone->height);
2549 };
2550
2551 if (!texture) {
2552 cairo_rectangle_int_t surface_extents;
2553 cairo_surface_t *unwrapped =
2554 _cairo_surface_get_source (surface, &surface_extents);
2555
2556 if (_cairo_surface_is_recording (surface)) {
2557 texture =
2558 _cairo_cogl_acquire_recording_surface_texture (destination,
2559 surface,
2560 extents,
2561 &pattern->matrix,
2562 &attributes->matrix,
2563 need_mirrored_texture,
2564 &is_mirrored_texture);
2565 } else if (surface->type == CAIRO_SURFACE_TYPE_COGL &&
2566 ((cairo_cogl_surface_t *)unwrapped)->texture) {
2567 texture =
2568 _cairo_cogl_acquire_cogl_surface_texture (destination,
2569 unwrapped,
2570 &surface_extents,
2571 &pattern->matrix,
2572 &attributes->matrix,
2573 need_mirrored_texture,
2574 &is_mirrored_texture);
2575 }
2576 }
2577
2578 if (!texture)
2579 texture =
2580 _cairo_cogl_acquire_generic_surface_texture (destination,
2581 surface,
2582 &pattern->matrix,
2583 &attributes->matrix,
2584 need_mirrored_texture,
2585 &is_mirrored_texture);
2586
2587 if (unlikely (!texture))
2588 return NULL;
2589
2590 attributes->extend = pattern->extend;
2591 attributes->filter =
2592 get_cogl_filter_for_filter (pattern->filter);
2593 attributes->has_component_alpha = pattern->has_component_alpha;
2594
2595 attributes->s_wrap =
2596 get_cogl_wrap_mode_for_extend (pattern->extend, dev);
2597 attributes->t_wrap = attributes->s_wrap;
2598
2599 /* In order to support CAIRO_EXTEND_NONE, we use the same wrap
2600 * mode as CAIRO_EXTEND_PAD, but pass a clip to the drawing
2601 * function to make sure that we never sample anything beyond
2602 * the texture boundaries. */
2603 if (pattern->extend == CAIRO_EXTEND_NONE && tex_clip)
2604 if (_cairo_cogl_create_tex_clip (tex_clip,
2605 attributes->matrix,
2606 is_mirrored_texture))
2607 {
2608 cogl_object_unref (texture);
2609 return NULL;
2610 }
2611
2612 return texture;
2613 }
2614 case CAIRO_PATTERN_TYPE_RADIAL:
2615 case CAIRO_PATTERN_TYPE_MESH:
2616 case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
2617 cairo_surface_t *surface;
2618 cairo_matrix_t new_pattern_matrix;
2619
2620 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2621 extents->width, extents->height);
2622 if (_cairo_surface_offset_paint (surface,
2623 extents->x, extents->y,
2624 CAIRO_OPERATOR_SOURCE,
2625 pattern, NULL)) {
2626 cairo_surface_destroy (surface);
2627 return NULL;
2628 }
2629
2630 cairo_matrix_init_translate (&new_pattern_matrix,
2631 -extents->x, -extents->y);
2632
2633 texture =
2634 _cairo_cogl_acquire_generic_surface_texture (destination,
2635 surface,
2636 &new_pattern_matrix,
2637 &attributes->matrix,
2638 need_mirrored_texture,
2639 &is_mirrored_texture);
2640 if (unlikely (!texture))
2641 goto BAIL;
2642
2643 attributes->extend = pattern->extend;
2644 attributes->filter = COGL_PIPELINE_FILTER_NEAREST;
2645 attributes->has_component_alpha = pattern->has_component_alpha;
2646
2647 /* any pattern extend modes have already been dealt with... */
2648 attributes->s_wrap = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
2649 attributes->t_wrap = attributes->s_wrap;
2650
2651 /* In order to support CAIRO_EXTEND_NONE, we use the same wrap
2652 * mode as CAIRO_EXTEND_PAD, but pass a clip to the drawing
2653 * function to make sure that we never sample anything beyond
2654 * the texture boundaries. */
2655 if (pattern->extend == CAIRO_EXTEND_NONE && tex_clip)
2656 if (_cairo_cogl_create_tex_clip (tex_clip,
2657 attributes->matrix,
2658 is_mirrored_texture))
2659 {
2660 cogl_object_unref (texture);
2661 cairo_surface_destroy (surface);
2662 return NULL;
2663 }
2664
2665 BAIL:
2666 cairo_surface_destroy (surface);
2667
2668 return texture;
2669 }
2670 case CAIRO_PATTERN_TYPE_LINEAR: {
2671 cairo_linear_pattern_t *linear_pattern = (cairo_linear_pattern_t *)pattern;
2672 cairo_cogl_linear_gradient_t *gradient;
2673 cairo_cogl_linear_texture_entry_t *linear_texture;
2674 cairo_int_status_t status;
2675 double dist, scale;
2676
2677 status = _cairo_cogl_get_linear_gradient (to_device(destination->base.device),
2678 pattern->extend,
2679 linear_pattern->base.n_stops,
2680 linear_pattern->base.stops,
2681 need_mirrored_texture,
2682 &gradient);
2683 if (unlikely (status))
2684 return NULL;
2685
2686 linear_texture = _cairo_cogl_linear_gradient_texture_for_extend (gradient, pattern->extend);
2687
2688 attributes->extend = pattern->extend;
2689 attributes->filter =
2690 get_cogl_filter_for_filter (pattern->filter);
2691 attributes->has_component_alpha = pattern->has_component_alpha;
2692 attributes->s_wrap =
2693 get_cogl_wrap_mode_for_extend (pattern->extend, dev);
2694 attributes->t_wrap = attributes->s_wrap;
2695
2696 attributes->matrix = pattern->matrix;
2697
2698 double a = linear_pattern->pd2.x - linear_pattern->pd1.x;
2699 double b = linear_pattern->pd2.y - linear_pattern->pd1.y;
2700 double angle = - atan2f (b, a);
2701
2702 cairo_matrix_rotate (&attributes->matrix, angle);
2703
2704 cairo_matrix_translate (&attributes->matrix,
2705 -linear_pattern->pd1.x,
2706 -linear_pattern->pd1.y);
2707
2708 /* Convert from un-normalized source coordinates in backend
2709 * coordinates to normalized texture coordinates. */
2710 dist = sqrtf (a*a + b*b);
2711 if (need_mirrored_texture)
2712 scale = 0.5 / dist;
2713 else
2714 scale = 1.0 / dist;
2715 _cairo_cogl_matrix_all_scale (&attributes->matrix,
2716 scale, scale);
2717
2718 return cogl_object_ref (linear_texture->texture);
2719 }
2720 default:
2721 g_warning ("Unsupported source type");
2722 return NULL;
2723 }
2724 }
2725
2726 static cairo_bool_t
set_blend(CoglPipeline * pipeline,const char * blend_string)2727 set_blend (CoglPipeline *pipeline, const char *blend_string)
2728 {
2729 CoglError *error = NULL;
2730 if (unlikely (!cogl_pipeline_set_blend (pipeline,
2731 blend_string,
2732 &error)))
2733 {
2734 g_warning ("Unsupported blend string with current gpu/driver: %s", blend_string);
2735 cogl_error_free (error);
2736 return FALSE;
2737 }
2738 return TRUE;
2739 }
2740
2741 static cairo_bool_t
_cairo_cogl_setup_op_state(CoglPipeline * pipeline,cairo_operator_t op)2742 _cairo_cogl_setup_op_state (CoglPipeline *pipeline,
2743 cairo_operator_t op)
2744 {
2745 cairo_bool_t status = FALSE;
2746
2747 switch ((int)op)
2748 {
2749 case CAIRO_OPERATOR_OVER:
2750 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))");
2751 break;
2752 case CAIRO_OPERATOR_IN:
2753 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], 0)");
2754 break;
2755 case CAIRO_OPERATOR_OUT:
2756 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), 0)");
2757 break;
2758 case CAIRO_OPERATOR_ATOP:
2759 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], DST_COLOR * (1 - SRC_COLOR[A]))");
2760 break;
2761 case CAIRO_OPERATOR_DEST:
2762 status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR)");
2763 break;
2764 case CAIRO_OPERATOR_DEST_OVER:
2765 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)");
2766 break;
2767 case CAIRO_OPERATOR_DEST_IN:
2768 status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])");
2769 break;
2770 case CAIRO_OPERATOR_DEST_OUT:
2771 status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * (1 - SRC_COLOR[A]))");
2772 break;
2773 case CAIRO_OPERATOR_DEST_ATOP:
2774 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR * SRC_COLOR[A])");
2775 break;
2776 case CAIRO_OPERATOR_XOR:
2777 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR * (1 - SRC_COLOR[A]))");
2778 break;
2779 /* In order to handle SOURCE with a mask, we use two passes. The
2780 * first consists of a CAIRO_OPERATOR_DEST_OUT with the source alpha
2781 * replaced by the mask alpha in order to multiply all the
2782 * destination values by one minus the mask alpha. The second pass
2783 * (this one) then adds the source values, which have already been
2784 * premultiplied by the mask alpha. */
2785 case CAIRO_OPERATOR_SOURCE:
2786 case CAIRO_OPERATOR_ADD:
2787 status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR)");
2788 break;
2789 case CAIRO_OPERATOR_CLEAR:
2790 /* Runtime check */
2791 /* CAIRO_OPERATOR_CLEAR is not supposed to use its own pipeline
2792 * type. Use CAIRO_OPERATOR_DEST_OUT with the mask alpha as
2793 * source alpha instead. */
2794 assert (0);
2795 default:
2796 g_warning ("Unsupported blend operator");
2797 assert (0);
2798 }
2799
2800 return status;
2801 }
2802
2803 static void
create_template_for_op_type(cairo_cogl_device_t * dev,cairo_operator_t op,cairo_cogl_template_type type)2804 create_template_for_op_type (cairo_cogl_device_t *dev,
2805 cairo_operator_t op,
2806 cairo_cogl_template_type type)
2807 {
2808 CoglPipeline *pipeline;
2809 CoglColor color;
2810
2811 if (dev->template_pipelines[op][type])
2812 return;
2813
2814 cogl_color_init_from_4f (&color, 1.0f, 1.0f, 1.0f, 1.0f);
2815
2816 if (!dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]) {
2817 CoglPipeline *base = cogl_pipeline_new (dev->cogl_context);
2818
2819 if (!_cairo_cogl_setup_op_state (base, op)) {
2820 cogl_object_unref (base);
2821 return;
2822 }
2823
2824 dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base;
2825 }
2826
2827 switch ((int)type)
2828 {
2829 case CAIRO_COGL_TEMPLATE_TYPE_SOLID:
2830 return;
2831 case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID:
2832 pipeline =
2833 cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
2834 cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
2835 cogl_pipeline_set_layer_combine (pipeline, 0,
2836 "RGBA = MODULATE (PRIMARY, CONSTANT[A])",
2837 NULL);
2838 break;
2839 case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID:
2840 pipeline =
2841 cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
2842 cogl_pipeline_set_layer_null_texture (pipeline, 0,
2843 COGL_TEXTURE_TYPE_2D);
2844 cogl_pipeline_set_layer_combine (pipeline, 0,
2845 "RGBA = MODULATE (PRIMARY, TEXTURE[A])",
2846 NULL);
2847 break;
2848 case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE:
2849 pipeline =
2850 cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
2851 cogl_pipeline_set_layer_null_texture (pipeline, 0,
2852 COGL_TEXTURE_TYPE_2D);
2853 break;
2854 case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE:
2855 pipeline =
2856 cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
2857 cogl_pipeline_set_layer_null_texture (pipeline, 0,
2858 COGL_TEXTURE_TYPE_2D);
2859 cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
2860 cogl_pipeline_set_layer_combine (pipeline, 1,
2861 "RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
2862 NULL);
2863 break;
2864 case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE:
2865 pipeline =
2866 cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
2867 cogl_pipeline_set_layer_null_texture (pipeline, 0,
2868 COGL_TEXTURE_TYPE_2D);
2869 cogl_pipeline_set_layer_null_texture (pipeline, 1,
2870 COGL_TEXTURE_TYPE_2D);
2871 cogl_pipeline_set_layer_combine (pipeline, 1,
2872 "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
2873 NULL);
2874 break;
2875 case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_IGNORE_ALPHA:
2876 pipeline =
2877 cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
2878 cogl_pipeline_set_layer_null_texture (pipeline, 0,
2879 COGL_TEXTURE_TYPE_2D);
2880 /* We do not set the combine color when we use this template
2881 * pipeline, so the source texture alpha will be replaces by
2882 * ones */
2883 cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
2884 cogl_pipeline_set_layer_combine (pipeline, 0,
2885 "RGB = REPLACE (TEXTURE)"
2886 "A = REPLACE (CONSTANT)",
2887 NULL);
2888 break;
2889 case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE_IGNORE_ALPHA:
2890 pipeline =
2891 cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
2892 cogl_pipeline_set_layer_null_texture (pipeline, 0,
2893 COGL_TEXTURE_TYPE_2D);
2894 /* We do not set the combine color when we use this template
2895 * pipeline, so the source texture alpha will be replaces by
2896 * ones */
2897 cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
2898 cogl_pipeline_set_layer_combine (pipeline, 0,
2899 "RGB = REPLACE (TEXTURE)"
2900 "A = REPLACE (CONSTANT)",
2901 NULL);
2902 cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
2903 cogl_pipeline_set_layer_combine (pipeline, 1,
2904 "RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
2905 NULL);
2906 break;
2907 case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE_IGNORE_ALPHA:
2908 pipeline =
2909 cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
2910 cogl_pipeline_set_layer_null_texture (pipeline, 0,
2911 COGL_TEXTURE_TYPE_2D);
2912 /* We do not set the combine color when we use this template
2913 * pipeline, so the source texture alpha will be replaces by
2914 * ones */
2915 cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
2916 cogl_pipeline_set_layer_combine (pipeline, 0,
2917 "RGB = REPLACE (TEXTURE)"
2918 "A = REPLACE (CONSTANT)",
2919 NULL);
2920 cogl_pipeline_set_layer_null_texture (pipeline, 1,
2921 COGL_TEXTURE_TYPE_2D);
2922 cogl_pipeline_set_layer_combine (pipeline, 1,
2923 "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
2924 NULL);
2925 break;
2926 default:
2927 g_warning ("Invalid cogl pipeline template type");
2928 return;
2929 }
2930
2931 dev->template_pipelines[op][type] = pipeline;
2932 }
2933
2934 static void
set_layer_texture_with_attributes(CoglPipeline * pipeline,int layer_index,CoglTexture * texture,cairo_cogl_texture_attributes_t * attributes,cairo_matrix_t * path_transform)2935 set_layer_texture_with_attributes (CoglPipeline *pipeline,
2936 int layer_index,
2937 CoglTexture *texture,
2938 cairo_cogl_texture_attributes_t *attributes,
2939 cairo_matrix_t *path_transform)
2940 {
2941 cairo_matrix_t m;
2942
2943 cogl_pipeline_set_layer_texture (pipeline, layer_index, texture);
2944
2945 cogl_pipeline_set_layer_filters (pipeline,
2946 layer_index,
2947 attributes->filter,
2948 attributes->filter);
2949
2950 /* We multiply in the path transform here so that we read texture
2951 * values from coordinates that are consistent with the coordinates
2952 * of the path after it is transformed by the modelview matrix */
2953 if (path_transform)
2954 cairo_matrix_multiply (&m, path_transform, &attributes->matrix);
2955 else
2956 m = attributes->matrix;
2957
2958 if (!_cairo_matrix_is_identity (&m)) {
2959 float texture_matrixfv[16] = {
2960 m.xx, m.yx, 0, 0,
2961 m.xy, m.yy, 0, 0,
2962 0, 0, 1, 0,
2963 m.x0, m.y0, 0, 1
2964 };
2965 CoglMatrix texture_matrix;
2966 cogl_matrix_init_from_array (&texture_matrix, texture_matrixfv);
2967 cogl_pipeline_set_layer_matrix (pipeline, layer_index, &texture_matrix);
2968 }
2969
2970 if (attributes->s_wrap != attributes->t_wrap) {
2971 cogl_pipeline_set_layer_wrap_mode_s (pipeline, layer_index, attributes->s_wrap);
2972 cogl_pipeline_set_layer_wrap_mode_t (pipeline, layer_index, attributes->t_wrap);
2973 } else {
2974 cogl_pipeline_set_layer_wrap_mode (pipeline, layer_index, attributes->s_wrap);
2975 }
2976 }
2977
2978 /* This takes an argument of a pointer to an array of two pointers to
2979 * #cairo_cogl_pipeline_t. On failure, both pointers will be set to
2980 * NULL */
2981 static void
get_source_mask_operator_destination_pipelines(cairo_cogl_pipeline_t ** pipelines,const cairo_pattern_t * mask,const cairo_pattern_t * source,cairo_operator_t op,cairo_cogl_surface_t * destination,cairo_composite_rectangles_t * extents,cairo_matrix_t * path_transform)2982 get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t **pipelines,
2983 const cairo_pattern_t *mask,
2984 const cairo_pattern_t *source,
2985 cairo_operator_t op,
2986 cairo_cogl_surface_t *destination,
2987 cairo_composite_rectangles_t *extents,
2988 cairo_matrix_t *path_transform)
2989 {
2990 cairo_cogl_template_type template_type;
2991 cairo_cogl_device_t *dev = to_device(destination->base.device);
2992
2993 pipelines[0] = NULL;
2994 pipelines[1] = NULL;
2995
2996 switch ((int)source->type)
2997 {
2998 case CAIRO_PATTERN_TYPE_SOLID:
2999 if (mask) {
3000 /* If the mask surface has no alpha content, we use a mask
3001 * of solid ones */
3002 if ((mask->type == CAIRO_PATTERN_TYPE_SOLID) ||
3003 (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
3004 ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR))
3005 template_type =
3006 CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID;
3007 else
3008 template_type =
3009 CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID;
3010 } else {
3011 template_type = CAIRO_COGL_TEMPLATE_TYPE_SOLID;
3012 }
3013 break;
3014 case CAIRO_PATTERN_TYPE_SURFACE:
3015 /* If the source does not have alpha content, we have to use
3016 * a specialized set of texture combining functions in order to
3017 * ensure that if we have a CAIRO_FORMAT_RGB24 source, we are
3018 * ignoring the alpha and replacing it with ones. Otherwise, we
3019 * use the template types for any other type of non-solid
3020 * source. */
3021 if (((cairo_surface_pattern_t *)source)->surface->content ==
3022 CAIRO_CONTENT_COLOR)
3023 {
3024 if (mask) {
3025 /* If the mask surface has no alpha content, we use a
3026 * mask of solid ones */
3027 if ((mask->type == CAIRO_PATTERN_TYPE_SOLID) ||
3028 (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
3029 ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR))
3030 template_type =
3031 CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE_IGNORE_ALPHA;
3032 else
3033 template_type =
3034 CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE_IGNORE_ALPHA;
3035 } else {
3036 template_type =
3037 CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_IGNORE_ALPHA;
3038 }
3039 break;
3040 }
3041 // else fall through
3042 case CAIRO_PATTERN_TYPE_LINEAR:
3043 case CAIRO_PATTERN_TYPE_RADIAL:
3044 case CAIRO_PATTERN_TYPE_MESH:
3045 case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
3046 if (mask) {
3047 /* If the mask surface has no alpha content, we use a mask
3048 * of solid ones */
3049 if ((mask->type == CAIRO_PATTERN_TYPE_SOLID) ||
3050 (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
3051 ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR))
3052 template_type =
3053 CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE;
3054 else
3055 template_type =
3056 CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE;
3057 } else {
3058 template_type = CAIRO_COGL_TEMPLATE_TYPE_TEXTURE;
3059 }
3060 break;
3061 default:
3062 g_warning ("Unsupported source type");
3063 return;
3064 }
3065
3066 /* pipelines[0] is for pre-rendering the mask alpha in the case
3067 * that it cannot be represented through the source color alpha
3068 * value. For more details, go to the description in
3069 * _cairo_cogl_setup_op_state */
3070 if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) {
3071 cairo_cogl_template_type prerender_type;
3072
3073 pipelines[0] = g_new (cairo_cogl_pipeline_t, 1);
3074
3075 if (mask && mask->type != CAIRO_PATTERN_TYPE_SOLID)
3076 prerender_type = CAIRO_COGL_TEMPLATE_TYPE_SOLID;
3077 else
3078 prerender_type = CAIRO_COGL_TEMPLATE_TYPE_TEXTURE;
3079
3080 /* Lazily create pipeline templates */
3081 if (unlikely (dev->template_pipelines[CAIRO_OPERATOR_DEST_OUT][prerender_type] == NULL))
3082 create_template_for_op_type (dev,
3083 CAIRO_OPERATOR_DEST_OUT,
3084 prerender_type);
3085
3086 pipelines[0]->pipeline =
3087 cogl_pipeline_copy (dev->template_pipelines[CAIRO_OPERATOR_DEST_OUT][prerender_type]);
3088
3089 pipelines[0]->mask_bounded =
3090 _cairo_operator_bounded_by_mask (op);
3091 pipelines[0]->src_bounded =
3092 _cairo_operator_bounded_by_source (op);
3093 pipelines[0]->op = CAIRO_OPERATOR_DEST_OUT;
3094 pipelines[0]->n_layers = 0;
3095 pipelines[0]->has_src_tex_clip = FALSE;
3096 pipelines[0]->has_mask_tex_clip = FALSE;
3097 pipelines[0]->unbounded_extents = extents->unbounded;
3098 }
3099
3100 /* pipelines[1] is for normal rendering, modulating the mask with
3101 * the source. Most operators will only need this pipeline. */
3102 if (op != CAIRO_OPERATOR_CLEAR) {
3103 pipelines[1] = g_new (cairo_cogl_pipeline_t, 1);
3104
3105 /* Lazily create pipeline templates */
3106 if (unlikely (dev->template_pipelines[op][template_type] == NULL))
3107 create_template_for_op_type (dev, op, template_type);
3108
3109 pipelines[1]->pipeline =
3110 cogl_pipeline_copy (dev->template_pipelines[op][template_type]);
3111
3112 pipelines[1]->mask_bounded =
3113 _cairo_operator_bounded_by_mask (op);
3114 pipelines[1]->src_bounded =
3115 _cairo_operator_bounded_by_source (op);
3116 pipelines[1]->op = op;
3117 pipelines[1]->n_layers = 0;
3118 pipelines[1]->has_src_tex_clip = FALSE;
3119 pipelines[1]->has_mask_tex_clip = FALSE;
3120 pipelines[1]->unbounded_extents = extents->unbounded;
3121 }
3122
3123 if (pipelines[1]) {
3124 if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
3125 cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source;
3126 cogl_pipeline_set_color4f (pipelines[1]->pipeline,
3127 solid_pattern->color.red * solid_pattern->color.alpha,
3128 solid_pattern->color.green * solid_pattern->color.alpha,
3129 solid_pattern->color.blue * solid_pattern->color.alpha,
3130 solid_pattern->color.alpha);
3131 } else {
3132 cairo_cogl_texture_attributes_t attributes;
3133
3134 _cairo_path_fixed_init (&pipelines[1]->src_tex_clip);
3135
3136 CoglTexture *texture =
3137 _cairo_cogl_acquire_pattern_texture (source, destination,
3138 &extents->bounded,
3139 &attributes,
3140 &pipelines[1]->src_tex_clip);
3141 if (unlikely (!texture))
3142 goto BAIL;
3143 set_layer_texture_with_attributes (pipelines[1]->pipeline,
3144 pipelines[1]->n_layers++,
3145 texture,
3146 &attributes,
3147 path_transform);
3148 cogl_object_unref (texture);
3149
3150 if (pipelines[1]->src_tex_clip.buf.base.num_ops > 0)
3151 pipelines[1]->has_src_tex_clip = TRUE;
3152 else
3153 _cairo_path_fixed_fini (&pipelines[1]->src_tex_clip);
3154 }
3155 }
3156
3157 if (mask) {
3158 if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
3159 cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)mask;
3160 CoglColor color;
3161 cogl_color_init_from_4f (&color,
3162 solid_pattern->color.red * solid_pattern->color.alpha,
3163 solid_pattern->color.green * solid_pattern->color.alpha,
3164 solid_pattern->color.blue * solid_pattern->color.alpha,
3165 solid_pattern->color.alpha);
3166 if (pipelines[1])
3167 cogl_pipeline_set_layer_combine_constant (pipelines[1]->pipeline,
3168 pipelines[1]->n_layers++,
3169 &color);
3170 if (pipelines[0])
3171 cogl_pipeline_set_color (pipelines[0]->pipeline,
3172 &color);
3173 /* If the only component present in our mask is a color
3174 * component, skip setting the layer texture, as we already
3175 * set a solid of uniform ones on it during the template
3176 * creation process */
3177 } else if (!(mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
3178 ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR)) {
3179 cairo_cogl_texture_attributes_t attributes;
3180 cairo_path_fixed_t mask_tex_clip;
3181
3182 _cairo_path_fixed_init (&mask_tex_clip);
3183
3184 CoglTexture *texture =
3185 _cairo_cogl_acquire_pattern_texture (mask, destination,
3186 &extents->bounded,
3187 &attributes,
3188 &mask_tex_clip);
3189 if (unlikely (!texture))
3190 goto BAIL;
3191 if (pipelines[1]) {
3192 if (mask_tex_clip.buf.base.num_ops > 0) {
3193 pipelines[1]->has_mask_tex_clip = TRUE;
3194 if (unlikely (_cairo_path_fixed_init_copy (&pipelines[1]->mask_tex_clip,
3195 &mask_tex_clip)))
3196 goto BAIL;
3197 }
3198 set_layer_texture_with_attributes (pipelines[1]->pipeline,
3199 pipelines[1]->n_layers++,
3200 texture,
3201 &attributes,
3202 path_transform);
3203 }
3204 if (pipelines[0]) {
3205 if (mask_tex_clip.buf.base.num_ops > 0) {
3206 pipelines[0]->has_mask_tex_clip = TRUE;
3207 if (unlikely (_cairo_path_fixed_init_copy (&pipelines[0]->mask_tex_clip,
3208 &mask_tex_clip)))
3209 goto BAIL;
3210 }
3211 set_layer_texture_with_attributes (pipelines[0]->pipeline,
3212 pipelines[0]->n_layers++,
3213 texture,
3214 &attributes,
3215 path_transform);
3216 }
3217
3218 _cairo_path_fixed_fini (&mask_tex_clip);
3219 cogl_object_unref (texture);
3220 }
3221 }
3222
3223 return;
3224
3225 BAIL:
3226 if (pipelines[0]) {
3227 cogl_object_unref (pipelines[0]->pipeline);
3228 if (pipelines[0]->has_src_tex_clip)
3229 _cairo_path_fixed_fini (&pipelines[0]->src_tex_clip);
3230 if (pipelines[0]->has_mask_tex_clip)
3231 _cairo_path_fixed_fini (&pipelines[0]->mask_tex_clip);
3232 g_free (pipelines[0]);
3233 pipelines[0] = NULL;
3234 }
3235 if (pipelines[1]) {
3236 cogl_object_unref (pipelines[1]->pipeline);
3237 if (pipelines[1]->has_src_tex_clip)
3238 _cairo_path_fixed_fini (&pipelines[1]->src_tex_clip);
3239 if (pipelines[1]->has_mask_tex_clip)
3240 _cairo_path_fixed_fini (&pipelines[1]->mask_tex_clip);
3241 g_free (pipelines[1]);
3242 pipelines[1] = NULL;
3243 }
3244 }
3245
3246 #if 0
3247 CoglPrimitive *
3248 _cairo_cogl_rectangle_new_p2t2t2 (CoglContext *cogl_context,
3249 float x,
3250 float y,
3251 float width,
3252 float height)
3253 {
3254 CoglVertexP2 vertices[] = {
3255 {x, y}, {x, y + height}, {x + width, y + height},
3256 {x, y}, {x + width, y + height}, {x + width, y}
3257 };
3258 CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (cogl_context,
3259 sizeof (vertices));
3260 CoglAttribute *pos = cogl_attribute_new (buffer,
3261 "cogl_position_in",
3262 sizeof (CoglVertexP2),
3263 0,
3264 2,
3265 COGL_ATTRIBUTE_TYPE_FLOAT);
3266 CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
3267 "cogl_tex_coord0_in",
3268 sizeof (CoglVertexP2),
3269 0,
3270 2,
3271 COGL_ATTRIBUTE_TYPE_FLOAT);
3272 CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
3273 "cogl_tex_coord0_in",
3274 sizeof (CoglVertexP2),
3275 0,
3276 2,
3277 COGL_ATTRIBUTE_TYPE_FLOAT);
3278 CoglPrimitive *prim;
3279
3280 cogl_buffer_set_data (buffer, 0, vertices, sizeof (vertices));
3281
3282 /* The attributes will now keep the buffer alive... */
3283 cogl_object_unref (buffer);
3284
3285 prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
3286 6, pos, tex_coords, NULL);
3287
3288 /* The primitive will now keep the attribute alive... */
3289 cogl_object_unref (pos);
3290
3291 return prim;
3292 }
3293 #endif
3294
3295 static void
_cairo_cogl_log_clip(cairo_cogl_surface_t * surface,const cairo_clip_t * clip)3296 _cairo_cogl_log_clip (cairo_cogl_surface_t *surface,
3297 const cairo_clip_t *clip)
3298 {
3299 if (!_cairo_clip_equal (clip, surface->last_clip)) {
3300 _cairo_cogl_journal_log_clip (surface, clip);
3301 _cairo_clip_destroy (surface->last_clip);
3302 surface->last_clip = _cairo_clip_copy (clip);
3303 }
3304 }
3305
3306 static void
_cairo_cogl_maybe_log_clip(cairo_cogl_surface_t * surface,cairo_composite_rectangles_t * composite)3307 _cairo_cogl_maybe_log_clip (cairo_cogl_surface_t *surface,
3308 cairo_composite_rectangles_t *composite)
3309 {
3310 cairo_clip_t *clip = composite->clip;
3311
3312 if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
3313 clip = NULL;
3314
3315 if (clip == NULL) {
3316 if (_cairo_composite_rectangles_can_reduce_clip (composite,
3317 surface->last_clip))
3318 return;
3319 }
3320
3321 _cairo_cogl_log_clip (surface, clip);
3322 }
3323
3324 static cairo_bool_t
is_operator_supported(cairo_operator_t op)3325 is_operator_supported (cairo_operator_t op)
3326 {
3327 switch ((int)op) {
3328 case CAIRO_OPERATOR_CLEAR:
3329 case CAIRO_OPERATOR_SOURCE:
3330 case CAIRO_OPERATOR_OVER:
3331 case CAIRO_OPERATOR_IN:
3332 case CAIRO_OPERATOR_OUT:
3333 case CAIRO_OPERATOR_ATOP:
3334 case CAIRO_OPERATOR_DEST:
3335 case CAIRO_OPERATOR_DEST_OVER:
3336 case CAIRO_OPERATOR_DEST_IN:
3337 case CAIRO_OPERATOR_DEST_OUT:
3338 case CAIRO_OPERATOR_DEST_ATOP:
3339 case CAIRO_OPERATOR_XOR:
3340 case CAIRO_OPERATOR_ADD:
3341 return TRUE;
3342
3343 default:
3344 g_warning("cairo-cogl: Blend operator not supported");
3345 return FALSE;
3346 }
3347 }
3348
3349 static cairo_int_status_t
_cairo_cogl_surface_paint(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_clip_t * clip)3350 _cairo_cogl_surface_paint (void *abstract_surface,
3351 cairo_operator_t op,
3352 const cairo_pattern_t *source,
3353 const cairo_clip_t *clip)
3354 {
3355 cairo_cogl_surface_t *surface;
3356 cairo_int_status_t status;
3357 cairo_matrix_t identity;
3358 cairo_cogl_pipeline_t *pipelines[2];
3359 cairo_composite_rectangles_t extents;
3360
3361 if (clip == NULL) {
3362 status = _cairo_cogl_surface_ensure_framebuffer (abstract_surface);
3363 if (unlikely (status))
3364 return status;
3365
3366 if (op == CAIRO_OPERATOR_CLEAR)
3367 return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
3368 else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
3369 (op == CAIRO_OPERATOR_SOURCE ||
3370 (op == CAIRO_OPERATOR_OVER && (((cairo_surface_t *)abstract_surface)->is_clear || _cairo_pattern_is_opaque_solid (source))))) {
3371 return _cairo_cogl_surface_clear (abstract_surface,
3372 &((cairo_solid_pattern_t *) source)->color);
3373 }
3374 }
3375
3376 /* fall back to handling the paint in terms of a rectangle... */
3377
3378 surface = (cairo_cogl_surface_t *)abstract_surface;
3379
3380 if (!is_operator_supported (op))
3381 return CAIRO_INT_STATUS_UNSUPPORTED;
3382
3383 status =
3384 _cairo_composite_rectangles_init_for_paint (&extents,
3385 &surface->base,
3386 op,
3387 source,
3388 clip);
3389 if (unlikely (status))
3390 return status;
3391
3392 get_source_mask_operator_destination_pipelines (pipelines,
3393 NULL,
3394 source,
3395 op,
3396 surface,
3397 &extents,
3398 NULL);
3399 if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) {
3400 status = CAIRO_INT_STATUS_UNSUPPORTED;
3401 goto BAIL;
3402 }
3403
3404 _cairo_cogl_maybe_log_clip (surface, &extents);
3405
3406 cairo_matrix_init_identity (&identity);
3407 if (pipelines[0])
3408 _cairo_cogl_journal_log_rectangle (surface,
3409 pipelines[0],
3410 extents.bounded.x,
3411 extents.bounded.y,
3412 extents.bounded.width,
3413 extents.bounded.height,
3414 &identity);
3415 if (pipelines[1])
3416 _cairo_cogl_journal_log_rectangle (surface,
3417 pipelines[1],
3418 extents.bounded.x,
3419 extents.bounded.y,
3420 extents.bounded.width,
3421 extents.bounded.height,
3422 &identity);
3423
3424 BAIL:
3425 _cairo_composite_rectangles_fini (&extents);
3426
3427 return status;
3428 }
3429
3430 static cairo_int_status_t
_cairo_cogl_surface_mask(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_pattern_t * mask,const cairo_clip_t * clip)3431 _cairo_cogl_surface_mask (void *abstract_surface,
3432 cairo_operator_t op,
3433 const cairo_pattern_t *source,
3434 const cairo_pattern_t *mask,
3435 const cairo_clip_t *clip)
3436 {
3437 cairo_cogl_surface_t *surface = abstract_surface;
3438 cairo_composite_rectangles_t extents;
3439 cairo_int_status_t status;
3440 cairo_cogl_pipeline_t *pipelines[2];
3441 cairo_matrix_t identity;
3442
3443 /* XXX: Use this to smoke test the acquire_source/dest_image fallback
3444 * paths... */
3445 //return CAIRO_INT_STATUS_UNSUPPORTED;
3446
3447 if (!is_operator_supported (op))
3448 return CAIRO_INT_STATUS_UNSUPPORTED;
3449
3450 status = _cairo_composite_rectangles_init_for_mask (&extents,
3451 &surface->base,
3452 op, source, mask, clip);
3453 if (unlikely (status))
3454 return status;
3455
3456 get_source_mask_operator_destination_pipelines (pipelines,
3457 mask,
3458 source,
3459 op,
3460 surface,
3461 &extents,
3462 NULL);
3463 if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) {
3464 status = CAIRO_INT_STATUS_UNSUPPORTED;
3465 goto BAIL;
3466 }
3467
3468 _cairo_cogl_maybe_log_clip (surface, &extents);
3469
3470 cairo_matrix_init_identity (&identity);
3471 if (pipelines[0])
3472 _cairo_cogl_journal_log_rectangle (surface,
3473 pipelines[0],
3474 extents.bounded.x,
3475 extents.bounded.y,
3476 extents.bounded.width,
3477 extents.bounded.height,
3478 &identity);
3479 if (pipelines[1])
3480 _cairo_cogl_journal_log_rectangle (surface,
3481 pipelines[1],
3482 extents.bounded.x,
3483 extents.bounded.y,
3484 extents.bounded.width,
3485 extents.bounded.height,
3486 &identity);
3487
3488 BAIL:
3489 _cairo_composite_rectangles_fini (&extents);
3490
3491 return status;
3492 }
3493
3494 static cairo_int_status_t
_cairo_cogl_surface_stroke(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,const cairo_stroke_style_t * style,const cairo_matrix_t * ctm,const cairo_matrix_t * ctm_inverse,double tolerance,cairo_antialias_t antialias,const cairo_clip_t * clip)3495 _cairo_cogl_surface_stroke (void *abstract_surface,
3496 cairo_operator_t op,
3497 const cairo_pattern_t *source,
3498 const cairo_path_fixed_t *path,
3499 const cairo_stroke_style_t *style,
3500 const cairo_matrix_t *ctm,
3501 const cairo_matrix_t *ctm_inverse,
3502 double tolerance,
3503 cairo_antialias_t antialias,
3504 const cairo_clip_t *clip)
3505 {
3506 cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
3507 cairo_composite_rectangles_t extents;
3508 cairo_cogl_pipeline_t *pipelines[2];
3509 cairo_int_status_t status;
3510 cairo_matrix_t transform;
3511 CoglPrimitive *prim = NULL;
3512
3513 if (! is_operator_supported (op))
3514 return CAIRO_INT_STATUS_UNSUPPORTED;
3515
3516 status = _cairo_composite_rectangles_init_for_stroke (&extents,
3517 &surface->base,
3518 op, source, path,
3519 style,
3520 ctm,
3521 clip);
3522 if (unlikely (status))
3523 return status;
3524
3525 status = _cairo_cogl_stroke_to_primitive (surface, path, style,
3526 tolerance, TRUE, &prim,
3527 &transform);
3528 if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
3529 && _cairo_operator_bounded_by_mask (op) == FALSE) {
3530 /* Just render the unbounded rectangle */
3531 prim = NULL;
3532 } else if (unlikely (status)) {
3533 goto BAIL;
3534 }
3535
3536 get_source_mask_operator_destination_pipelines (pipelines,
3537 NULL,
3538 source,
3539 op,
3540 surface,
3541 &extents,
3542 &transform);
3543 if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) {
3544 status = CAIRO_INT_STATUS_UNSUPPORTED;
3545 goto BAIL;
3546 }
3547
3548 _cairo_cogl_maybe_log_clip (surface, &extents);
3549
3550 if (pipelines[0])
3551 _cairo_cogl_journal_log_primitive (surface,
3552 pipelines[0],
3553 prim,
3554 &transform);
3555 if (pipelines[1])
3556 _cairo_cogl_journal_log_primitive (surface,
3557 pipelines[1],
3558 prim,
3559 &transform);
3560
3561 BAIL:
3562 /* The journal will take a reference on the primitive... */
3563 if (prim)
3564 cogl_object_unref (prim);
3565
3566 _cairo_composite_rectangles_fini (&extents);
3567
3568 return status;
3569 }
3570
3571 static cairo_int_status_t
_cairo_cogl_surface_fill(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_antialias_t antialias,const cairo_clip_t * clip)3572 _cairo_cogl_surface_fill (void *abstract_surface,
3573 cairo_operator_t op,
3574 const cairo_pattern_t *source,
3575 const cairo_path_fixed_t *path,
3576 cairo_fill_rule_t fill_rule,
3577 double tolerance,
3578 cairo_antialias_t antialias,
3579 const cairo_clip_t *clip)
3580 {
3581 cairo_cogl_surface_t *surface = abstract_surface;
3582 cairo_composite_rectangles_t extents;
3583 cairo_int_status_t status;
3584 cairo_matrix_t transform;
3585 CoglPrimitive *prim = NULL;
3586 cairo_cogl_pipeline_t *pipelines[2];
3587
3588 if (! is_operator_supported (op))
3589 return CAIRO_INT_STATUS_UNSUPPORTED;
3590
3591 status = _cairo_composite_rectangles_init_for_fill (&extents,
3592 &surface->base,
3593 op, source, path,
3594 clip);
3595 if (unlikely (status))
3596 return status;
3597
3598 status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule,
3599 tolerance, TRUE, &prim,
3600 &transform);
3601 if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
3602 && _cairo_operator_bounded_by_mask (op) == FALSE) {
3603 /* Just render the unbounded rectangle */
3604 prim = NULL;
3605 } else if (unlikely (status)) {
3606 goto BAIL;
3607 }
3608
3609 get_source_mask_operator_destination_pipelines (pipelines,
3610 NULL,
3611 source,
3612 op,
3613 surface,
3614 &extents,
3615 &transform);
3616 if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) {
3617 status = CAIRO_INT_STATUS_UNSUPPORTED;
3618 goto BAIL;
3619 }
3620
3621 _cairo_cogl_maybe_log_clip (surface, &extents);
3622
3623 if (pipelines[0])
3624 _cairo_cogl_journal_log_primitive (surface,
3625 pipelines[0],
3626 prim,
3627 &transform);
3628 if (pipelines[1])
3629 _cairo_cogl_journal_log_primitive (surface,
3630 pipelines[1],
3631 prim,
3632 &transform);
3633
3634 BAIL:
3635 /* The journal will take a reference on the prim */
3636 if (prim)
3637 cogl_object_unref (prim);
3638 _cairo_composite_rectangles_fini (&extents);
3639
3640 return status;
3641 }
3642
3643 /* Mostly taken from #cairo_vg_surface.c */
3644 /* TODO: implement actual font support, with either cogl-pango's glyph
3645 * cache or our own */
3646 static cairo_int_status_t
_cairo_cogl_surface_show_glyphs(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_glyph_t * glyphs,int num_glyphs,cairo_scaled_font_t * scaled_font,const cairo_clip_t * clip)3647 _cairo_cogl_surface_show_glyphs (void *abstract_surface,
3648 cairo_operator_t op,
3649 const cairo_pattern_t *source,
3650 cairo_glyph_t *glyphs,
3651 int num_glyphs,
3652 cairo_scaled_font_t *scaled_font,
3653 const cairo_clip_t *clip)
3654 {
3655 cairo_status_t status = CAIRO_STATUS_SUCCESS;
3656 cairo_path_fixed_t path;
3657 int num_chunk_glyphs;
3658 int i;
3659
3660 if (num_glyphs <= 0)
3661 return CAIRO_STATUS_SUCCESS;
3662
3663 #define GLYPH_CHUNK_SIZE 100
3664
3665 /* Chunk glyphs in order to avoid large computation overheads
3666 * during tessellation of long strings */
3667 for (i = 0; i < num_glyphs; i += GLYPH_CHUNK_SIZE) {
3668 num_chunk_glyphs = (num_glyphs - i) < GLYPH_CHUNK_SIZE ?
3669 (num_glyphs - i) : GLYPH_CHUNK_SIZE;
3670
3671 _cairo_path_fixed_init (&path);
3672 status = _cairo_scaled_font_glyph_path (scaled_font,
3673 &glyphs[i],
3674 num_chunk_glyphs,
3675 &path);
3676 if (unlikely (status))
3677 goto BAIL;
3678
3679 status = _cairo_cogl_surface_fill (abstract_surface,
3680 op, source, &path,
3681 CAIRO_FILL_RULE_WINDING,
3682 CAIRO_GSTATE_TOLERANCE_DEFAULT,
3683 CAIRO_ANTIALIAS_DEFAULT,
3684 clip);
3685
3686 _cairo_path_fixed_fini (&path);
3687 }
3688
3689 #undef GLYPH_CHUNK_SIZE
3690
3691 return CAIRO_STATUS_SUCCESS;
3692
3693 BAIL:
3694 _cairo_path_fixed_fini (&path);
3695
3696 return status;
3697 }
3698
3699 const cairo_surface_backend_t _cairo_cogl_surface_backend = {
3700 CAIRO_SURFACE_TYPE_COGL,
3701 _cairo_cogl_surface_finish,
3702 _cairo_default_context_create,
3703
3704 _cairo_cogl_surface_create_similar,
3705 NULL, /* create similar image */
3706 NULL, /* map to image */
3707 NULL, /* unmap image */
3708
3709 _cairo_surface_default_source,
3710 _cairo_cogl_surface_acquire_source_image,
3711 _cairo_cogl_surface_release_source_image,
3712 NULL, /* snapshot */
3713
3714 NULL, /* copy_page */
3715 NULL, /* show_page */
3716
3717 _cairo_cogl_surface_get_extents,
3718 NULL, /* get_font_options */
3719
3720 _cairo_cogl_surface_flush, /* flush */
3721 NULL, /* mark_dirty_rectangle */
3722
3723 _cairo_cogl_surface_paint,
3724 _cairo_cogl_surface_mask,
3725 _cairo_cogl_surface_stroke,
3726 _cairo_cogl_surface_fill,
3727 NULL, /* fill_stroke */
3728 _cairo_cogl_surface_show_glyphs,
3729 };
3730
3731 static cairo_surface_t *
_cairo_cogl_surface_create_full(cairo_cogl_device_t * dev,cairo_content_t content,CoglFramebuffer * framebuffer,CoglTexture * texture)3732 _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
3733 cairo_content_t content,
3734 CoglFramebuffer *framebuffer,
3735 CoglTexture *texture)
3736 {
3737 cairo_cogl_surface_t *surface;
3738 cairo_status_t status;
3739
3740 status = cairo_device_acquire (&dev->base);
3741 if (unlikely (status))
3742 return _cairo_surface_create_in_error (status);
3743
3744 surface = _cairo_malloc (sizeof (cairo_cogl_surface_t));
3745 if (unlikely (surface == NULL))
3746 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
3747
3748 surface->is_mirrored_snapshot = FALSE;
3749
3750 surface->framebuffer = framebuffer;
3751 if (framebuffer) {
3752 surface->width = cogl_framebuffer_get_width (framebuffer);
3753 surface->height = cogl_framebuffer_get_height (framebuffer);
3754 cogl_object_ref (framebuffer);
3755 }
3756
3757 /* FIXME: If texture == NULL and we are given an offscreen framebuffer
3758 * then we want a way to poke inside the framebuffer to get a texture */
3759 surface->texture = texture;
3760 if (texture) {
3761 if (!framebuffer) {
3762 surface->width = cogl_texture_get_width (texture);
3763 surface->height = cogl_texture_get_height (texture);
3764 }
3765 cogl_object_ref (texture);
3766 }
3767
3768 surface->journal = NULL;
3769
3770 surface->last_clip = NULL;
3771
3772 surface->n_clip_updates_per_frame = 0;
3773
3774 surface->path_is_rectangle = FALSE;
3775 surface->user_path = NULL;
3776
3777 _cairo_surface_init (&surface->base,
3778 &_cairo_cogl_surface_backend,
3779 &dev->base,
3780 content,
3781 FALSE); /* is_vector */
3782
3783 return &surface->base;
3784 }
3785
3786 cairo_surface_t *
cairo_cogl_surface_create_for_fb(cairo_device_t * abstract_device,CoglFramebuffer * framebuffer,cairo_content_t content)3787 cairo_cogl_surface_create_for_fb (cairo_device_t *abstract_device,
3788 CoglFramebuffer *framebuffer,
3789 cairo_content_t content)
3790 {
3791 cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
3792
3793 if (abstract_device == NULL)
3794 return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
3795
3796 if (abstract_device->status)
3797 return _cairo_surface_create_in_error (abstract_device->status);
3798
3799 if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
3800 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
3801
3802 return _cairo_cogl_surface_create_full (dev,
3803 content,
3804 framebuffer,
3805 NULL);
3806 }
3807 slim_hidden_def (cairo_cogl_surface_create_for_fb);
3808
3809 cairo_surface_t *
cairo_cogl_onscreen_surface_create(cairo_device_t * abstract_device,cairo_content_t content,int width,int height)3810 cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device,
3811 cairo_content_t content,
3812 int width, int height)
3813 {
3814 CoglFramebuffer *fb;
3815 CoglTextureComponents components;
3816 CoglError *error = NULL;
3817 cairo_surface_t *surface;
3818 cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
3819
3820 if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
3821 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
3822
3823 /* We don't yet have a way to set the components of a framebuffer */
3824 components = get_components_from_cairo_content (content);
3825
3826 fb = cogl_onscreen_new (dev->cogl_context, width, height);
3827
3828 if (unlikely (!cogl_framebuffer_allocate (fb, &error))) {
3829 g_warning ("Could not allocate framebuffer for onscreen "
3830 "surface: %s", error->message);
3831 cogl_error_free (error);
3832 return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
3833 }
3834 cogl_framebuffer_orthographic (fb, 0, 0, width, height, -1, 100);
3835
3836 surface = cairo_cogl_surface_create_for_fb (abstract_device,
3837 fb,
3838 content);
3839
3840 /* The surface will take a reference on the framebuffer */
3841 cogl_object_unref (fb);
3842
3843 return surface;
3844 }
3845 slim_hidden_def (cairo_cogl_onscreen_surface_create);
3846
3847 cairo_surface_t *
cairo_cogl_offscreen_surface_create(cairo_device_t * abstract_device,cairo_content_t content,int width,int height)3848 cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device,
3849 cairo_content_t content,
3850 int width, int height)
3851 {
3852 CoglFramebuffer *fb;
3853 CoglTexture *tex;
3854 CoglError *error = NULL;
3855 cairo_surface_t *surface;
3856 cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
3857 int tex_width = width;
3858 int tex_height = height;
3859
3860 if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
3861 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
3862
3863 /* If we cannot use an NPOT texture, allocate the texture in power
3864 * of two dimensions instead */
3865 if (!dev->has_npots) {
3866 tex_width = (int)pow (2, ceil (log2 (tex_width)));
3867 tex_height = (int)pow (2, ceil (log2 (tex_height)));
3868 }
3869
3870 tex = cogl_texture_2d_new_with_size (dev->cogl_context,
3871 tex_width, tex_height);
3872 cogl_texture_set_components (tex,
3873 get_components_from_cairo_content (content));
3874 fb = cogl_offscreen_new_with_texture (tex);
3875
3876 if (unlikely (!cogl_framebuffer_allocate (fb, &error))) {
3877 g_warning ("Could not allocate framebuffer for offscreen "
3878 "surface: %s", error->message);
3879 cogl_error_free (error);
3880 return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
3881 }
3882 cogl_framebuffer_orthographic (fb, 0, 0,
3883 tex_width, tex_height,
3884 -1, 100);
3885
3886 /* The framebuffer will take a reference on the texture */
3887 cogl_object_unref (tex);
3888
3889 surface = cairo_cogl_surface_create_for_fb (abstract_device,
3890 fb,
3891 content);
3892
3893 /* The surface will take a reference on the framebuffer */
3894 cogl_object_unref (fb);
3895
3896 ((cairo_cogl_surface_t *)surface)->width = width;
3897 ((cairo_cogl_surface_t *)surface)->height = height;
3898
3899 return surface;
3900 }
3901 slim_hidden_def (cairo_cogl_offscreen_surface_create);
3902
3903 CoglFramebuffer *
cairo_cogl_surface_get_framebuffer(cairo_surface_t * abstract_surface)3904 cairo_cogl_surface_get_framebuffer (cairo_surface_t *abstract_surface)
3905 {
3906 cairo_cogl_surface_t *surface;
3907
3908 if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
3909 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
3910 return NULL;
3911 }
3912
3913 surface = (cairo_cogl_surface_t *) abstract_surface;
3914
3915 return surface->framebuffer;
3916 }
3917 slim_hidden_def (cairo_cogl_surface_get_framebuffer);
3918
3919 CoglTexture *
cairo_cogl_surface_get_texture(cairo_surface_t * abstract_surface)3920 cairo_cogl_surface_get_texture (cairo_surface_t *abstract_surface)
3921 {
3922 cairo_cogl_surface_t *surface;
3923
3924 if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
3925 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
3926 return NULL;
3927 }
3928
3929 surface = (cairo_cogl_surface_t *) abstract_surface;
3930
3931 return surface->texture;
3932 }
3933 slim_hidden_def (cairo_cogl_surface_get_texture);
3934
3935 static cairo_status_t
_cairo_cogl_device_flush(void * device)3936 _cairo_cogl_device_flush (void *device)
3937 {
3938 cairo_status_t status;
3939 cairo_cogl_device_t *dev = device;
3940
3941 status = cairo_device_acquire (device);
3942 if (unlikely (status))
3943 return status;
3944
3945 /* XXX: we don't need to flush Cogl here, we just need to flush
3946 * any batching we do of compositing primitives. */
3947
3948 if (dev->buffer_stack && dev->buffer_stack_offset) {
3949 cogl_buffer_unmap (dev->buffer_stack);
3950 cogl_object_unref (dev->buffer_stack);
3951 dev->buffer_stack = NULL;
3952 }
3953
3954 cairo_device_release (device);
3955
3956 return CAIRO_STATUS_SUCCESS;
3957 }
3958
3959 static void
_cairo_cogl_device_finish(void * device)3960 _cairo_cogl_device_finish (void *device)
3961 {
3962 cairo_status_t status;
3963 cairo_cogl_device_t *dev = device;
3964 int i, j;
3965
3966 status = cairo_device_acquire (device);
3967 if (unlikely (status))
3968 return;
3969
3970 /* XXX: Drop references to external resources */
3971
3972 _cairo_cache_fini (&dev->linear_cache);
3973 _cairo_cache_fini (&dev->path_fill_prim_cache);
3974 _cairo_cache_fini (&dev->path_stroke_prim_cache);
3975
3976 _cairo_freelist_fini (&dev->path_fill_meta_freelist);
3977 _cairo_freelist_fini (&dev->path_stroke_meta_freelist);
3978
3979 if (dev->buffer_stack && dev->buffer_stack_offset) {
3980 cogl_buffer_unmap (dev->buffer_stack);
3981 cogl_object_unref (dev->buffer_stack);
3982 dev->buffer_stack = NULL;
3983 }
3984
3985 for (i = 0; i < CAIRO_OPERATOR_SATURATE; i++)
3986 for (j = 0; j < CAIRO_COGL_TEMPLATE_TYPE_COUNT; j++)
3987 if (dev->template_pipelines[i][j] != NULL) {
3988 cogl_object_unref (dev->template_pipelines[i][j]);
3989 dev->template_pipelines[i][j] = NULL;
3990 }
3991
3992 cogl_object_unref (dev->cogl_context);
3993
3994 cairo_device_release (device);
3995 }
3996
3997 static void
_cairo_cogl_device_destroy(void * device)3998 _cairo_cogl_device_destroy (void *device)
3999 {
4000 cairo_cogl_device_t *dev = device;
4001
4002 g_free (dev);
4003 }
4004
4005 static const cairo_device_backend_t _cairo_cogl_device_backend = {
4006 CAIRO_DEVICE_TYPE_COGL,
4007
4008 NULL, /* lock */
4009 NULL, /* unlock */
4010
4011 _cairo_cogl_device_flush,
4012 _cairo_cogl_device_finish,
4013 _cairo_cogl_device_destroy,
4014 };
4015
4016 cairo_device_t *
cairo_cogl_device_create(CoglContext * cogl_context)4017 cairo_cogl_device_create (CoglContext *cogl_context)
4018 {
4019 cairo_cogl_device_t *dev = g_new (cairo_cogl_device_t, 1);
4020 cairo_status_t status;
4021
4022 dev->cogl_context = cogl_object_ref (cogl_context);
4023
4024 dev->has_npots =
4025 cogl_has_features (cogl_context,
4026 COGL_FEATURE_ID_TEXTURE_NPOT_BASIC,
4027 COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT,
4028 0);
4029
4030 dev->has_mirrored_repeat =
4031 cogl_has_feature (cogl_context,
4032 COGL_FEATURE_ID_MIRRORED_REPEAT);
4033
4034 dev->buffer_stack = NULL;
4035 dev->buffer_stack_size = 4096;
4036
4037 /* Set all template pipelines to NULL */
4038 memset (dev->template_pipelines, 0, sizeof (dev->template_pipelines));
4039
4040 status = _cairo_cache_init (&dev->linear_cache,
4041 _cairo_cogl_linear_gradient_equal,
4042 NULL,
4043 (cairo_destroy_func_t) _cairo_cogl_linear_gradient_destroy,
4044 CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE);
4045 if (unlikely (status)) {
4046 g_free (dev);
4047 return _cairo_device_create_in_error (status);
4048 }
4049
4050 status = _cairo_cache_init (&dev->path_fill_prim_cache,
4051 _cairo_cogl_path_fill_meta_equal,
4052 NULL,
4053 (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy,
4054 CAIRO_COGL_PATH_META_CACHE_SIZE);
4055
4056 status = _cairo_cache_init (&dev->path_stroke_prim_cache,
4057 _cairo_cogl_path_stroke_meta_equal,
4058 NULL,
4059 (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
4060 CAIRO_COGL_PATH_META_CACHE_SIZE);
4061
4062 _cairo_freelist_init (&dev->path_fill_meta_freelist,
4063 sizeof(cairo_cogl_path_fill_meta_t));
4064 _cairo_freelist_init (&dev->path_stroke_meta_freelist,
4065 sizeof(cairo_cogl_path_stroke_meta_t));
4066
4067 _cairo_device_init (&dev->base, &_cairo_cogl_device_backend);
4068 return &dev->base;
4069 }
4070 slim_hidden_def (cairo_cogl_device_create);
4071
4072 cairo_status_t
cairo_cogl_surface_end_frame(cairo_surface_t * abstract_surface)4073 cairo_cogl_surface_end_frame (cairo_surface_t *abstract_surface)
4074 {
4075 cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
4076
4077 if (abstract_surface->backend != &_cairo_cogl_surface_backend)
4078 return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
4079
4080 cairo_surface_flush (abstract_surface);
4081
4082 if (surface->framebuffer)
4083 if (cogl_is_onscreen (surface->framebuffer))
4084 cogl_onscreen_swap_buffers (surface->framebuffer);
4085
4086 //g_print ("n_clip_updates_per_frame = %d\n", surface->n_clip_updates_per_frame);
4087 surface->n_clip_updates_per_frame = 0;
4088
4089 return CAIRO_STATUS_SUCCESS;
4090 }
4091 slim_hidden_def (cairo_cogl_surface_end_frame);
4092
4093 cairo_status_t
cairo_cogl_surface_synchronize(cairo_surface_t * abstract_surface)4094 cairo_cogl_surface_synchronize (cairo_surface_t *abstract_surface)
4095 {
4096 cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
4097
4098 if (abstract_surface->backend != &_cairo_cogl_surface_backend)
4099 return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
4100
4101 if (surface->framebuffer)
4102 cogl_framebuffer_finish (surface->framebuffer);
4103
4104 return CAIRO_STATUS_SUCCESS;
4105 }
4106 slim_hidden_def (cairo_cogl_surface_synchronize);
4107