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