Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3959 | Serge | 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 |
||
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-surface-clipper-private.h" |
||
38 | #include "cairo-fixed-private.h" |
||
39 | #include "cairo-device-private.h" |
||
40 | #include "cairo-composite-rectangles-private.h" |
||
41 | #include "cairo-image-surface-inline.h" |
||
42 | #include "cairo-cogl-private.h" |
||
43 | #include "cairo-cogl-gradient-private.h" |
||
44 | #include "cairo-arc-private.h" |
||
45 | #include "cairo-traps-private.h" |
||
46 | #include "cairo-cogl-context-private.h" |
||
47 | #include "cairo-cogl-utils-private.h" |
||
48 | #include "cairo-box-inline.h" |
||
49 | #include "cairo-surface-subsurface-inline.h" |
||
50 | #include "cairo-surface-fallback-private.h" |
||
51 | #include "cairo-surface-offset-private.h" |
||
52 | |||
53 | #include "cairo-cogl.h" |
||
54 | |||
55 | #include |
||
56 | #include |
||
57 | |||
58 | #define CAIRO_COGL_DEBUG 0 |
||
59 | //#define FILL_WITH_COGL_PATH |
||
60 | //#define USE_CAIRO_PATH_FLATTENER |
||
61 | #define ENABLE_PATH_CACHE |
||
62 | //#define DISABLE_BATCHING |
||
63 | #define USE_COGL_RECTANGLE_API |
||
64 | #define ENABLE_RECTANGLES_FASTPATH |
||
65 | |||
66 | #if defined (USE_COGL_RECTANGLE_API) || defined (ENABLE_PATH_CACHE) |
||
67 | #define NEED_COGL_CONTEXT |
||
68 | #endif |
||
69 | |||
70 | #if CAIRO_COGL_DEBUG && __GNUC__ |
||
71 | #define UNSUPPORTED(reason) ({ \ |
||
72 | g_warning ("cairo-cogl: hit unsupported operation: %s", reason); \ |
||
73 | CAIRO_INT_STATUS_UNSUPPORTED; \ |
||
74 | }) |
||
75 | #else |
||
76 | #define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED |
||
77 | #endif |
||
78 | |||
79 | #define CAIRO_COGL_PATH_META_CACHE_SIZE (1024 * 1024) |
||
80 | |||
81 | typedef struct _cairo_cogl_texture_attributes { |
||
82 | /* nabbed from cairo_surface_attributes_t... */ |
||
83 | cairo_matrix_t matrix; |
||
84 | cairo_extend_t extend; |
||
85 | cairo_filter_t filter; |
||
86 | cairo_bool_t has_component_alpha; |
||
87 | |||
88 | CoglPipelineWrapMode s_wrap; |
||
89 | CoglPipelineWrapMode t_wrap; |
||
90 | } cairo_cogl_texture_attributes_t; |
||
91 | |||
92 | typedef enum _cairo_cogl_journal_entry_type { |
||
93 | CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE, |
||
94 | CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE, |
||
95 | CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH, |
||
96 | CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP |
||
97 | } cairo_cogl_journal_entry_type_t; |
||
98 | |||
99 | typedef struct _cairo_cogl_journal_entry { |
||
100 | cairo_cogl_journal_entry_type_t type; |
||
101 | } cairo_cogl_journal_entry_t; |
||
102 | |||
103 | typedef struct _cairo_cogl_journal_clip_entry { |
||
104 | cairo_cogl_journal_entry_t base; |
||
105 | cairo_clip_t *clip; |
||
106 | } cairo_cogl_journal_clip_entry_t; |
||
107 | |||
108 | typedef struct _cairo_cogl_journal_rect_entry { |
||
109 | cairo_cogl_journal_entry_t base; |
||
110 | CoglPipeline *pipeline; |
||
111 | float x; |
||
112 | float y; |
||
113 | float width; |
||
114 | float height; |
||
115 | int n_layers; |
||
116 | cairo_matrix_t ctm; |
||
117 | } cairo_cogl_journal_rect_entry_t; |
||
118 | |||
119 | typedef struct _cairo_cogl_journal_prim_entry { |
||
120 | cairo_cogl_journal_entry_t base; |
||
121 | CoglPipeline *pipeline; |
||
122 | CoglPrimitive *primitive; |
||
123 | gboolean has_transform; |
||
124 | cairo_matrix_t transform; |
||
125 | } cairo_cogl_journal_prim_entry_t; |
||
126 | |||
127 | typedef struct _cairo_cogl_journal_path_entry { |
||
128 | cairo_cogl_journal_entry_t base; |
||
129 | CoglPipeline *pipeline; |
||
130 | CoglPath *path; |
||
131 | } cairo_cogl_journal_path_entry_t; |
||
132 | |||
133 | typedef struct _cairo_cogl_path_fill_meta { |
||
134 | cairo_cache_entry_t cache_entry; |
||
135 | cairo_reference_count_t ref_count; |
||
136 | int counter; |
||
137 | cairo_path_fixed_t *user_path; |
||
138 | cairo_matrix_t ctm_inverse; |
||
139 | |||
140 | /* TODO */ |
||
141 | #if 0 |
||
142 | /* A cached path tessellation should be re-usable with different rotations |
||
143 | * and translations but not for different scales. |
||
144 | * |
||
145 | * one idea is to track the diagonal lenghts of a unit rectangle |
||
146 | * transformed through the original ctm use to tesselate the geometry |
||
147 | * so we can check what the lengths are for any new ctm to know if |
||
148 | * this geometry is compatible. |
||
149 | */ |
||
150 | #endif |
||
151 | |||
152 | CoglPrimitive *prim; |
||
153 | } cairo_cogl_path_fill_meta_t; |
||
154 | |||
155 | typedef struct _cairo_cogl_path_stroke_meta { |
||
156 | cairo_cache_entry_t cache_entry; |
||
157 | cairo_reference_count_t ref_count; |
||
158 | int counter; |
||
159 | cairo_path_fixed_t *user_path; |
||
160 | cairo_matrix_t ctm_inverse; |
||
161 | cairo_stroke_style_t style; |
||
162 | double tolerance; |
||
163 | |||
164 | /* TODO */ |
||
165 | #if 0 |
||
166 | /* A cached path tessellation should be re-usable with different rotations |
||
167 | * and translations but not for different scales. |
||
168 | * |
||
169 | * one idea is to track the diagonal lenghts of a unit rectangle |
||
170 | * transformed through the original ctm use to tesselate the geometry |
||
171 | * so we can check what the lengths are for any new ctm to know if |
||
172 | * this geometry is compatible. |
||
173 | */ |
||
174 | #endif |
||
175 | |||
176 | CoglPrimitive *prim; |
||
177 | } cairo_cogl_path_stroke_meta_t; |
||
178 | |||
179 | static cairo_surface_t * |
||
180 | _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev, |
||
181 | cairo_bool_t ignore_alpha, |
||
182 | CoglFramebuffer *framebuffer, |
||
183 | CoglTexture *texture); |
||
184 | |||
185 | static cairo_int_status_t |
||
186 | _cairo_cogl_surface_fill (void *abstract_surface, |
||
187 | cairo_operator_t op, |
||
188 | const cairo_pattern_t *source, |
||
189 | const cairo_path_fixed_t *path, |
||
190 | cairo_fill_rule_t fill_rule, |
||
191 | double tolerance, |
||
192 | cairo_antialias_t antialias, |
||
193 | const cairo_clip_t *clip); |
||
194 | |||
195 | static void |
||
196 | _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface); |
||
197 | |||
198 | cairo_private extern const cairo_surface_backend_t _cairo_cogl_surface_backend; |
||
199 | |||
200 | slim_hidden_proto (cairo_cogl_device_create); |
||
201 | slim_hidden_proto (cairo_cogl_surface_create); |
||
202 | slim_hidden_proto (cairo_cogl_surface_get_framebuffer); |
||
203 | slim_hidden_proto (cairo_cogl_surface_get_texture); |
||
204 | slim_hidden_proto (cairo_cogl_surface_end_frame); |
||
205 | |||
206 | static cairo_cogl_device_t * |
||
207 | to_device (cairo_device_t *device) |
||
208 | { |
||
209 | return (cairo_cogl_device_t *)device; |
||
210 | } |
||
211 | |||
212 | /* moves trap points such that they become the actual corners of the trapezoid */ |
||
213 | static void |
||
214 | _sanitize_trap (cairo_trapezoid_t *t) |
||
215 | { |
||
216 | cairo_trapezoid_t s = *t; |
||
217 | |||
218 | #define FIX(lr, tb, p) \ |
||
219 | if (t->lr.p.y != t->tb) { \ |
||
220 | t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \ |
||
221 | t->lr.p.y = s.tb; \ |
||
222 | } |
||
223 | FIX (left, top, p1); |
||
224 | FIX (left, bottom, p2); |
||
225 | FIX (right, top, p1); |
||
226 | FIX (right, bottom, p2); |
||
227 | } |
||
228 | |||
229 | static cairo_status_t |
||
230 | _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface) |
||
231 | { |
||
232 | GError *error = NULL; |
||
233 | |||
234 | if (surface->framebuffer) |
||
235 | return CAIRO_STATUS_SUCCESS; |
||
236 | |||
237 | surface->framebuffer = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (surface->texture)); |
||
238 | if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) { |
||
239 | g_error_free (error); |
||
240 | cogl_object_unref (surface->framebuffer); |
||
241 | surface->framebuffer = NULL; |
||
242 | return CAIRO_STATUS_NO_MEMORY; |
||
243 | } |
||
244 | |||
245 | cogl_push_framebuffer (surface->framebuffer); |
||
246 | cogl_ortho (0, surface->width, |
||
247 | surface->height, 0, |
||
248 | -1, 100); |
||
249 | cogl_pop_framebuffer (); |
||
250 | |||
251 | return CAIRO_STATUS_SUCCESS; |
||
252 | } |
||
253 | |||
254 | static cairo_surface_t * |
||
255 | _cairo_cogl_surface_create_similar (void *abstract_surface, |
||
256 | cairo_content_t content, |
||
257 | int width, |
||
258 | int height) |
||
259 | { |
||
260 | cairo_cogl_surface_t *reference_surface = abstract_surface; |
||
261 | cairo_cogl_surface_t *surface; |
||
262 | CoglTexture *texture; |
||
263 | cairo_status_t status; |
||
264 | |||
265 | texture = cogl_texture_new_with_size (width, height, |
||
266 | COGL_TEXTURE_NO_SLICING, |
||
267 | (content & CAIRO_CONTENT_COLOR) ? |
||
268 | COGL_PIXEL_FORMAT_BGRA_8888_PRE : |
||
269 | COGL_PIXEL_FORMAT_A_8); |
||
270 | if (!texture) |
||
271 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
||
272 | |||
273 | surface = (cairo_cogl_surface_t *) |
||
274 | _cairo_cogl_surface_create_full (to_device(reference_surface->base.device), |
||
275 | (content & CAIRO_CONTENT_ALPHA) == 0, |
||
276 | NULL, |
||
277 | texture); |
||
278 | if (unlikely (surface->base.status)) |
||
279 | return &surface->base; |
||
280 | |||
281 | status = _cairo_cogl_surface_ensure_framebuffer (surface); |
||
282 | if (unlikely (status)) { |
||
283 | cairo_surface_destroy (&surface->base); |
||
284 | return _cairo_surface_create_in_error (status); |
||
285 | } |
||
286 | |||
287 | return &surface->base; |
||
288 | } |
||
289 | |||
290 | static cairo_bool_t |
||
291 | _cairo_cogl_surface_get_extents (void *abstract_surface, |
||
292 | cairo_rectangle_int_t *extents) |
||
293 | { |
||
294 | cairo_cogl_surface_t *surface = abstract_surface; |
||
295 | |||
296 | extents->x = 0; |
||
297 | extents->y = 0; |
||
298 | extents->width = surface->width; |
||
299 | extents->height = surface->height; |
||
300 | |||
301 | return TRUE; |
||
302 | } |
||
303 | |||
304 | static void |
||
305 | _cairo_cogl_journal_free (cairo_cogl_surface_t *surface) |
||
306 | { |
||
307 | GList *l; |
||
308 | |||
309 | for (l = surface->journal->head; l; l = l->next) { |
||
310 | cairo_cogl_journal_entry_t *entry = l->data; |
||
311 | |||
312 | if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE) { |
||
313 | cairo_cogl_journal_prim_entry_t *prim_entry = |
||
314 | (cairo_cogl_journal_prim_entry_t *)entry; |
||
315 | cogl_object_unref (prim_entry->primitive); |
||
316 | } else if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH) { |
||
317 | cairo_cogl_journal_path_entry_t *path_entry = |
||
318 | (cairo_cogl_journal_path_entry_t *)entry; |
||
319 | cogl_object_unref (path_entry->path); |
||
320 | } |
||
321 | } |
||
322 | |||
323 | g_queue_free (surface->journal); |
||
324 | surface->journal = NULL; |
||
325 | } |
||
326 | |||
327 | #ifdef FILL_WITH_COGL_PATH |
||
328 | static void |
||
329 | _cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface, |
||
330 | CoglPipeline *pipeline, |
||
331 | CoglPath *path) |
||
332 | { |
||
333 | cairo_cogl_journal_path_entry_t *entry; |
||
334 | |||
335 | if (unlikely (surface->journal == NULL)) |
||
336 | surface->journal = g_queue_new (); |
||
337 | |||
338 | /* FIXME: Instead of a GList here we should stack allocate the journal |
||
339 | * entries so it would be cheaper to allocate and they can all be freed in |
||
340 | * one go after flushing! */ |
||
341 | entry = g_slice_new (cairo_cogl_journal_path_entry_t); |
||
342 | entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH; |
||
343 | |||
344 | entry->pipeline = cogl_object_ref (pipeline); |
||
345 | entry->path = cogl_object_ref (path); |
||
346 | |||
347 | g_queue_push_tail (surface->journal, entry); |
||
348 | |||
349 | #ifdef DISABLE_BATCHING |
||
350 | _cairo_cogl_journal_flush (surface); |
||
351 | #endif |
||
352 | } |
||
353 | #endif /* FILL_WITH_COGL_PATH */ |
||
354 | |||
355 | static void |
||
356 | _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface, |
||
357 | CoglPipeline *pipeline, |
||
358 | CoglPrimitive *primitive, |
||
359 | cairo_matrix_t *transform) |
||
360 | { |
||
361 | cairo_cogl_journal_prim_entry_t *entry; |
||
362 | |||
363 | if (unlikely (surface->journal == NULL)) |
||
364 | surface->journal = g_queue_new (); |
||
365 | |||
366 | /* FIXME: Instead of a GList here we should stack allocate the journal |
||
367 | * entries so it would be cheaper to allocate and they can all be freed in |
||
368 | * one go after flushing! */ |
||
369 | entry = g_slice_new (cairo_cogl_journal_prim_entry_t); |
||
370 | entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE; |
||
371 | |||
372 | entry->pipeline = cogl_object_ref (pipeline); |
||
373 | |||
374 | if (transform) { |
||
375 | entry->transform = *transform; |
||
376 | entry->has_transform = TRUE; |
||
377 | } else |
||
378 | entry->has_transform = FALSE; |
||
379 | |||
380 | entry->primitive = cogl_object_ref (primitive); |
||
381 | |||
382 | g_queue_push_tail (surface->journal, entry); |
||
383 | |||
384 | #ifdef DISABLE_BATCHING |
||
385 | _cairo_cogl_journal_flush (surface); |
||
386 | #endif |
||
387 | } |
||
388 | |||
389 | static void |
||
390 | _cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface, |
||
391 | CoglPipeline *pipeline, |
||
392 | float x, |
||
393 | float y, |
||
394 | float width, |
||
395 | float height, |
||
396 | int n_layers, |
||
397 | cairo_matrix_t *ctm) |
||
398 | { |
||
399 | cairo_cogl_journal_rect_entry_t *entry; |
||
400 | |||
401 | if (unlikely (surface->journal == NULL)) |
||
402 | surface->journal = g_queue_new (); |
||
403 | |||
404 | /* FIXME: Instead of a GList here we should stack allocate the journal |
||
405 | * entries so it would be cheaper to allocate and they can all be freed in |
||
406 | * one go after flushing! */ |
||
407 | entry = g_slice_new (cairo_cogl_journal_rect_entry_t); |
||
408 | entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE; |
||
409 | |||
410 | entry->pipeline = cogl_object_ref (pipeline); |
||
411 | |||
412 | entry->x = x; |
||
413 | entry->y = y; |
||
414 | entry->width = width; |
||
415 | entry->height = height; |
||
416 | entry->ctm = *ctm; |
||
417 | |||
418 | entry->n_layers = n_layers; |
||
419 | |||
420 | g_queue_push_tail (surface->journal, entry); |
||
421 | |||
422 | #ifdef DISABLE_BATCHING |
||
423 | _cairo_cogl_journal_flush (surface); |
||
424 | #endif |
||
425 | } |
||
426 | |||
427 | static void |
||
428 | _cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface, |
||
429 | const cairo_clip_t *clip) |
||
430 | { |
||
431 | cairo_cogl_journal_clip_entry_t *entry; |
||
432 | |||
433 | if (unlikely (surface->journal == NULL)) |
||
434 | surface->journal = g_queue_new (); |
||
435 | |||
436 | /* FIXME: Instead of a GList here we should stack allocate the journal |
||
437 | * entries so it would be cheaper to allocate and they can all be freed in |
||
438 | * one go after flushing! */ |
||
439 | entry = g_slice_new (cairo_cogl_journal_clip_entry_t); |
||
440 | entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP; |
||
441 | entry->clip = _cairo_clip_copy (clip); |
||
442 | |||
443 | g_queue_push_tail (surface->journal, entry); |
||
444 | } |
||
445 | |||
446 | static void |
||
447 | _cairo_cogl_journal_discard (cairo_cogl_surface_t *surface) |
||
448 | { |
||
449 | GList *l; |
||
450 | |||
451 | if (!surface->journal) { |
||
452 | assert (surface->last_clip == NULL); |
||
453 | return; |
||
454 | } |
||
455 | |||
456 | if (surface->buffer_stack && surface->buffer_stack_offset) { |
||
457 | cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack)); |
||
458 | cogl_object_unref (surface->buffer_stack); |
||
459 | surface->buffer_stack = NULL; |
||
460 | } |
||
461 | |||
462 | for (l = surface->journal->head; l; l = l->next) { |
||
463 | cairo_cogl_journal_entry_t *entry = l->data; |
||
464 | gsize entry_size; |
||
465 | |||
466 | switch (entry->type) |
||
467 | { |
||
468 | case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: { |
||
469 | cairo_cogl_journal_clip_entry_t *clip_entry = |
||
470 | (cairo_cogl_journal_clip_entry_t *)entry; |
||
471 | _cairo_clip_destroy (clip_entry->clip); |
||
472 | entry_size = sizeof (cairo_cogl_journal_clip_entry_t); |
||
473 | break; |
||
474 | } |
||
475 | case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: { |
||
476 | cairo_cogl_journal_rect_entry_t *rect_entry = |
||
477 | (cairo_cogl_journal_rect_entry_t *)entry; |
||
478 | cogl_object_unref (rect_entry->pipeline); |
||
479 | entry_size = sizeof (cairo_cogl_journal_rect_entry_t); |
||
480 | break; |
||
481 | } |
||
482 | case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: { |
||
483 | cairo_cogl_journal_prim_entry_t *prim_entry = |
||
484 | (cairo_cogl_journal_prim_entry_t *)entry; |
||
485 | cogl_object_unref (prim_entry->pipeline); |
||
486 | cogl_object_unref (prim_entry->primitive); |
||
487 | entry_size = sizeof (cairo_cogl_journal_prim_entry_t); |
||
488 | break; |
||
489 | } |
||
490 | case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: { |
||
491 | cairo_cogl_journal_path_entry_t *path_entry = |
||
492 | (cairo_cogl_journal_path_entry_t *)entry; |
||
493 | cogl_object_unref (path_entry->pipeline); |
||
494 | cogl_object_unref (path_entry->path); |
||
495 | entry_size = sizeof (cairo_cogl_journal_path_entry_t); |
||
496 | break; |
||
497 | } |
||
498 | default: |
||
499 | assert (0); /* not reached! */ |
||
500 | entry_size = 0; /* avoid compiler warning */ |
||
501 | } |
||
502 | g_slice_free1 (entry_size, entry); |
||
503 | } |
||
504 | |||
505 | g_queue_clear (surface->journal); |
||
506 | |||
507 | if (surface->last_clip) { |
||
508 | _cairo_clip_destroy (surface->last_clip); |
||
509 | surface->last_clip = NULL; |
||
510 | } |
||
511 | } |
||
512 | |||
513 | static CoglAttributeBuffer * |
||
514 | _cairo_cogl_surface_allocate_buffer_space (cairo_cogl_surface_t *surface, |
||
515 | size_t size, |
||
516 | size_t *offset, |
||
517 | void **pointer) |
||
518 | { |
||
519 | /* XXX: In the Cogl journal we found it more efficient to have a pool of |
||
520 | * buffers that we re-cycle but for now we simply thow away our stack |
||
521 | * buffer each time we flush. */ |
||
522 | if (unlikely (surface->buffer_stack && |
||
523 | (surface->buffer_stack_size - surface->buffer_stack_offset) < size)) { |
||
524 | cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack)); |
||
525 | cogl_object_unref (surface->buffer_stack); |
||
526 | surface->buffer_stack = NULL; |
||
527 | surface->buffer_stack_size *= 2; |
||
528 | } |
||
529 | |||
530 | if (unlikely (surface->buffer_stack_size < size)) |
||
531 | surface->buffer_stack_size = size * 2; |
||
532 | |||
533 | if (unlikely (surface->buffer_stack == NULL)) { |
||
534 | surface->buffer_stack = cogl_attribute_buffer_new (surface->buffer_stack_size, NULL); |
||
535 | surface->buffer_stack_pointer = |
||
536 | cogl_buffer_map (COGL_BUFFER (surface->buffer_stack), |
||
537 | COGL_BUFFER_ACCESS_WRITE, |
||
538 | COGL_BUFFER_MAP_HINT_DISCARD); |
||
539 | surface->buffer_stack_offset = 0; |
||
540 | } |
||
541 | |||
542 | *pointer = surface->buffer_stack_pointer + surface->buffer_stack_offset; |
||
543 | *offset = surface->buffer_stack_offset; |
||
544 | |||
545 | surface->buffer_stack_offset += size; |
||
546 | return cogl_object_ref (surface->buffer_stack); |
||
547 | } |
||
548 | |||
549 | |||
550 | static CoglAttributeBuffer * |
||
551 | _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface, |
||
552 | cairo_traps_t *traps, |
||
553 | size_t *offset, |
||
554 | gboolean one_shot) |
||
555 | { |
||
556 | CoglAttributeBuffer *buffer; |
||
557 | int n_traps = traps->num_traps; |
||
558 | int i; |
||
559 | CoglVertexP2 *triangles; |
||
560 | |||
561 | if (one_shot) { |
||
562 | buffer = _cairo_cogl_surface_allocate_buffer_space (surface, |
||
563 | n_traps * sizeof (CoglVertexP2) * 6, |
||
564 | offset, |
||
565 | (void **)&triangles); |
||
566 | if (!buffer) |
||
567 | return NULL; |
||
568 | } else { |
||
569 | buffer = cogl_attribute_buffer_new (n_traps * sizeof (CoglVertexP2) * 6, NULL); |
||
570 | if (!buffer) |
||
571 | return NULL; |
||
572 | triangles = cogl_buffer_map (COGL_BUFFER (buffer), |
||
573 | COGL_BUFFER_ACCESS_WRITE, |
||
574 | COGL_BUFFER_MAP_HINT_DISCARD); |
||
575 | if (!triangles) |
||
576 | return NULL; |
||
577 | *offset = 0; |
||
578 | } |
||
579 | |||
580 | /* XXX: This is can be very expensive. I'm not sure a.t.m if it's |
||
581 | * predominantly the bandwidth required or the cost of the fixed_to_float |
||
582 | * conversions but either way we should try using an index buffer to |
||
583 | * reduce the amount we upload by 1/3 (offset by allocating and uploading |
||
584 | * indices though) sadly though my experience with the intel mesa drivers |
||
585 | * is that slow paths can easily be hit when starting to use indices. |
||
586 | */ |
||
587 | for (i = 0; i < n_traps; i++) |
||
588 | { |
||
589 | CoglVertexP2 *p = &triangles[i * 6]; |
||
590 | cairo_trapezoid_t *trap = &traps->traps[i]; |
||
591 | |||
592 | p[0].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x); |
||
593 | p[0].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y); |
||
594 | |||
595 | p[1].x = _cairo_cogl_util_fixed_to_float (trap->left.p2.x); |
||
596 | p[1].y = _cairo_cogl_util_fixed_to_float (trap->left.p2.y); |
||
597 | |||
598 | p[2].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x); |
||
599 | p[2].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y); |
||
600 | |||
601 | p[3].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x); |
||
602 | p[3].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y); |
||
603 | |||
604 | p[4].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x); |
||
605 | p[4].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y); |
||
606 | |||
607 | p[5].x = _cairo_cogl_util_fixed_to_float (trap->right.p1.x); |
||
608 | p[5].y = _cairo_cogl_util_fixed_to_float (trap->right.p1.y); |
||
609 | } |
||
610 | |||
611 | if (!one_shot) |
||
612 | cogl_buffer_unmap (COGL_BUFFER (buffer)); |
||
613 | |||
614 | return buffer; |
||
615 | } |
||
616 | |||
617 | /* Used for solid fills, in this case we just need a mesh made of |
||
618 | * a single (2-component) position attribute. */ |
||
619 | static CoglPrimitive * |
||
620 | _cairo_cogl_traps_to_composite_prim_p2 (cairo_cogl_surface_t *surface, |
||
621 | cairo_traps_t *traps, |
||
622 | gboolean one_shot) |
||
623 | { |
||
624 | size_t offset; |
||
625 | CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot); |
||
626 | CoglAttribute *pos = cogl_attribute_new (buffer, |
||
627 | "cogl_position_in", |
||
628 | sizeof (CoglVertexP2), |
||
629 | offset, |
||
630 | 2, |
||
631 | COGL_ATTRIBUTE_TYPE_FLOAT); |
||
632 | CoglPrimitive *prim; |
||
633 | |||
634 | /* The attribute will have taken a reference on the buffer */ |
||
635 | cogl_object_unref (buffer); |
||
636 | |||
637 | prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES, |
||
638 | traps->num_traps * 6, pos, NULL); |
||
639 | |||
640 | /* The primitive will now keep the attribute alive... */ |
||
641 | cogl_object_unref (pos); |
||
642 | |||
643 | return prim; |
||
644 | } |
||
645 | |||
646 | /* Used for surface fills, in this case we need a mesh made of a single |
||
647 | * (2-component) position attribute + we also alias the same attribute as |
||
648 | * (2-component) texture coordinates */ |
||
649 | static CoglPrimitive * |
||
650 | _cairo_cogl_traps_to_composite_prim_p2t2 (cairo_cogl_surface_t *surface, |
||
651 | cairo_traps_t *traps, |
||
652 | gboolean one_shot) |
||
653 | { |
||
654 | size_t offset; |
||
655 | CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot); |
||
656 | CoglAttribute *pos = cogl_attribute_new (buffer, |
||
657 | "cogl_position_in", |
||
658 | sizeof (CoglVertexP2), |
||
659 | offset, |
||
660 | 2, |
||
661 | COGL_ATTRIBUTE_TYPE_FLOAT); |
||
662 | CoglAttribute *tex_coords = cogl_attribute_new (buffer, |
||
663 | "cogl_tex_coord0_in", |
||
664 | sizeof (CoglVertexP2), |
||
665 | 0, |
||
666 | 2, |
||
667 | COGL_ATTRIBUTE_TYPE_FLOAT); |
||
668 | CoglPrimitive *prim; |
||
669 | |||
670 | /* The attributes will have taken references on the buffer */ |
||
671 | cogl_object_unref (buffer); |
||
672 | |||
673 | prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES, |
||
674 | traps->num_traps * 6, pos, tex_coords, NULL); |
||
675 | |||
676 | /* The primitive will now keep the attributes alive... */ |
||
677 | cogl_object_unref (pos); |
||
678 | cogl_object_unref (tex_coords); |
||
679 | |||
680 | return prim; |
||
681 | } |
||
682 | |||
683 | static CoglPrimitive * |
||
684 | _cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface, |
||
685 | cairo_traps_t *traps, |
||
686 | int n_layers, |
||
687 | gboolean one_shot) |
||
688 | { |
||
689 | int n_traps = traps->num_traps; |
||
690 | int i; |
||
691 | |||
692 | /* XXX: Ideally we would skip tessellating to traps entirely since |
||
693 | * given their representation, conversion to triangles is quite expensive. |
||
694 | * |
||
695 | * This simplifies the conversion to triangles by making the end points of |
||
696 | * the two side lines actually just correspond to the corners of the |
||
697 | * traps. |
||
698 | */ |
||
699 | for (i = 0; i < n_traps; i++) |
||
700 | _sanitize_trap (&traps->traps[i]); |
||
701 | |||
702 | if (n_layers == 0) |
||
703 | return _cairo_cogl_traps_to_composite_prim_p2 (surface, traps, one_shot); |
||
704 | else { |
||
705 | assert (n_layers == 1); |
||
706 | return _cairo_cogl_traps_to_composite_prim_p2t2 (surface, traps, one_shot); |
||
707 | } |
||
708 | } |
||
709 | |||
710 | static cairo_int_status_t |
||
711 | _cairo_cogl_fill_to_primitive (cairo_cogl_surface_t *surface, |
||
712 | const cairo_path_fixed_t *path, |
||
713 | cairo_fill_rule_t fill_rule, |
||
714 | double tolerance, |
||
715 | int n_layers, |
||
716 | cairo_bool_t one_shot, |
||
717 | CoglPrimitive **primitive, |
||
718 | size_t *size) |
||
719 | { |
||
720 | cairo_traps_t traps; |
||
721 | cairo_int_status_t status; |
||
722 | |||
723 | _cairo_traps_init (&traps); |
||
724 | status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps); |
||
725 | if (unlikely (status)) |
||
726 | goto BAIL; |
||
727 | |||
728 | if (traps.num_traps == 0) { |
||
729 | status = CAIRO_INT_STATUS_NOTHING_TO_DO; |
||
730 | goto BAIL; |
||
731 | } |
||
732 | |||
733 | *size = traps.num_traps * sizeof (CoglVertexP2) * 6; |
||
734 | |||
735 | *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot); |
||
736 | if (!*primitive) { |
||
737 | status = CAIRO_INT_STATUS_NO_MEMORY; |
||
738 | goto BAIL; |
||
739 | } |
||
740 | |||
741 | BAIL: |
||
742 | _cairo_traps_fini (&traps); |
||
743 | return status; |
||
744 | } |
||
745 | |||
746 | static void |
||
747 | _cairo_cogl_clip_push_box (const cairo_box_t *box) |
||
748 | { |
||
749 | if (_cairo_box_is_pixel_aligned (box)) { |
||
750 | cairo_rectangle_int_t rect; |
||
751 | _cairo_box_round_to_rectangle (box, &rect); |
||
752 | cogl_clip_push_window_rectangle (rect.x, rect.y, |
||
753 | rect.width, rect.height); |
||
754 | } else { |
||
755 | double x1, y1, x2, y2; |
||
756 | _cairo_box_to_doubles (box, &x1, &y1, &x2, &y2); |
||
757 | cogl_clip_push_rectangle (x1, y1, x2, y2); |
||
758 | } |
||
759 | } |
||
760 | |||
761 | static void |
||
762 | _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface) |
||
763 | { |
||
764 | GList *l; |
||
765 | int clip_stack_depth = 0; |
||
766 | int i; |
||
767 | |||
768 | if (!surface->journal) |
||
769 | return; |
||
770 | |||
771 | if (surface->buffer_stack && surface->buffer_stack_offset) { |
||
772 | cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack)); |
||
773 | cogl_object_unref (surface->buffer_stack); |
||
774 | surface->buffer_stack = NULL; |
||
775 | } |
||
776 | |||
777 | cogl_set_framebuffer (surface->framebuffer); |
||
778 | |||
779 | cogl_push_matrix (); |
||
780 | |||
781 | for (l = surface->journal->head; l; l = l->next) { |
||
782 | cairo_cogl_journal_entry_t *entry = l->data; |
||
783 | |||
784 | switch (entry->type) |
||
785 | { |
||
786 | case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: { |
||
787 | cairo_cogl_journal_clip_entry_t *clip_entry = |
||
788 | (cairo_cogl_journal_clip_entry_t *)entry; |
||
789 | cairo_clip_path_t *path; |
||
790 | #if 0 |
||
791 | cairo_bool_t checked_for_primitives = FALSE; |
||
792 | cairo_cogl_clip_primitives_t *clip_primitives; |
||
793 | #endif |
||
794 | |||
795 | for (i = 0; i < clip_stack_depth; i++) |
||
796 | cogl_clip_pop (); |
||
797 | clip_stack_depth = 0; |
||
798 | |||
799 | for (path = clip_entry->clip->path, i = 0; path; path = path->prev, i++) { |
||
800 | cairo_rectangle_int_t extents; |
||
801 | cairo_int_status_t status; |
||
802 | CoglPrimitive *prim; |
||
803 | size_t prim_size; |
||
804 | |||
805 | _cairo_path_fixed_approximate_clip_extents (&path->path, &extents); |
||
806 | |||
807 | /* TODO - maintain a fifo of the last 10 used clips with cached |
||
808 | * primitives to see if we can avoid tesselating the path and |
||
809 | * uploading the vertices... |
||
810 | */ |
||
811 | #if 0 |
||
812 | if (!checked_for_primitives) { |
||
813 | clip_primitives = find_clip_primitives (clip); |
||
814 | checked_for_primitives = TRUE; |
||
815 | } |
||
816 | if (clip_primitives) |
||
817 | prim = clip_primitives->primitives[i]; |
||
818 | #endif |
||
819 | status = _cairo_cogl_fill_to_primitive (surface, |
||
820 | &path->path, |
||
821 | path->fill_rule, |
||
822 | path->tolerance, |
||
823 | 0, |
||
824 | TRUE, |
||
825 | &prim, |
||
826 | &prim_size); |
||
827 | if (unlikely (status)) { |
||
828 | g_warning ("Failed to get primitive for clip path while flushing journal"); |
||
829 | continue; |
||
830 | } |
||
831 | clip_stack_depth++; |
||
832 | cogl_clip_push_primitive (prim, |
||
833 | extents.x, extents.y, |
||
834 | extents.x + extents.width, |
||
835 | extents.y + extents.height); |
||
836 | cogl_object_unref (prim); |
||
837 | } |
||
838 | |||
839 | for (i = 0; i < clip_entry->clip->num_boxes; i++) { |
||
840 | clip_stack_depth++; |
||
841 | _cairo_cogl_clip_push_box (&clip_entry->clip->boxes[i]); |
||
842 | } |
||
843 | |||
844 | surface->n_clip_updates_per_frame++; |
||
845 | break; |
||
846 | } |
||
847 | case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: { |
||
848 | cairo_cogl_journal_rect_entry_t *rect_entry = |
||
849 | (cairo_cogl_journal_rect_entry_t *)entry; |
||
850 | float tex_coords[8]; |
||
851 | float x1 = rect_entry->x; |
||
852 | float y1 = rect_entry->y; |
||
853 | float x2 = rect_entry->x + rect_entry->width; |
||
854 | float y2 = rect_entry->y + rect_entry->height; |
||
855 | cairo_matrix_t *ctm = &rect_entry->ctm; |
||
856 | float ctmfv[16] = { |
||
857 | ctm->xx, ctm->yx, 0, 0, |
||
858 | ctm->xy, ctm->yy, 0, 0, |
||
859 | 0, 0, 1, 0, |
||
860 | ctm->x0, ctm->y0, 0, 1 |
||
861 | }; |
||
862 | CoglMatrix transform; |
||
863 | |||
864 | cogl_matrix_init_from_array (&transform, ctmfv); |
||
865 | |||
866 | if (rect_entry->n_layers) { |
||
867 | g_assert (rect_entry->n_layers <= 2); |
||
868 | tex_coords[0] = x1; |
||
869 | tex_coords[1] = y1; |
||
870 | tex_coords[2] = x2; |
||
871 | tex_coords[3] = y2; |
||
872 | if (rect_entry->n_layers > 1) |
||
873 | memcpy (&tex_coords[4], tex_coords, sizeof (float) * 4); |
||
874 | } |
||
875 | |||
876 | cogl_set_source (rect_entry->pipeline); |
||
877 | cogl_push_matrix (); |
||
878 | cogl_transform (&transform); |
||
879 | cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2, |
||
880 | tex_coords, 4 * rect_entry->n_layers); |
||
881 | cogl_pop_matrix (); |
||
882 | break; |
||
883 | } |
||
884 | case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: { |
||
885 | cairo_cogl_journal_prim_entry_t *prim_entry = |
||
886 | (cairo_cogl_journal_prim_entry_t *)entry; |
||
887 | CoglMatrix transform; |
||
888 | |||
889 | cogl_push_matrix (); |
||
890 | if (prim_entry->has_transform) { |
||
891 | cairo_matrix_t *ctm = &prim_entry->transform; |
||
892 | float ctmfv[16] = { |
||
893 | ctm->xx, ctm->yx, 0, 0, |
||
894 | ctm->xy, ctm->yy, 0, 0, |
||
895 | 0, 0, 1, 0, |
||
896 | ctm->x0, ctm->y0, 0, 1 |
||
897 | }; |
||
898 | cogl_matrix_init_from_array (&transform, ctmfv); |
||
899 | cogl_transform (&transform); |
||
900 | } else { |
||
901 | cogl_matrix_init_identity (&transform); |
||
902 | cogl_set_modelview_matrix (&transform); |
||
903 | } |
||
904 | |||
905 | cogl_set_source (prim_entry->pipeline); |
||
906 | cogl_primitive_draw (prim_entry->primitive); |
||
907 | cogl_pop_matrix (); |
||
908 | break; |
||
909 | } |
||
910 | case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: { |
||
911 | cairo_cogl_journal_path_entry_t *path_entry = |
||
912 | (cairo_cogl_journal_path_entry_t *)entry; |
||
913 | |||
914 | cogl_set_source (path_entry->pipeline); |
||
915 | cogl_path_fill (path_entry->path); |
||
916 | break; |
||
917 | } |
||
918 | default: |
||
919 | assert (0); /* not reached! */ |
||
920 | } |
||
921 | } |
||
922 | |||
923 | cogl_pop_matrix (); |
||
924 | |||
925 | for (i = 0; i < clip_stack_depth; i++) |
||
926 | cogl_clip_pop (); |
||
927 | |||
928 | _cairo_cogl_journal_discard (surface); |
||
929 | } |
||
930 | |||
931 | static cairo_status_t |
||
932 | _cairo_cogl_surface_flush (void *abstract_surface, |
||
933 | unsigned flags) |
||
934 | { |
||
935 | cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; |
||
936 | |||
937 | if (flags) |
||
938 | return CAIRO_STATUS_SUCCESS; |
||
939 | |||
940 | _cairo_cogl_journal_flush (surface); |
||
941 | |||
942 | return CAIRO_STATUS_SUCCESS; |
||
943 | } |
||
944 | |||
945 | static cairo_status_t |
||
946 | _cairo_cogl_surface_finish (void *abstract_surface) |
||
947 | { |
||
948 | cairo_cogl_surface_t *surface = abstract_surface; |
||
949 | |||
950 | if (surface->texture) |
||
951 | cogl_object_unref (surface->texture); |
||
952 | |||
953 | if (surface->framebuffer) |
||
954 | cogl_object_unref (surface->framebuffer); |
||
955 | |||
956 | if (surface->journal) |
||
957 | _cairo_cogl_journal_free (surface); |
||
958 | |||
959 | /*XXX wtf */ |
||
960 | cairo_device_release (surface->base.device); |
||
961 | |||
962 | return CAIRO_STATUS_SUCCESS; |
||
963 | } |
||
964 | |||
965 | static CoglPixelFormat |
||
966 | get_cogl_format_from_cairo_format (cairo_format_t cairo_format); |
||
967 | |||
968 | /* XXX: We often use RGBA format for onscreen framebuffers so make sure |
||
969 | * to handle CAIRO_FORMAT_INVALID sensibly */ |
||
970 | static cairo_format_t |
||
971 | get_cairo_format_from_cogl_format (CoglPixelFormat format) |
||
972 | { |
||
973 | switch ((int)format) |
||
974 | { |
||
975 | case COGL_PIXEL_FORMAT_A_8: |
||
976 | return CAIRO_FORMAT_A8; |
||
977 | case COGL_PIXEL_FORMAT_RGB_565: |
||
978 | return CAIRO_FORMAT_RGB16_565; |
||
979 | |||
980 | case COGL_PIXEL_FORMAT_BGRA_8888_PRE: |
||
981 | case COGL_PIXEL_FORMAT_ARGB_8888_PRE: |
||
982 | case COGL_PIXEL_FORMAT_RGBA_8888_PRE: |
||
983 | /* Note: this is ambiguous since CAIRO_FORMAT_RGB24 |
||
984 | * would also map to the same CoglPixelFormat */ |
||
985 | return CAIRO_FORMAT_ARGB32; |
||
986 | |||
987 | default: |
||
988 | g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d\n", |
||
989 | format, |
||
990 | format & COGL_A_BIT, |
||
991 | format & COGL_BGR_BIT, |
||
992 | format & COGL_PREMULT_BIT, |
||
993 | format & ~(COGL_A_BIT | COGL_BGR_BIT | COGL_PREMULT_BIT)); |
||
994 | return CAIRO_FORMAT_INVALID; |
||
995 | } |
||
996 | } |
||
997 | |||
998 | static CoglPixelFormat |
||
999 | get_cogl_format_from_cairo_format (cairo_format_t cairo_format) |
||
1000 | { |
||
1001 | switch (cairo_format) |
||
1002 | { |
||
1003 | case CAIRO_FORMAT_ARGB32: |
||
1004 | case CAIRO_FORMAT_RGB24: |
||
1005 | #if G_BYTE_ORDER == G_LITTLE_ENDIAN |
||
1006 | return COGL_PIXEL_FORMAT_BGRA_8888_PRE; |
||
1007 | #else |
||
1008 | return COGL_PIXEL_FORMAT_ARGB_8888_PRE; |
||
1009 | #endif |
||
1010 | case CAIRO_FORMAT_A8: |
||
1011 | return COGL_PIXEL_FORMAT_A_8; |
||
1012 | case CAIRO_FORMAT_RGB16_565: |
||
1013 | return COGL_PIXEL_FORMAT_RGB_565; |
||
1014 | case CAIRO_FORMAT_INVALID: |
||
1015 | case CAIRO_FORMAT_A1: |
||
1016 | case CAIRO_FORMAT_RGB30: |
||
1017 | return 0; |
||
1018 | } |
||
1019 | |||
1020 | g_warn_if_reached (); |
||
1021 | return 0; |
||
1022 | } |
||
1023 | |||
1024 | static cairo_status_t |
||
1025 | _cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t *surface, |
||
1026 | cairo_rectangle_int_t *interest, |
||
1027 | cairo_image_surface_t **image_out) |
||
1028 | { |
||
1029 | cairo_image_surface_t *image; |
||
1030 | cairo_status_t status; |
||
1031 | cairo_format_t cairo_format; |
||
1032 | CoglPixelFormat cogl_format; |
||
1033 | |||
1034 | /* TODO: Add cogl_texture_get_region() API so we don't have to ensure the |
||
1035 | * surface is bound to an fbo to read back pixels */ |
||
1036 | status = _cairo_cogl_surface_ensure_framebuffer (surface); |
||
1037 | if (unlikely (status)) |
||
1038 | return status; |
||
1039 | |||
1040 | cairo_format = get_cairo_format_from_cogl_format (surface->cogl_format); |
||
1041 | if (cairo_format == CAIRO_FORMAT_INVALID) { |
||
1042 | cairo_format = CAIRO_FORMAT_ARGB32; |
||
1043 | cogl_format = get_cogl_format_from_cairo_format (cairo_format); |
||
1044 | } else { |
||
1045 | cogl_format = cogl_framebuffer_get_color_format (surface->framebuffer); |
||
1046 | } |
||
1047 | |||
1048 | image = (cairo_image_surface_t *) |
||
1049 | cairo_image_surface_create (cairo_format, surface->width, surface->height); |
||
1050 | if (image->base.status) |
||
1051 | return image->base.status; |
||
1052 | |||
1053 | /* TODO: Add cogl_framebuffer_read_pixels() API */ |
||
1054 | cogl_push_framebuffer (surface->framebuffer); |
||
1055 | cogl_read_pixels (0, 0, surface->width, surface->height, |
||
1056 | COGL_READ_PIXELS_COLOR_BUFFER, |
||
1057 | cogl_format, |
||
1058 | image->data); |
||
1059 | cogl_pop_framebuffer (); |
||
1060 | |||
1061 | *image_out = image; |
||
1062 | |||
1063 | return CAIRO_STATUS_SUCCESS; |
||
1064 | } |
||
1065 | |||
1066 | static cairo_status_t |
||
1067 | _cairo_cogl_surface_acquire_source_image (void *abstract_surface, |
||
1068 | cairo_image_surface_t **image_out, |
||
1069 | void **image_extra) |
||
1070 | { |
||
1071 | cairo_cogl_surface_t *surface = abstract_surface; |
||
1072 | cairo_status_t status; |
||
1073 | |||
1074 | if (surface->texture) { |
||
1075 | cairo_format_t format = get_cairo_format_from_cogl_format (surface->cogl_format); |
||
1076 | cairo_image_surface_t *image = (cairo_image_surface_t *) |
||
1077 | cairo_image_surface_create (format, surface->width, surface->height); |
||
1078 | if (image->base.status) |
||
1079 | return image->base.status; |
||
1080 | |||
1081 | cogl_texture_get_data (surface->texture, |
||
1082 | cogl_texture_get_format (surface->texture), |
||
1083 | 0, |
||
1084 | image->data); |
||
1085 | |||
1086 | image->base.is_clear = FALSE; |
||
1087 | *image_out = image; |
||
1088 | } else { |
||
1089 | cairo_rectangle_int_t extents = { |
||
1090 | 0, 0, surface->width, surface->height |
||
1091 | }; |
||
1092 | status = _cairo_cogl_surface_read_rect_to_image_surface (surface, &extents, |
||
1093 | image_out); |
||
1094 | if (unlikely (status)) |
||
1095 | return status; |
||
1096 | } |
||
1097 | |||
1098 | *image_extra = NULL; |
||
1099 | |||
1100 | return CAIRO_STATUS_SUCCESS; |
||
1101 | } |
||
1102 | |||
1103 | static void |
||
1104 | _cairo_cogl_surface_release_source_image (void *abstract_surface, |
||
1105 | cairo_image_surface_t *image, |
||
1106 | void *image_extra) |
||
1107 | { |
||
1108 | cairo_surface_destroy (&image->base); |
||
1109 | } |
||
1110 | |||
1111 | static cairo_status_t |
||
1112 | _cairo_cogl_surface_clear (cairo_cogl_surface_t *surface, |
||
1113 | const cairo_color_t *color) |
||
1114 | { |
||
1115 | /* Anything batched in the journal up until now is redundant... */ |
||
1116 | _cairo_cogl_journal_discard (surface); |
||
1117 | |||
1118 | /* XXX: we currently implicitly clear the depth and stencil buffer here |
||
1119 | * but since we use the framebuffer_discard extension when available I |
||
1120 | * suppose this doesn't matter too much. |
||
1121 | * |
||
1122 | * The main concern is that we want to avoid re-loading an external z |
||
1123 | * buffer at the start of each frame, but also many gpu architectures have |
||
1124 | * optimizations for how they handle the depth/stencil buffers and can get |
||
1125 | * upset if they aren't cleared together at the start of the frame. |
||
1126 | * |
||
1127 | * FIXME: we need a way to assert that the clip stack currently isn't |
||
1128 | * using the stencil buffer before clearing it here! |
||
1129 | */ |
||
1130 | cogl_framebuffer_clear4f (surface->framebuffer, |
||
1131 | COGL_BUFFER_BIT_COLOR | |
||
1132 | COGL_BUFFER_BIT_DEPTH | |
||
1133 | COGL_BUFFER_BIT_STENCIL, |
||
1134 | color->red * color->alpha, |
||
1135 | color->green * color->alpha, |
||
1136 | color->blue * color->alpha, |
||
1137 | color->alpha); |
||
1138 | return CAIRO_STATUS_SUCCESS; |
||
1139 | } |
||
1140 | |||
1141 | cairo_status_t |
||
1142 | _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path, |
||
1143 | cairo_fixed_t x, |
||
1144 | cairo_fixed_t y, |
||
1145 | cairo_fixed_t width, |
||
1146 | cairo_fixed_t height) |
||
1147 | { |
||
1148 | cairo_status_t status; |
||
1149 | |||
1150 | status = _cairo_path_fixed_move_to (path, x, y); |
||
1151 | if (unlikely (status)) |
||
1152 | return status; |
||
1153 | |||
1154 | status = _cairo_path_fixed_rel_line_to (path, width, 0); |
||
1155 | if (unlikely (status)) |
||
1156 | return status; |
||
1157 | |||
1158 | status = _cairo_path_fixed_rel_line_to (path, 0, height); |
||
1159 | if (unlikely (status)) |
||
1160 | return status; |
||
1161 | |||
1162 | status = _cairo_path_fixed_rel_line_to (path, -width, 0); |
||
1163 | if (unlikely (status)) |
||
1164 | return status; |
||
1165 | |||
1166 | status = _cairo_path_fixed_close_path (path); |
||
1167 | if (unlikely (status)) |
||
1168 | return status; |
||
1169 | |||
1170 | return CAIRO_STATUS_SUCCESS; |
||
1171 | } |
||
1172 | |||
1173 | static cairo_int_status_t |
||
1174 | _cairo_cogl_surface_paint (void *abstract_surface, |
||
1175 | cairo_operator_t op, |
||
1176 | const cairo_pattern_t *source, |
||
1177 | const cairo_clip_t *clip) |
||
1178 | { |
||
1179 | cairo_cogl_surface_t *surface; |
||
1180 | cairo_path_fixed_t path; |
||
1181 | cairo_status_t status; |
||
1182 | cairo_matrix_t identity; |
||
1183 | |||
1184 | if (clip == NULL) { |
||
1185 | if (op == CAIRO_OPERATOR_CLEAR) |
||
1186 | return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT); |
||
1187 | else if (source->type == CAIRO_PATTERN_TYPE_SOLID && |
||
1188 | (op == CAIRO_OPERATOR_SOURCE || |
||
1189 | (op == CAIRO_OPERATOR_OVER && (((cairo_surface_t *)abstract_surface)->is_clear || _cairo_pattern_is_opaque_solid (source))))) { |
||
1190 | return _cairo_cogl_surface_clear (abstract_surface, |
||
1191 | &((cairo_solid_pattern_t *) source)->color); |
||
1192 | } |
||
1193 | } |
||
1194 | |||
1195 | /* fallback to handling the paint in terms of a fill... */ |
||
1196 | |||
1197 | surface = abstract_surface; |
||
1198 | |||
1199 | _cairo_path_fixed_init (&path); |
||
1200 | |||
1201 | status = _cairo_cogl_path_fixed_rectangle (&path, 0, 0, surface->width, surface->height); |
||
1202 | if (unlikely (status)) |
||
1203 | goto BAIL; |
||
1204 | |||
1205 | #ifdef NEED_COGL_CONTEXT |
||
1206 | /* XXX: in cairo-cogl-context.c we set some sideband data on the |
||
1207 | * surface before issuing a fill so we need to do that here too... */ |
||
1208 | surface->user_path = &path; |
||
1209 | cairo_matrix_init_identity (&identity); |
||
1210 | surface->ctm = &identity; |
||
1211 | surface->ctm_inverse = &identity; |
||
1212 | surface->path_is_rectangle = TRUE; |
||
1213 | surface->path_rectangle_x = 0; |
||
1214 | surface->path_rectangle_y = 0; |
||
1215 | surface->path_rectangle_width = surface->width; |
||
1216 | surface->path_rectangle_height = surface->height; |
||
1217 | #endif |
||
1218 | |||
1219 | status = _cairo_cogl_surface_fill (abstract_surface, |
||
1220 | op, |
||
1221 | source, |
||
1222 | &path, |
||
1223 | CAIRO_FILL_RULE_WINDING, |
||
1224 | 1, |
||
1225 | CAIRO_ANTIALIAS_DEFAULT, |
||
1226 | clip); |
||
1227 | BAIL: |
||
1228 | _cairo_path_fixed_fini (&path); |
||
1229 | return status; |
||
1230 | } |
||
1231 | |||
1232 | static CoglPipelineWrapMode |
||
1233 | get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode) |
||
1234 | { |
||
1235 | switch (extend_mode) |
||
1236 | { |
||
1237 | case CAIRO_EXTEND_NONE: |
||
1238 | return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; |
||
1239 | case CAIRO_EXTEND_PAD: |
||
1240 | return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; |
||
1241 | case CAIRO_EXTEND_REPEAT: |
||
1242 | return COGL_PIPELINE_WRAP_MODE_REPEAT; |
||
1243 | case CAIRO_EXTEND_REFLECT: |
||
1244 | /* TODO: return COGL_PIPELINE_WRAP_MODE_MIRROR; */ |
||
1245 | return CAIRO_EXTEND_REPEAT; |
||
1246 | } |
||
1247 | assert (0); /* not reached */ |
||
1248 | return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; |
||
1249 | } |
||
1250 | |||
1251 | #if 0 |
||
1252 | /* Given an arbitrary texture, check if it's already a pot texture and simply |
||
1253 | * return it back if so. If not create a new pot texture, scale the old to |
||
1254 | * fill it, unref the old and return a pointer to the new pot texture. */ |
||
1255 | static cairo_int_status_t |
||
1256 | _cairo_cogl_get_pot_texture (CoglContext *context, |
||
1257 | CoglTexture *texture, |
||
1258 | CoglTexture **pot_texture) |
||
1259 | { |
||
1260 | int width = cogl_texture_get_width (texture); |
||
1261 | int height = cogl_texture_get_height (texture); |
||
1262 | int pot_width; |
||
1263 | int pot_height; |
||
1264 | CoglHandle offscreen = NULL; |
||
1265 | CoglTexture2D *pot = NULL; |
||
1266 | GError *error; |
||
1267 | |||
1268 | pot_width = _cairo_cogl_util_next_p2 (width); |
||
1269 | pot_height = _cairo_cogl_util_next_p2 (height); |
||
1270 | |||
1271 | if (pot_width == width && pot_height == height) |
||
1272 | return CAIRO_INT_STATUS_SUCCESS; |
||
1273 | |||
1274 | for (;;) { |
||
1275 | error = NULL; |
||
1276 | pot = cogl_texture_2d_new_with_size (context, |
||
1277 | pot_width, |
||
1278 | pot_height, |
||
1279 | cogl_texture_get_format (texture), |
||
1280 | &error); |
||
1281 | if (pot) |
||
1282 | break; |
||
1283 | else |
||
1284 | g_error_free (error); |
||
1285 | |||
1286 | if (pot_width > pot_height) |
||
1287 | pot_width >>= 1; |
||
1288 | else |
||
1289 | pot_height >>= 1; |
||
1290 | |||
1291 | if (!pot_width || !pot_height) |
||
1292 | break; |
||
1293 | } |
||
1294 | |||
1295 | *pot_texture = COGL_TEXTURE (pot); |
||
1296 | |||
1297 | if (!pot) |
||
1298 | return CAIRO_INT_STATUS_NO_MEMORY; |
||
1299 | |||
1300 | /* Use the GPU to do a bilinear filtered scale from npot to pot... */ |
||
1301 | offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (pot)); |
||
1302 | error = NULL; |
||
1303 | if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (offscreen), &error)) { |
||
1304 | /* NB: if we don't pass an error then Cogl is allowed to simply abort |
||
1305 | * automatically. */ |
||
1306 | g_error_free (error); |
||
1307 | cogl_object_unref (pot); |
||
1308 | *pot_texture = NULL; |
||
1309 | return CAIRO_INT_STATUS_NO_MEMORY; |
||
1310 | } |
||
1311 | |||
1312 | cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen)); |
||
1313 | cogl_set_source_texture (texture); |
||
1314 | cogl_rectangle (-1, 1, 1, -1); |
||
1315 | cogl_pop_framebuffer (); |
||
1316 | |||
1317 | cogl_object_unref (offscreen); |
||
1318 | } |
||
1319 | #endif |
||
1320 | |||
1321 | /* NB: a reference for the texture is transferred to the caller which should |
||
1322 | * be unrefed */ |
||
1323 | static CoglTexture * |
||
1324 | _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t *reference_surface, |
||
1325 | cairo_surface_t *abstract_surface) |
||
1326 | { |
||
1327 | cairo_image_surface_t *image; |
||
1328 | cairo_image_surface_t *acquired_image = NULL; |
||
1329 | void *image_extra; |
||
1330 | CoglPixelFormat format; |
||
1331 | cairo_image_surface_t *image_clone = NULL; |
||
1332 | CoglTexture2D *texture; |
||
1333 | GError *error = NULL; |
||
1334 | cairo_surface_t *clone; |
||
1335 | |||
1336 | if (abstract_surface->device == reference_surface->base.device) { |
||
1337 | cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; |
||
1338 | _cairo_cogl_surface_flush (surface, 0); |
||
1339 | return surface->texture ? cogl_object_ref (surface->texture) : NULL; |
||
1340 | } |
||
1341 | |||
1342 | if (abstract_surface->type == CAIRO_SURFACE_TYPE_COGL) { |
||
1343 | if (_cairo_surface_is_subsurface (abstract_surface)) { |
||
1344 | cairo_cogl_surface_t *surface; |
||
1345 | |||
1346 | surface = (cairo_cogl_surface_t *) |
||
1347 | _cairo_surface_subsurface_get_target (abstract_surface); |
||
1348 | if (surface->base.device == reference_surface->base.device) |
||
1349 | return surface->texture ? cogl_object_ref (surface->texture) : NULL; |
||
1350 | } |
||
1351 | } |
||
1352 | |||
1353 | clone = _cairo_surface_has_snapshot (abstract_surface, &_cairo_cogl_surface_backend); |
||
1354 | if (clone) { |
||
1355 | cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)clone; |
||
1356 | return surface->texture ? cogl_object_ref (surface->texture) : NULL; |
||
1357 | } |
||
1358 | |||
1359 | g_warning ("Uploading image surface to texture"); |
||
1360 | |||
1361 | if (_cairo_surface_is_image (abstract_surface)) { |
||
1362 | image = (cairo_image_surface_t *)abstract_surface; |
||
1363 | } else { |
||
1364 | cairo_status_t status = _cairo_surface_acquire_source_image (abstract_surface, |
||
1365 | &acquired_image, &image_extra); |
||
1366 | if (unlikely (status)) { |
||
1367 | g_warning ("acquire_source_image failed: %s [%d]\n", |
||
1368 | cairo_status_to_string (status), status); |
||
1369 | return NULL; |
||
1370 | } |
||
1371 | image = acquired_image; |
||
1372 | } |
||
1373 | |||
1374 | format = get_cogl_format_from_cairo_format (image->format); |
||
1375 | if (!format) |
||
1376 | { |
||
1377 | image_clone = _cairo_image_surface_coerce (image); |
||
1378 | if (unlikely (image_clone->base.status)) { |
||
1379 | g_warning ("image_surface_coerce failed"); |
||
1380 | texture = NULL; |
||
1381 | goto BAIL; |
||
1382 | } |
||
1383 | |||
1384 | format = get_cogl_format_from_cairo_format (image_clone->format); |
||
1385 | assert (format); |
||
1386 | } |
||
1387 | |||
1388 | texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context, |
||
1389 | image->width, |
||
1390 | image->height, |
||
1391 | format, /* incoming */ |
||
1392 | format, /* desired */ |
||
1393 | image->stride, |
||
1394 | image->data, |
||
1395 | &error); |
||
1396 | if (!texture) { |
||
1397 | g_warning ("Failed to allocate texture: %s", error->message); |
||
1398 | g_error_free (error); |
||
1399 | goto BAIL; |
||
1400 | } |
||
1401 | |||
1402 | clone = _cairo_cogl_surface_create_full (to_device(reference_surface->base.device), |
||
1403 | reference_surface->ignore_alpha, |
||
1404 | NULL, COGL_TEXTURE (texture)); |
||
1405 | |||
1406 | _cairo_surface_attach_snapshot (abstract_surface, clone, NULL); |
||
1407 | |||
1408 | /* Attaching the snapshot will take a reference on the clone surface... */ |
||
1409 | cairo_surface_destroy (clone); |
||
1410 | |||
1411 | BAIL: |
||
1412 | if (image_clone) |
||
1413 | cairo_surface_destroy (&image_clone->base); |
||
1414 | if (acquired_image) |
||
1415 | _cairo_surface_release_source_image (abstract_surface, acquired_image, image_extra); |
||
1416 | |||
1417 | return COGL_TEXTURE (texture); |
||
1418 | } |
||
1419 | |||
1420 | /* NB: a reference for the texture is transferred to the caller which should |
||
1421 | * be unrefed */ |
||
1422 | static CoglTexture * |
||
1423 | _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern, |
||
1424 | cairo_cogl_surface_t *destination, |
||
1425 | const cairo_rectangle_int_t *extents, |
||
1426 | const cairo_rectangle_int_t *sample, |
||
1427 | cairo_cogl_texture_attributes_t *attributes) |
||
1428 | { |
||
1429 | CoglTexture *texture = NULL; |
||
1430 | |||
1431 | switch ((int)pattern->type) |
||
1432 | { |
||
1433 | case CAIRO_PATTERN_TYPE_SURFACE: { |
||
1434 | cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface; |
||
1435 | texture = _cairo_cogl_acquire_surface_texture (destination, surface); |
||
1436 | if (!texture) |
||
1437 | return NULL; |
||
1438 | |||
1439 | /* XXX: determine if it would have no effect to change the |
||
1440 | * extend mode to EXTEND_PAD instead since we can simply map |
||
1441 | * EXTEND_PAD to CLAMP_TO_EDGE without needing fragment shader |
||
1442 | * tricks or extra border texels. */ |
||
1443 | #if 0 |
||
1444 | /* TODO: We still need to consider HW such as SGX which doesn't have |
||
1445 | * full support for NPOT textures. */ |
||
1446 | if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_REFLECT) { |
||
1447 | _cairo_cogl_get_pot_texture (); |
||
1448 | } |
||
1449 | #endif |
||
1450 | |||
1451 | cairo_matrix_init_identity (&attributes->matrix); |
||
1452 | |||
1453 | /* Convert from un-normalized source coordinates in backend |
||
1454 | * coordinates to normalized texture coordinates */ |
||
1455 | cairo_matrix_scale (&attributes->matrix, |
||
1456 | 1.0f / cogl_texture_get_width (texture), |
||
1457 | 1.0f / cogl_texture_get_height (texture)); |
||
1458 | |||
1459 | /* XXX: need to multiply in the pattern->matrix */ |
||
1460 | |||
1461 | attributes->extend = pattern->extend; |
||
1462 | attributes->filter = CAIRO_FILTER_BILINEAR; |
||
1463 | attributes->has_component_alpha = pattern->has_component_alpha; |
||
1464 | |||
1465 | attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend); |
||
1466 | attributes->t_wrap = attributes->s_wrap; |
||
1467 | |||
1468 | return texture; |
||
1469 | } |
||
1470 | case CAIRO_PATTERN_TYPE_RADIAL: |
||
1471 | case CAIRO_PATTERN_TYPE_MESH: { |
||
1472 | cairo_surface_t *surface; |
||
1473 | cairo_matrix_t texture_matrix; |
||
1474 | |||
1475 | surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, |
||
1476 | extents->width, extents->height); |
||
1477 | if (_cairo_surface_offset_paint (surface, |
||
1478 | extents->x, extents->y, |
||
1479 | CAIRO_OPERATOR_SOURCE, |
||
1480 | pattern, NULL)) { |
||
1481 | cairo_surface_destroy (surface); |
||
1482 | return NULL; |
||
1483 | } |
||
1484 | |||
1485 | texture = _cairo_cogl_acquire_surface_texture (destination, surface); |
||
1486 | if (!texture) |
||
1487 | goto BAIL; |
||
1488 | |||
1489 | cairo_matrix_init_identity (&texture_matrix); |
||
1490 | |||
1491 | /* Convert from un-normalized source coordinates in backend |
||
1492 | * coordinates to normalized texture coordinates */ |
||
1493 | cairo_matrix_scale (&texture_matrix, |
||
1494 | 1.0f / cogl_texture_get_width (texture), |
||
1495 | 1.0f / cogl_texture_get_height (texture)); |
||
1496 | |||
1497 | cairo_matrix_translate (&texture_matrix, -extents->x, -extents->y); |
||
1498 | |||
1499 | attributes->matrix = texture_matrix; |
||
1500 | attributes->extend = pattern->extend; |
||
1501 | attributes->filter = CAIRO_FILTER_NEAREST; |
||
1502 | attributes->has_component_alpha = pattern->has_component_alpha; |
||
1503 | |||
1504 | /* any pattern extend modes have already been dealt with... */ |
||
1505 | attributes->s_wrap = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; |
||
1506 | attributes->t_wrap = attributes->s_wrap; |
||
1507 | |||
1508 | BAIL: |
||
1509 | cairo_surface_destroy (surface); |
||
1510 | |||
1511 | return texture; |
||
1512 | } |
||
1513 | case CAIRO_PATTERN_TYPE_LINEAR: { |
||
1514 | cairo_linear_pattern_t *linear_pattern = (cairo_linear_pattern_t *)pattern; |
||
1515 | cairo_cogl_linear_gradient_t *gradient; |
||
1516 | cairo_cogl_linear_texture_entry_t *linear_texture; |
||
1517 | cairo_int_status_t status; |
||
1518 | float a, b; |
||
1519 | float dist; |
||
1520 | float scale; |
||
1521 | float angle; |
||
1522 | |||
1523 | status = _cairo_cogl_get_linear_gradient (to_device(destination->base.device), |
||
1524 | pattern->extend, |
||
1525 | linear_pattern->base.n_stops, |
||
1526 | linear_pattern->base.stops, |
||
1527 | &gradient); |
||
1528 | if (unlikely (status)) |
||
1529 | return NULL; |
||
1530 | |||
1531 | linear_texture = _cairo_cogl_linear_gradient_texture_for_extend (gradient, pattern->extend); |
||
1532 | |||
1533 | attributes->extend = pattern->extend; |
||
1534 | attributes->filter = CAIRO_FILTER_BILINEAR; |
||
1535 | attributes->has_component_alpha = pattern->has_component_alpha; |
||
1536 | attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend); |
||
1537 | attributes->t_wrap = COGL_PIPELINE_WRAP_MODE_REPEAT; |
||
1538 | |||
1539 | cairo_matrix_init_identity (&attributes->matrix); |
||
1540 | |||
1541 | a = linear_pattern->pd2.x - linear_pattern->pd1.x; |
||
1542 | b = linear_pattern->pd2.y - linear_pattern->pd1.y; |
||
1543 | dist = sqrtf (a*a + b*b); |
||
1544 | scale = 1.0f / dist; |
||
1545 | angle = - atan2f (b, a); |
||
1546 | |||
1547 | cairo_matrix_rotate (&attributes->matrix, angle); |
||
1548 | cairo_matrix_scale (&attributes->matrix, scale, scale); |
||
1549 | |||
1550 | cairo_matrix_translate (&attributes->matrix, |
||
1551 | -linear_pattern->pd1.x, |
||
1552 | -linear_pattern->pd1.y); |
||
1553 | |||
1554 | /* XXX: this caught me out: cairo doesn't follow the standard |
||
1555 | * maths convention for multiplying two matrices A x B - cairo |
||
1556 | * does B x A so the final matrix is as if A's transforms were |
||
1557 | * applied first. |
||
1558 | */ |
||
1559 | cairo_matrix_multiply (&attributes->matrix, |
||
1560 | &pattern->matrix, |
||
1561 | &attributes->matrix); |
||
1562 | |||
1563 | return cogl_object_ref (linear_texture->texture); |
||
1564 | } |
||
1565 | default: |
||
1566 | g_warning ("Un-supported source type"); |
||
1567 | return NULL; |
||
1568 | } |
||
1569 | } |
||
1570 | |||
1571 | static void |
||
1572 | set_layer_texture_with_attributes (CoglPipeline *pipeline, |
||
1573 | int layer_index, |
||
1574 | CoglTexture *texture, |
||
1575 | cairo_cogl_texture_attributes_t *attributes) |
||
1576 | { |
||
1577 | cogl_pipeline_set_layer_texture (pipeline, layer_index, texture); |
||
1578 | |||
1579 | if (!_cairo_matrix_is_identity (&attributes->matrix)) { |
||
1580 | cairo_matrix_t *m = &attributes->matrix; |
||
1581 | float texture_matrixfv[16] = { |
||
1582 | m->xx, m->yx, 0, 0, |
||
1583 | m->xy, m->yy, 0, 0, |
||
1584 | 0, 0, 1, 0, |
||
1585 | m->x0, m->y0, 0, 1 |
||
1586 | }; |
||
1587 | CoglMatrix texture_matrix; |
||
1588 | cogl_matrix_init_from_array (&texture_matrix, texture_matrixfv); |
||
1589 | cogl_pipeline_set_layer_matrix (pipeline, layer_index, &texture_matrix); |
||
1590 | } |
||
1591 | |||
1592 | if (attributes->s_wrap != attributes->t_wrap) { |
||
1593 | cogl_pipeline_set_layer_wrap_mode_s (pipeline, layer_index, attributes->s_wrap); |
||
1594 | cogl_pipeline_set_layer_wrap_mode_t (pipeline, layer_index, attributes->t_wrap); |
||
1595 | } else |
||
1596 | cogl_pipeline_set_layer_wrap_mode (pipeline, layer_index, attributes->s_wrap); |
||
1597 | } |
||
1598 | |||
1599 | static CoglPipeline * |
||
1600 | get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask, |
||
1601 | const cairo_pattern_t *source, |
||
1602 | cairo_operator_t op, |
||
1603 | cairo_cogl_surface_t *destination, |
||
1604 | cairo_composite_rectangles_t *extents) |
||
1605 | { |
||
1606 | cairo_cogl_template_type template_type; |
||
1607 | CoglPipeline *pipeline; |
||
1608 | |||
1609 | switch ((int)source->type) |
||
1610 | { |
||
1611 | case CAIRO_PATTERN_TYPE_SOLID: |
||
1612 | template_type = mask ? |
||
1613 | CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID : CAIRO_COGL_TEMPLATE_TYPE_SOLID; |
||
1614 | break; |
||
1615 | case CAIRO_PATTERN_TYPE_SURFACE: |
||
1616 | case CAIRO_PATTERN_TYPE_LINEAR: |
||
1617 | case CAIRO_PATTERN_TYPE_RADIAL: |
||
1618 | case CAIRO_PATTERN_TYPE_MESH: |
||
1619 | template_type = mask ? |
||
1620 | CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE : CAIRO_COGL_TEMPLATE_TYPE_TEXTURE; |
||
1621 | break; |
||
1622 | default: |
||
1623 | g_warning ("Un-supported source type"); |
||
1624 | return NULL; |
||
1625 | } |
||
1626 | |||
1627 | pipeline = cogl_pipeline_copy (to_device(destination->base.device)->template_pipelines[op][template_type]); |
||
1628 | |||
1629 | if (source->type == CAIRO_PATTERN_TYPE_SOLID) { |
||
1630 | cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source; |
||
1631 | cogl_pipeline_set_color4f (pipeline, |
||
1632 | solid_pattern->color.red * solid_pattern->color.alpha, |
||
1633 | solid_pattern->color.green * solid_pattern->color.alpha, |
||
1634 | solid_pattern->color.blue * solid_pattern->color.alpha, |
||
1635 | solid_pattern->color.alpha); |
||
1636 | } else { |
||
1637 | cairo_cogl_texture_attributes_t attributes; |
||
1638 | CoglTexture *texture = |
||
1639 | _cairo_cogl_acquire_pattern_texture (source, destination, |
||
1640 | &extents->bounded, |
||
1641 | &extents->source_sample_area, |
||
1642 | &attributes); |
||
1643 | if (!texture) |
||
1644 | goto BAIL; |
||
1645 | set_layer_texture_with_attributes (pipeline, 0, texture, &attributes); |
||
1646 | cogl_object_unref (texture); |
||
1647 | } |
||
1648 | |||
1649 | if (mask) { |
||
1650 | if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { |
||
1651 | cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)mask; |
||
1652 | CoglColor color; |
||
1653 | cogl_color_init_from_4f (&color, |
||
1654 | solid_pattern->color.red * solid_pattern->color.alpha, |
||
1655 | solid_pattern->color.green * solid_pattern->color.alpha, |
||
1656 | solid_pattern->color.blue * solid_pattern->color.alpha, |
||
1657 | solid_pattern->color.alpha); |
||
1658 | cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color); |
||
1659 | } else { |
||
1660 | cairo_cogl_texture_attributes_t attributes; |
||
1661 | CoglTexture *texture = |
||
1662 | _cairo_cogl_acquire_pattern_texture (mask, destination, |
||
1663 | &extents->bounded, |
||
1664 | &extents->mask_sample_area, |
||
1665 | &attributes); |
||
1666 | if (!texture) |
||
1667 | goto BAIL; |
||
1668 | set_layer_texture_with_attributes (pipeline, 1, texture, &attributes); |
||
1669 | cogl_object_unref (texture); |
||
1670 | } |
||
1671 | } |
||
1672 | |||
1673 | return pipeline; |
||
1674 | |||
1675 | BAIL: |
||
1676 | cogl_object_unref (pipeline); |
||
1677 | return NULL; |
||
1678 | } |
||
1679 | |||
1680 | #if 0 |
||
1681 | CoglPrimitive * |
||
1682 | _cairo_cogl_rectangle_new_p2t2t2 (float x, |
||
1683 | float y, |
||
1684 | float width, |
||
1685 | float height) |
||
1686 | { |
||
1687 | CoglVertexP2 vertices[] = { |
||
1688 | {x, y}, {x, y + height}, {x + width, y + height}, |
||
1689 | {x, y}, {x + width, y + height}, {x + width, y} |
||
1690 | }; |
||
1691 | CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (sizeof (vertices)); |
||
1692 | CoglAttribute *pos = cogl_attribute_new (buffer, |
||
1693 | "cogl_position_in", |
||
1694 | sizeof (CoglVertexP2), |
||
1695 | 0, |
||
1696 | 2, |
||
1697 | COGL_ATTRIBUTE_TYPE_FLOAT); |
||
1698 | CoglAttribute *tex_coords0 = cogl_attribute_new (buffer, |
||
1699 | "cogl_tex_coord0_in", |
||
1700 | sizeof (CoglVertexP2), |
||
1701 | 0, |
||
1702 | 2, |
||
1703 | COGL_ATTRIBUTE_TYPE_FLOAT); |
||
1704 | CoglAttribute *tex_coords0 = cogl_attribute_new (buffer, |
||
1705 | "cogl_tex_coord0_in", |
||
1706 | sizeof (CoglVertexP2), |
||
1707 | 0, |
||
1708 | 2, |
||
1709 | COGL_ATTRIBUTE_TYPE_FLOAT); |
||
1710 | CoglPrimitive *prim; |
||
1711 | |||
1712 | cogl_buffer_set_data (COGL_BUFFER (buffer), 0, vertices, sizeof (vertices)); |
||
1713 | |||
1714 | /* The attributes will now keep the buffer alive... */ |
||
1715 | cogl_object_unref (buffer); |
||
1716 | |||
1717 | prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES, |
||
1718 | 6, pos, tex_coords, NULL); |
||
1719 | |||
1720 | /* The primitive will now keep the attribute alive... */ |
||
1721 | cogl_object_unref (pos); |
||
1722 | |||
1723 | return prim; |
||
1724 | } |
||
1725 | #endif |
||
1726 | |||
1727 | static void |
||
1728 | _cairo_cogl_log_clip (cairo_cogl_surface_t *surface, |
||
1729 | const cairo_clip_t *clip) |
||
1730 | { |
||
1731 | if (!_cairo_clip_equal (clip, surface->last_clip)) { |
||
1732 | _cairo_cogl_journal_log_clip (surface, clip); |
||
1733 | _cairo_clip_destroy (surface->last_clip); |
||
1734 | surface->last_clip = _cairo_clip_copy (clip); |
||
1735 | } |
||
1736 | } |
||
1737 | |||
1738 | static void |
||
1739 | _cairo_cogl_maybe_log_clip (cairo_cogl_surface_t *surface, |
||
1740 | cairo_composite_rectangles_t *composite) |
||
1741 | { |
||
1742 | cairo_clip_t *clip = composite->clip; |
||
1743 | |||
1744 | if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) |
||
1745 | clip = NULL; |
||
1746 | |||
1747 | if (clip == NULL) { |
||
1748 | if (_cairo_composite_rectangles_can_reduce_clip (composite, |
||
1749 | surface->last_clip)) |
||
1750 | return; |
||
1751 | } |
||
1752 | |||
1753 | _cairo_cogl_log_clip (surface, clip); |
||
1754 | } |
||
1755 | |||
1756 | static cairo_bool_t |
||
1757 | is_operator_supported (cairo_operator_t op) |
||
1758 | { |
||
1759 | switch ((int)op) { |
||
1760 | case CAIRO_OPERATOR_SOURCE: |
||
1761 | case CAIRO_OPERATOR_OVER: |
||
1762 | case CAIRO_OPERATOR_IN: |
||
1763 | case CAIRO_OPERATOR_DEST_OVER: |
||
1764 | case CAIRO_OPERATOR_DEST_IN: |
||
1765 | case CAIRO_OPERATOR_ADD: |
||
1766 | return TRUE; |
||
1767 | |||
1768 | default: |
||
1769 | return FALSE; |
||
1770 | } |
||
1771 | } |
||
1772 | |||
1773 | static cairo_int_status_t |
||
1774 | _cairo_cogl_surface_mask (void *abstract_surface, |
||
1775 | cairo_operator_t op, |
||
1776 | const cairo_pattern_t *source, |
||
1777 | const cairo_pattern_t *mask, |
||
1778 | const cairo_clip_t *clip) |
||
1779 | { |
||
1780 | cairo_cogl_surface_t *surface = abstract_surface; |
||
1781 | cairo_composite_rectangles_t extents; |
||
1782 | cairo_status_t status; |
||
1783 | CoglPipeline *pipeline; |
||
1784 | cairo_matrix_t identity; |
||
1785 | |||
1786 | /* XXX: Use this to smoke test the acquire_source/dest_image fallback |
||
1787 | * paths... */ |
||
1788 | //return CAIRO_INT_STATUS_UNSUPPORTED; |
||
1789 | |||
1790 | if (!is_operator_supported (op)) |
||
1791 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
1792 | |||
1793 | status = _cairo_composite_rectangles_init_for_mask (&extents, |
||
1794 | &surface->base, |
||
1795 | op, source, mask, clip); |
||
1796 | if (unlikely (status)) |
||
1797 | return status; |
||
1798 | |||
1799 | pipeline = get_source_mask_operator_destination_pipeline (mask, source, |
||
1800 | op, surface, &extents); |
||
1801 | if (!pipeline){ |
||
1802 | status = CAIRO_INT_STATUS_UNSUPPORTED; |
||
1803 | goto BAIL; |
||
1804 | } |
||
1805 | |||
1806 | _cairo_cogl_maybe_log_clip (surface, &extents); |
||
1807 | |||
1808 | cairo_matrix_init_identity (&identity); |
||
1809 | _cairo_cogl_journal_log_rectangle (surface, pipeline, |
||
1810 | extents.bounded.x, |
||
1811 | extents.bounded.y, |
||
1812 | extents.bounded.width, |
||
1813 | extents.bounded.height, |
||
1814 | 2, |
||
1815 | &identity); |
||
1816 | |||
1817 | /* The journal will take a reference on the pipeline and clip_path... */ |
||
1818 | cogl_object_unref (pipeline); |
||
1819 | |||
1820 | BAIL: |
||
1821 | return status; |
||
1822 | } |
||
1823 | |||
1824 | static int |
||
1825 | _cairo_cogl_source_n_layers (const cairo_pattern_t *source) |
||
1826 | { |
||
1827 | switch ((int)source->type) |
||
1828 | { |
||
1829 | case CAIRO_PATTERN_TYPE_SOLID: |
||
1830 | return 0; |
||
1831 | case CAIRO_PATTERN_TYPE_LINEAR: |
||
1832 | case CAIRO_PATTERN_TYPE_RADIAL: |
||
1833 | case CAIRO_PATTERN_TYPE_MESH: |
||
1834 | case CAIRO_PATTERN_TYPE_SURFACE: |
||
1835 | return 1; |
||
1836 | default: |
||
1837 | g_warning ("Unsupported source type"); |
||
1838 | return 0; |
||
1839 | } |
||
1840 | } |
||
1841 | |||
1842 | static cairo_bool_t |
||
1843 | _cairo_cogl_path_fill_meta_equal (const void *key_a, const void *key_b) |
||
1844 | { |
||
1845 | const cairo_cogl_path_fill_meta_t *meta0 = key_a; |
||
1846 | const cairo_cogl_path_fill_meta_t *meta1 = key_b; |
||
1847 | |||
1848 | return _cairo_path_fixed_equal (meta0->user_path, meta1->user_path); |
||
1849 | } |
||
1850 | |||
1851 | static cairo_bool_t |
||
1852 | _cairo_cogl_stroke_style_equal (const cairo_stroke_style_t *a, |
||
1853 | const cairo_stroke_style_t *b) |
||
1854 | { |
||
1855 | if (a->line_width == b->line_width && |
||
1856 | a->line_cap == b->line_cap && |
||
1857 | a->line_join == b->line_join && |
||
1858 | a->miter_limit == b->miter_limit && |
||
1859 | a->num_dashes == b->num_dashes && |
||
1860 | a->dash_offset == b->dash_offset) |
||
1861 | { |
||
1862 | unsigned int i; |
||
1863 | for (i = 0; i < a->num_dashes; i++) { |
||
1864 | if (a->dash[i] != b->dash[i]) |
||
1865 | return FALSE; |
||
1866 | } |
||
1867 | } |
||
1868 | return TRUE; |
||
1869 | } |
||
1870 | |||
1871 | static cairo_bool_t |
||
1872 | _cairo_cogl_path_stroke_meta_equal (const void *key_a, const void *key_b) |
||
1873 | { |
||
1874 | const cairo_cogl_path_stroke_meta_t *meta0 = key_a; |
||
1875 | const cairo_cogl_path_stroke_meta_t *meta1 = key_b; |
||
1876 | |||
1877 | return _cairo_cogl_stroke_style_equal (&meta0->style, &meta1->style) && |
||
1878 | _cairo_path_fixed_equal (meta0->user_path, meta1->user_path); |
||
1879 | } |
||
1880 | |||
1881 | static cairo_cogl_path_stroke_meta_t * |
||
1882 | _cairo_cogl_path_stroke_meta_reference (cairo_cogl_path_stroke_meta_t *meta) |
||
1883 | { |
||
1884 | assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count)); |
||
1885 | |||
1886 | _cairo_reference_count_inc (&meta->ref_count); |
||
1887 | |||
1888 | return meta; |
||
1889 | } |
||
1890 | |||
1891 | static void |
||
1892 | _cairo_cogl_path_stroke_meta_destroy (cairo_cogl_path_stroke_meta_t *meta) |
||
1893 | { |
||
1894 | assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count)); |
||
1895 | |||
1896 | if (! _cairo_reference_count_dec_and_test (&meta->ref_count)) |
||
1897 | return; |
||
1898 | |||
1899 | _cairo_path_fixed_fini (meta->user_path); |
||
1900 | free (meta->user_path); |
||
1901 | |||
1902 | _cairo_stroke_style_fini (&meta->style); |
||
1903 | |||
1904 | if (meta->prim) |
||
1905 | cogl_object_unref (meta->prim); |
||
1906 | |||
1907 | free (meta); |
||
1908 | } |
||
1909 | |||
1910 | static cairo_cogl_path_stroke_meta_t * |
||
1911 | _cairo_cogl_path_stroke_meta_lookup (cairo_cogl_device_t *ctx, |
||
1912 | unsigned long hash, |
||
1913 | cairo_path_fixed_t *user_path, |
||
1914 | const cairo_stroke_style_t *style, |
||
1915 | double tolerance) |
||
1916 | { |
||
1917 | cairo_cogl_path_stroke_meta_t *ret; |
||
1918 | cairo_cogl_path_stroke_meta_t lookup; |
||
1919 | |||
1920 | lookup.cache_entry.hash = hash; |
||
1921 | lookup.user_path = user_path; |
||
1922 | lookup.style = *style; |
||
1923 | lookup.tolerance = tolerance; |
||
1924 | |||
1925 | ret = _cairo_cache_lookup (&ctx->path_stroke_staging_cache, &lookup.cache_entry); |
||
1926 | if (!ret) |
||
1927 | ret = _cairo_cache_lookup (&ctx->path_stroke_prim_cache, &lookup.cache_entry); |
||
1928 | return ret; |
||
1929 | } |
||
1930 | |||
1931 | static void |
||
1932 | _cairo_cogl_path_stroke_meta_set_prim_size (cairo_cogl_surface_t *surface, |
||
1933 | cairo_cogl_path_stroke_meta_t *meta, |
||
1934 | size_t size) |
||
1935 | { |
||
1936 | /* now that we know the meta structure is associated with a primitive |
||
1937 | * we promote it from the staging cache into the primitive cache. |
||
1938 | */ |
||
1939 | |||
1940 | /* XXX: _cairo_cache borks if you try and remove an entry that's already |
||
1941 | * been evicted so we explicitly look it up first... */ |
||
1942 | if (_cairo_cache_lookup (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry)) { |
||
1943 | _cairo_cogl_path_stroke_meta_reference (meta); |
||
1944 | _cairo_cache_remove (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry); |
||
1945 | } |
||
1946 | |||
1947 | meta->cache_entry.size = size; |
||
1948 | if (_cairo_cache_insert (&to_device(surface->base.device)->path_stroke_prim_cache, &meta->cache_entry) != |
||
1949 | CAIRO_STATUS_SUCCESS) |
||
1950 | _cairo_cogl_path_stroke_meta_destroy (meta); |
||
1951 | } |
||
1952 | |||
1953 | static unsigned int |
||
1954 | _cairo_cogl_stroke_style_hash (unsigned int hash, |
||
1955 | const cairo_stroke_style_t *style) |
||
1956 | { |
||
1957 | unsigned int i; |
||
1958 | hash = _cairo_hash_bytes (hash, &style->line_width, sizeof (style->line_width)); |
||
1959 | hash = _cairo_hash_bytes (hash, &style->line_cap, sizeof (style->line_cap)); |
||
1960 | hash = _cairo_hash_bytes (hash, &style->line_join, sizeof (style->line_join)); |
||
1961 | hash = _cairo_hash_bytes (hash, &style->miter_limit, sizeof (style->miter_limit)); |
||
1962 | hash = _cairo_hash_bytes (hash, &style->num_dashes, sizeof (style->num_dashes)); |
||
1963 | hash = _cairo_hash_bytes (hash, &style->dash_offset, sizeof (style->dash_offset)); |
||
1964 | for (i = 0; i < style->num_dashes; i++) |
||
1965 | hash = _cairo_hash_bytes (hash, &style->dash[i], sizeof (double)); |
||
1966 | return hash; |
||
1967 | } |
||
1968 | |||
1969 | static cairo_cogl_path_stroke_meta_t * |
||
1970 | _cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t *surface, |
||
1971 | const cairo_stroke_style_t *style, |
||
1972 | double tolerance) |
||
1973 | { |
||
1974 | unsigned long hash; |
||
1975 | cairo_cogl_path_stroke_meta_t *meta = NULL; |
||
1976 | cairo_path_fixed_t *meta_path = NULL; |
||
1977 | cairo_status_t status; |
||
1978 | |||
1979 | if (!surface->user_path) |
||
1980 | return NULL; |
||
1981 | |||
1982 | hash = _cairo_path_fixed_hash (surface->user_path); |
||
1983 | hash = _cairo_cogl_stroke_style_hash (hash, style); |
||
1984 | hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance)); |
||
1985 | |||
1986 | meta = _cairo_cogl_path_stroke_meta_lookup (to_device(surface->base.device), hash, |
||
1987 | surface->user_path, style, tolerance); |
||
1988 | if (meta) |
||
1989 | return meta; |
||
1990 | |||
1991 | meta = calloc (1, sizeof (cairo_cogl_path_stroke_meta_t)); |
||
1992 | if (!meta) |
||
1993 | goto BAIL; |
||
1994 | CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1); |
||
1995 | meta->cache_entry.hash = hash; |
||
1996 | meta->counter = 0; |
||
1997 | meta_path = malloc (sizeof (cairo_path_fixed_t)); |
||
1998 | if (!meta_path) |
||
1999 | goto BAIL; |
||
2000 | /* FIXME: we should add a ref-counted wrapper for our user_paths |
||
2001 | * so we don't have to keep copying them here! */ |
||
2002 | status = _cairo_path_fixed_init_copy (meta_path, surface->user_path); |
||
2003 | if (unlikely (status)) |
||
2004 | goto BAIL; |
||
2005 | meta->user_path = meta_path; |
||
2006 | meta->ctm_inverse = *surface->ctm_inverse; |
||
2007 | |||
2008 | status = _cairo_stroke_style_init_copy (&meta->style, style); |
||
2009 | if (unlikely (status)) { |
||
2010 | _cairo_path_fixed_fini (meta_path); |
||
2011 | goto BAIL; |
||
2012 | } |
||
2013 | meta->tolerance = tolerance; |
||
2014 | |||
2015 | return meta; |
||
2016 | |||
2017 | BAIL: |
||
2018 | free (meta_path); |
||
2019 | free (meta); |
||
2020 | return NULL; |
||
2021 | } |
||
2022 | |||
2023 | static cairo_int_status_t |
||
2024 | _cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t *surface, |
||
2025 | const cairo_path_fixed_t *path, |
||
2026 | const cairo_stroke_style_t *style, |
||
2027 | const cairo_matrix_t *ctm, |
||
2028 | const cairo_matrix_t *ctm_inverse, |
||
2029 | double tolerance, |
||
2030 | int n_layers, |
||
2031 | cairo_bool_t one_shot, |
||
2032 | CoglPrimitive **primitive, |
||
2033 | size_t *size) |
||
2034 | { |
||
2035 | cairo_traps_t traps; |
||
2036 | cairo_int_status_t status; |
||
2037 | |||
2038 | _cairo_traps_init (&traps); |
||
2039 | |||
2040 | status = _cairo_path_fixed_stroke_polygon_to_traps (path, style, |
||
2041 | ctm, ctm_inverse, |
||
2042 | tolerance, |
||
2043 | &traps); |
||
2044 | if (unlikely (status)) |
||
2045 | goto BAIL; |
||
2046 | |||
2047 | if (traps.num_traps == 0) { |
||
2048 | status = CAIRO_INT_STATUS_NOTHING_TO_DO; |
||
2049 | goto BAIL; |
||
2050 | } |
||
2051 | |||
2052 | *size = traps.num_traps * sizeof (CoglVertexP2) * 6; |
||
2053 | |||
2054 | //g_print ("new stroke prim\n"); |
||
2055 | *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot); |
||
2056 | if (!*primitive) { |
||
2057 | status = CAIRO_INT_STATUS_NO_MEMORY; |
||
2058 | goto BAIL; |
||
2059 | } |
||
2060 | |||
2061 | BAIL: |
||
2062 | _cairo_traps_fini (&traps); |
||
2063 | return status; |
||
2064 | } |
||
2065 | |||
2066 | static cairo_int_status_t |
||
2067 | _cairo_cogl_surface_stroke (void *abstract_surface, |
||
2068 | cairo_operator_t op, |
||
2069 | const cairo_pattern_t *source, |
||
2070 | const cairo_path_fixed_t *path, |
||
2071 | const cairo_stroke_style_t *style, |
||
2072 | const cairo_matrix_t *ctm, |
||
2073 | const cairo_matrix_t *ctm_inverse, |
||
2074 | double tolerance, |
||
2075 | cairo_antialias_t antialias, |
||
2076 | const cairo_clip_t *clip) |
||
2077 | { |
||
2078 | cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; |
||
2079 | cairo_composite_rectangles_t extents; |
||
2080 | CoglPipeline *pipeline; |
||
2081 | cairo_status_t status; |
||
2082 | #ifdef ENABLE_PATH_CACHE |
||
2083 | cairo_cogl_path_stroke_meta_t *meta = NULL; |
||
2084 | cairo_matrix_t transform_matrix; |
||
2085 | #endif |
||
2086 | cairo_matrix_t *transform = NULL; |
||
2087 | gboolean one_shot = TRUE; |
||
2088 | CoglPrimitive *prim = NULL; |
||
2089 | cairo_bool_t new_prim = FALSE; |
||
2090 | |||
2091 | if (! is_operator_supported (op)) |
||
2092 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2093 | |||
2094 | /* FIXME - support unbounded operators */ |
||
2095 | if (!_cairo_operator_bounded_by_mask (op)) { |
||
2096 | /* Currently IN this is the only unbounded operator we aim to support |
||
2097 | * in cairo-cogl. */ |
||
2098 | assert (op == CAIRO_OPERATOR_IN); |
||
2099 | g_warning ("FIXME: handle stroking with unbounded operators!"); |
||
2100 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2101 | } |
||
2102 | |||
2103 | status = _cairo_composite_rectangles_init_for_stroke (&extents, |
||
2104 | &surface->base, |
||
2105 | op, source, path, |
||
2106 | style, |
||
2107 | ctm, |
||
2108 | clip); |
||
2109 | if (unlikely (status)) |
||
2110 | return status; |
||
2111 | |||
2112 | #ifdef ENABLE_PATH_CACHE |
||
2113 | /* FIXME: we are currently leaking the meta state if we don't reach |
||
2114 | * the cache_insert at the end. */ |
||
2115 | meta = _cairo_cogl_get_path_stroke_meta (surface, style, tolerance); |
||
2116 | if (meta) { |
||
2117 | prim = meta->prim; |
||
2118 | if (prim) { |
||
2119 | cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm); |
||
2120 | transform = &transform_matrix; |
||
2121 | } else if (meta->counter++ > 10) |
||
2122 | one_shot = FALSE; |
||
2123 | } |
||
2124 | #endif |
||
2125 | |||
2126 | if (!prim) { |
||
2127 | int n_layers = _cairo_cogl_source_n_layers (source); |
||
2128 | size_t prim_size = 0; |
||
2129 | status = _cairo_cogl_stroke_to_primitive (surface, path, style, |
||
2130 | ctm, ctm_inverse, tolerance, |
||
2131 | n_layers, one_shot, |
||
2132 | &prim, &prim_size); |
||
2133 | if (unlikely (status)) |
||
2134 | return status; |
||
2135 | new_prim = TRUE; |
||
2136 | #if defined (ENABLE_PATH_CACHE) |
||
2137 | if (meta) { |
||
2138 | meta->prim = cogl_object_ref (prim); |
||
2139 | _cairo_cogl_path_stroke_meta_set_prim_size (surface, meta, prim_size); |
||
2140 | } |
||
2141 | #endif |
||
2142 | } |
||
2143 | |||
2144 | pipeline = get_source_mask_operator_destination_pipeline (NULL, source, |
||
2145 | op, surface, &extents); |
||
2146 | if (!pipeline) |
||
2147 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2148 | |||
2149 | _cairo_cogl_maybe_log_clip (surface, &extents); |
||
2150 | |||
2151 | _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform); |
||
2152 | |||
2153 | /* The journal will take a reference on the pipeline and primitive... */ |
||
2154 | cogl_object_unref (pipeline); |
||
2155 | if (new_prim) |
||
2156 | cogl_object_unref (prim); |
||
2157 | |||
2158 | return CAIRO_INT_STATUS_SUCCESS; |
||
2159 | } |
||
2160 | |||
2161 | static cairo_cogl_path_fill_meta_t * |
||
2162 | _cairo_cogl_path_fill_meta_reference (cairo_cogl_path_fill_meta_t *meta) |
||
2163 | { |
||
2164 | assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count)); |
||
2165 | |||
2166 | _cairo_reference_count_inc (&meta->ref_count); |
||
2167 | |||
2168 | return meta; |
||
2169 | } |
||
2170 | |||
2171 | static void |
||
2172 | _cairo_cogl_path_fill_meta_destroy (cairo_cogl_path_fill_meta_t *meta) |
||
2173 | { |
||
2174 | assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count)); |
||
2175 | |||
2176 | if (! _cairo_reference_count_dec_and_test (&meta->ref_count)) |
||
2177 | return; |
||
2178 | |||
2179 | _cairo_path_fixed_fini (meta->user_path); |
||
2180 | free (meta->user_path); |
||
2181 | |||
2182 | if (meta->prim) |
||
2183 | cogl_object_unref (meta->prim); |
||
2184 | |||
2185 | free (meta); |
||
2186 | } |
||
2187 | |||
2188 | static cairo_cogl_path_fill_meta_t * |
||
2189 | _cairo_cogl_path_fill_meta_lookup (cairo_cogl_device_t *ctx, |
||
2190 | unsigned long hash, |
||
2191 | cairo_path_fixed_t *user_path) |
||
2192 | { |
||
2193 | cairo_cogl_path_fill_meta_t *ret; |
||
2194 | cairo_cogl_path_fill_meta_t lookup; |
||
2195 | |||
2196 | lookup.cache_entry.hash = hash; |
||
2197 | lookup.user_path = user_path; |
||
2198 | |||
2199 | ret = _cairo_cache_lookup (&ctx->path_fill_staging_cache, &lookup.cache_entry); |
||
2200 | if (!ret) |
||
2201 | ret = _cairo_cache_lookup (&ctx->path_fill_prim_cache, &lookup.cache_entry); |
||
2202 | return ret; |
||
2203 | } |
||
2204 | |||
2205 | static void |
||
2206 | _cairo_cogl_path_fill_meta_set_prim_size (cairo_cogl_surface_t *surface, |
||
2207 | cairo_cogl_path_fill_meta_t *meta, |
||
2208 | size_t size) |
||
2209 | { |
||
2210 | /* now that we know the meta structure is associated with a primitive |
||
2211 | * we promote it from the staging cache into the primitive cache. |
||
2212 | */ |
||
2213 | |||
2214 | /* XXX: _cairo_cache borks if you try and remove an entry that's already |
||
2215 | * been evicted so we explicitly look it up first... */ |
||
2216 | if (_cairo_cache_lookup (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry)) { |
||
2217 | _cairo_cogl_path_fill_meta_reference (meta); |
||
2218 | _cairo_cache_remove (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry); |
||
2219 | } |
||
2220 | |||
2221 | meta->cache_entry.size = size; |
||
2222 | if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_prim_cache, &meta->cache_entry) != |
||
2223 | CAIRO_STATUS_SUCCESS) |
||
2224 | _cairo_cogl_path_fill_meta_destroy (meta); |
||
2225 | } |
||
2226 | |||
2227 | static cairo_cogl_path_fill_meta_t * |
||
2228 | _cairo_cogl_get_path_fill_meta (cairo_cogl_surface_t *surface) |
||
2229 | { |
||
2230 | unsigned long hash; |
||
2231 | cairo_cogl_path_fill_meta_t *meta = NULL; |
||
2232 | cairo_path_fixed_t *meta_path = NULL; |
||
2233 | cairo_status_t status; |
||
2234 | |||
2235 | if (!surface->user_path) |
||
2236 | return NULL; |
||
2237 | |||
2238 | hash = _cairo_path_fixed_hash (surface->user_path); |
||
2239 | |||
2240 | meta = _cairo_cogl_path_fill_meta_lookup (to_device(surface->base.device), |
||
2241 | hash, surface->user_path); |
||
2242 | if (meta) |
||
2243 | return meta; |
||
2244 | |||
2245 | meta = calloc (1, sizeof (cairo_cogl_path_fill_meta_t)); |
||
2246 | if (!meta) |
||
2247 | goto BAIL; |
||
2248 | meta->cache_entry.hash = hash; |
||
2249 | meta->counter = 0; |
||
2250 | CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1); |
||
2251 | meta_path = malloc (sizeof (cairo_path_fixed_t)); |
||
2252 | if (!meta_path) |
||
2253 | goto BAIL; |
||
2254 | /* FIXME: we should add a ref-counted wrapper for our user_paths |
||
2255 | * so we don't have to keep copying them here! */ |
||
2256 | status = _cairo_path_fixed_init_copy (meta_path, surface->user_path); |
||
2257 | if (unlikely (status)) |
||
2258 | goto BAIL; |
||
2259 | meta->user_path = meta_path; |
||
2260 | meta->ctm_inverse = *surface->ctm_inverse; |
||
2261 | |||
2262 | /* To start with - until we associate a CoglPrimitive with the meta |
||
2263 | * structure - we keep the meta in a staging structure until we |
||
2264 | * see whether it actually gets re-used. */ |
||
2265 | meta->cache_entry.size = 1; |
||
2266 | if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry) != |
||
2267 | CAIRO_STATUS_SUCCESS) |
||
2268 | _cairo_cogl_path_fill_meta_destroy (meta); |
||
2269 | |||
2270 | return meta; |
||
2271 | |||
2272 | BAIL: |
||
2273 | free (meta_path); |
||
2274 | free (meta); |
||
2275 | return NULL; |
||
2276 | } |
||
2277 | |||
2278 | static cairo_int_status_t |
||
2279 | _cairo_cogl_surface_fill (void *abstract_surface, |
||
2280 | cairo_operator_t op, |
||
2281 | const cairo_pattern_t *source, |
||
2282 | const cairo_path_fixed_t *path, |
||
2283 | cairo_fill_rule_t fill_rule, |
||
2284 | double tolerance, |
||
2285 | cairo_antialias_t antialias, |
||
2286 | const cairo_clip_t *clip) |
||
2287 | { |
||
2288 | cairo_cogl_surface_t *surface = abstract_surface; |
||
2289 | cairo_composite_rectangles_t extents; |
||
2290 | cairo_status_t status; |
||
2291 | #ifdef ENABLE_PATH_CACHE |
||
2292 | cairo_cogl_path_fill_meta_t *meta = NULL; |
||
2293 | cairo_matrix_t transform_matrix; |
||
2294 | #endif |
||
2295 | cairo_matrix_t *transform = NULL; |
||
2296 | cairo_bool_t one_shot = TRUE; |
||
2297 | CoglPrimitive *prim = NULL; |
||
2298 | cairo_bool_t new_prim = FALSE; |
||
2299 | CoglPipeline *pipeline; |
||
2300 | |||
2301 | if (! is_operator_supported (op)) |
||
2302 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2303 | |||
2304 | /* FIXME - support unbounded operators */ |
||
2305 | if (!_cairo_operator_bounded_by_mask (op)) { |
||
2306 | /* Currently IN this is the only unbounded operator we aim to support |
||
2307 | * in cairo-cogl. */ |
||
2308 | assert (op == CAIRO_OPERATOR_IN); |
||
2309 | g_warning ("FIXME: handle filling with unbounded operators!"); |
||
2310 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2311 | } |
||
2312 | |||
2313 | status = _cairo_composite_rectangles_init_for_fill (&extents, |
||
2314 | &surface->base, |
||
2315 | op, source, path, |
||
2316 | clip); |
||
2317 | if (unlikely (status)) |
||
2318 | return status; |
||
2319 | |||
2320 | #ifndef FILL_WITH_COGL_PATH |
||
2321 | #ifdef ENABLE_PATH_CACHE |
||
2322 | meta = _cairo_cogl_get_path_fill_meta (surface); |
||
2323 | if (meta) { |
||
2324 | prim = meta->prim; |
||
2325 | if (prim) { |
||
2326 | cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm); |
||
2327 | transform = &transform_matrix; |
||
2328 | } else if (meta->counter++ > 10) |
||
2329 | one_shot = FALSE; |
||
2330 | } |
||
2331 | #endif /* ENABLE_PATH_CACHE */ |
||
2332 | |||
2333 | if (!prim) { |
||
2334 | int n_layers = _cairo_cogl_source_n_layers (source); |
||
2335 | size_t prim_size; |
||
2336 | status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule, tolerance, |
||
2337 | one_shot, n_layers, &prim, &prim_size); |
||
2338 | if (unlikely (status)) |
||
2339 | return status; |
||
2340 | new_prim = TRUE; |
||
2341 | #ifdef ENABLE_PATH_CACHE |
||
2342 | if (meta) { |
||
2343 | meta->prim = cogl_object_ref (prim); |
||
2344 | _cairo_cogl_path_fill_meta_set_prim_size (surface, meta, prim_size); |
||
2345 | } |
||
2346 | #endif /* ENABLE_PATH_CACHE */ |
||
2347 | } |
||
2348 | |||
2349 | #endif /* !FILL_WITH_COGL_PATH */ |
||
2350 | |||
2351 | pipeline = get_source_mask_operator_destination_pipeline (NULL, source, |
||
2352 | op, surface, &extents); |
||
2353 | if (!pipeline) |
||
2354 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2355 | |||
2356 | _cairo_cogl_maybe_log_clip (surface, &extents); |
||
2357 | |||
2358 | #ifndef FILL_WITH_COGL_PATH |
||
2359 | _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform); |
||
2360 | /* The journal will take a reference on the prim */ |
||
2361 | if (new_prim) |
||
2362 | cogl_object_unref (prim); |
||
2363 | #else |
||
2364 | CoglPath * cogl_path = _cairo_cogl_util_path_from_cairo (path, fill_rule, tolerance); |
||
2365 | _cairo_cogl_journal_log_path (surface, pipeline, cogl_path); |
||
2366 | cogl_object_unref (cogl_path); |
||
2367 | #endif |
||
2368 | |||
2369 | /* The journal will take a reference on the pipeline... */ |
||
2370 | cogl_object_unref (pipeline); |
||
2371 | |||
2372 | return CAIRO_INT_STATUS_SUCCESS; |
||
2373 | } |
||
2374 | |||
2375 | cairo_int_status_t |
||
2376 | _cairo_cogl_surface_fill_rectangle (void *abstract_surface, |
||
2377 | cairo_operator_t op, |
||
2378 | const cairo_pattern_t *source, |
||
2379 | double x, |
||
2380 | double y, |
||
2381 | double width, |
||
2382 | double height, |
||
2383 | cairo_matrix_t *ctm, |
||
2384 | const cairo_clip_t *clip) |
||
2385 | { |
||
2386 | cairo_cogl_surface_t *surface = abstract_surface; |
||
2387 | CoglPipeline *pipeline; |
||
2388 | |||
2389 | if (! is_operator_supported (op)) |
||
2390 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2391 | |||
2392 | /* FIXME - support unbounded operators */ |
||
2393 | if (!_cairo_operator_bounded_by_mask (op)) { |
||
2394 | /* Currently IN this is the only unbounded operator we aim to support |
||
2395 | * in cairo-cogl. */ |
||
2396 | assert (op == CAIRO_OPERATOR_IN); |
||
2397 | g_warning ("FIXME: handle filling with unbounded operators!"); |
||
2398 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2399 | } |
||
2400 | |||
2401 | /* FIXME */ |
||
2402 | #if 0 |
||
2403 | status = _cairo_composite_rectangles_init_for_fill_rectangle (&extents, |
||
2404 | &surface->base, |
||
2405 | op, source, path, |
||
2406 | clip); |
||
2407 | if (unlikely (status)) |
||
2408 | return status; |
||
2409 | #endif |
||
2410 | |||
2411 | if (source->type == CAIRO_PATTERN_TYPE_SOLID) { |
||
2412 | double x1 = x; |
||
2413 | double y1 = y; |
||
2414 | double x2 = x1 + width; |
||
2415 | double y2 = y1 + height; |
||
2416 | |||
2417 | pipeline = get_source_mask_operator_destination_pipeline (NULL, source, |
||
2418 | op, surface, NULL); |
||
2419 | if (!pipeline) |
||
2420 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2421 | |||
2422 | _cairo_cogl_log_clip (surface, clip); |
||
2423 | |||
2424 | _cairo_cogl_journal_log_rectangle (surface, |
||
2425 | pipeline, |
||
2426 | x1, y1, x2, y2, |
||
2427 | 0, |
||
2428 | ctm); |
||
2429 | return CAIRO_INT_STATUS_SUCCESS; |
||
2430 | } else |
||
2431 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2432 | |||
2433 | /* TODO: |
||
2434 | * We need to acquire the textures here, look at the corresponding |
||
2435 | * attributes and see if this can be trivially handled by logging |
||
2436 | * a textured rectangle only needing simple scaling or translation |
||
2437 | * of texture coordinates. |
||
2438 | * |
||
2439 | * At this point we should also aim to remap the default |
||
2440 | * EXTEND_NONE mode to EXTEND_PAD which is more efficient if we |
||
2441 | * know it makes no difference either way since we can map that to |
||
2442 | * CLAMP_TO_EDGE. |
||
2443 | */ |
||
2444 | } |
||
2445 | |||
2446 | static cairo_int_status_t |
||
2447 | _cairo_cogl_surface_show_glyphs (void *surface, |
||
2448 | cairo_operator_t op, |
||
2449 | const cairo_pattern_t *source, |
||
2450 | cairo_glyph_t *glyphs, |
||
2451 | int num_glyphs, |
||
2452 | cairo_scaled_font_t *scaled_font, |
||
2453 | const cairo_clip_t *clip) |
||
2454 | { |
||
2455 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
2456 | } |
||
2457 | |||
2458 | const cairo_surface_backend_t _cairo_cogl_surface_backend = { |
||
2459 | CAIRO_SURFACE_TYPE_COGL, |
||
2460 | _cairo_cogl_surface_finish, |
||
2461 | #ifdef NEED_COGL_CONTEXT |
||
2462 | _cairo_cogl_context_create, |
||
2463 | #else |
||
2464 | _cairo_default_context_create, |
||
2465 | #endif |
||
2466 | |||
2467 | _cairo_cogl_surface_create_similar, |
||
2468 | NULL, /* create similar image */ |
||
2469 | NULL, /* map to image */ |
||
2470 | NULL, /* unmap image */ |
||
2471 | |||
2472 | _cairo_surface_default_source, |
||
2473 | _cairo_cogl_surface_acquire_source_image, |
||
2474 | _cairo_cogl_surface_release_source_image, |
||
2475 | NULL, /* snapshot */ |
||
2476 | |||
2477 | NULL, /* copy_page */ |
||
2478 | NULL, /* show_page */ |
||
2479 | |||
2480 | _cairo_cogl_surface_get_extents, |
||
2481 | NULL, /* get_font_options */ |
||
2482 | |||
2483 | _cairo_cogl_surface_flush, /* flush */ |
||
2484 | NULL, /* mark_dirty_rectangle */ |
||
2485 | |||
2486 | _cairo_cogl_surface_paint, |
||
2487 | _cairo_cogl_surface_mask, |
||
2488 | _cairo_cogl_surface_stroke, |
||
2489 | _cairo_cogl_surface_fill, |
||
2490 | NULL, /* fill_stroke*/ |
||
2491 | _cairo_surface_fallback_glyphs, |
||
2492 | }; |
||
2493 | |||
2494 | static cairo_surface_t * |
||
2495 | _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev, |
||
2496 | cairo_bool_t ignore_alpha, |
||
2497 | CoglFramebuffer *framebuffer, |
||
2498 | CoglTexture *texture) |
||
2499 | { |
||
2500 | cairo_cogl_surface_t *surface; |
||
2501 | cairo_status_t status; |
||
2502 | |||
2503 | status = cairo_device_acquire (&dev->base); |
||
2504 | if (unlikely (status)) |
||
2505 | return _cairo_surface_create_in_error (status); |
||
2506 | |||
2507 | surface = malloc (sizeof (cairo_cogl_surface_t)); |
||
2508 | if (unlikely (surface == NULL)) |
||
2509 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
||
2510 | |||
2511 | surface->ignore_alpha = ignore_alpha; |
||
2512 | |||
2513 | surface->framebuffer = framebuffer; |
||
2514 | if (framebuffer) { |
||
2515 | surface->width = cogl_framebuffer_get_width (framebuffer); |
||
2516 | surface->height = cogl_framebuffer_get_height (framebuffer); |
||
2517 | surface->cogl_format = cogl_framebuffer_get_color_format (framebuffer); |
||
2518 | cogl_object_ref (framebuffer); |
||
2519 | } |
||
2520 | |||
2521 | /* FIXME: If texture == NULL and we are given an offscreen framebuffer |
||
2522 | * then we want a way to poke inside the framebuffer to get a texture */ |
||
2523 | surface->texture = texture; |
||
2524 | if (texture) { |
||
2525 | if (!framebuffer) { |
||
2526 | surface->width = cogl_texture_get_width (texture); |
||
2527 | surface->height = cogl_texture_get_height (texture); |
||
2528 | surface->cogl_format = cogl_texture_get_format (texture); |
||
2529 | } |
||
2530 | cogl_object_ref (texture); |
||
2531 | } |
||
2532 | |||
2533 | assert(surface->width && surface->height); |
||
2534 | |||
2535 | surface->journal = NULL; |
||
2536 | |||
2537 | surface->buffer_stack = NULL; |
||
2538 | surface->buffer_stack_size = 4096; |
||
2539 | |||
2540 | surface->last_clip = NULL; |
||
2541 | |||
2542 | surface->n_clip_updates_per_frame = 0; |
||
2543 | |||
2544 | _cairo_surface_init (&surface->base, |
||
2545 | &_cairo_cogl_surface_backend, |
||
2546 | &dev->base, |
||
2547 | CAIRO_CONTENT_COLOR_ALPHA); |
||
2548 | |||
2549 | return &surface->base; |
||
2550 | } |
||
2551 | |||
2552 | cairo_surface_t * |
||
2553 | cairo_cogl_surface_create (cairo_device_t *abstract_device, |
||
2554 | CoglFramebuffer *framebuffer) |
||
2555 | { |
||
2556 | cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device; |
||
2557 | |||
2558 | if (abstract_device == NULL) |
||
2559 | return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR); |
||
2560 | |||
2561 | if (abstract_device->status) |
||
2562 | return _cairo_surface_create_in_error (abstract_device->status); |
||
2563 | |||
2564 | if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL) |
||
2565 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); |
||
2566 | |||
2567 | return _cairo_cogl_surface_create_full (dev, FALSE, framebuffer, NULL); |
||
2568 | } |
||
2569 | slim_hidden_def (cairo_cogl_surface_create); |
||
2570 | |||
2571 | CoglFramebuffer * |
||
2572 | cairo_cogl_surface_get_framebuffer (cairo_surface_t *abstract_surface) |
||
2573 | { |
||
2574 | cairo_cogl_surface_t *surface; |
||
2575 | |||
2576 | if (abstract_surface->backend != &_cairo_cogl_surface_backend) { |
||
2577 | _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); |
||
2578 | return NULL; |
||
2579 | } |
||
2580 | |||
2581 | surface = (cairo_cogl_surface_t *) abstract_surface; |
||
2582 | |||
2583 | return surface->framebuffer; |
||
2584 | } |
||
2585 | slim_hidden_def (cairo_cogl_surface_get_framebuffer); |
||
2586 | |||
2587 | CoglTexture * |
||
2588 | cairo_cogl_surface_get_texture (cairo_surface_t *abstract_surface) |
||
2589 | { |
||
2590 | cairo_cogl_surface_t *surface; |
||
2591 | |||
2592 | if (abstract_surface->backend != &_cairo_cogl_surface_backend) { |
||
2593 | _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); |
||
2594 | return NULL; |
||
2595 | } |
||
2596 | |||
2597 | surface = (cairo_cogl_surface_t *) abstract_surface; |
||
2598 | |||
2599 | return surface->texture; |
||
2600 | } |
||
2601 | slim_hidden_def (cairo_cogl_surface_get_texture); |
||
2602 | |||
2603 | static cairo_status_t |
||
2604 | _cairo_cogl_device_flush (void *device) |
||
2605 | { |
||
2606 | cairo_status_t status; |
||
2607 | |||
2608 | status = cairo_device_acquire (device); |
||
2609 | if (unlikely (status)) |
||
2610 | return status; |
||
2611 | |||
2612 | /* XXX: we don't need to flush Cogl here, we just need to flush |
||
2613 | * any batching we do of compositing primitives. */ |
||
2614 | |||
2615 | cairo_device_release (device); |
||
2616 | |||
2617 | return CAIRO_STATUS_SUCCESS; |
||
2618 | } |
||
2619 | |||
2620 | static void |
||
2621 | _cairo_cogl_device_finish (void *device) |
||
2622 | { |
||
2623 | cairo_status_t status; |
||
2624 | |||
2625 | status = cairo_device_acquire (device); |
||
2626 | if (unlikely (status)) |
||
2627 | return; |
||
2628 | |||
2629 | /* XXX: Drop references to external resources */ |
||
2630 | |||
2631 | cairo_device_release (device); |
||
2632 | } |
||
2633 | |||
2634 | static void |
||
2635 | _cairo_cogl_device_destroy (void *device) |
||
2636 | { |
||
2637 | cairo_cogl_device_t *dev = device; |
||
2638 | |||
2639 | /* FIXME: Free stuff! */ |
||
2640 | |||
2641 | g_free (dev); |
||
2642 | } |
||
2643 | |||
2644 | static const cairo_device_backend_t _cairo_cogl_device_backend = { |
||
2645 | CAIRO_DEVICE_TYPE_COGL, |
||
2646 | |||
2647 | NULL, /* lock */ |
||
2648 | NULL, /* unlock */ |
||
2649 | |||
2650 | _cairo_cogl_device_flush, |
||
2651 | _cairo_cogl_device_finish, |
||
2652 | _cairo_cogl_device_destroy, |
||
2653 | }; |
||
2654 | |||
2655 | static cairo_bool_t |
||
2656 | set_blend (CoglPipeline *pipeline, const char *blend_string) |
||
2657 | { |
||
2658 | GError *error = NULL; |
||
2659 | if (!cogl_pipeline_set_blend (pipeline, blend_string, &error)) { |
||
2660 | g_warning ("Unsupported blend string with current gpu/driver: %s", blend_string); |
||
2661 | g_error_free (error); |
||
2662 | return FALSE; |
||
2663 | } |
||
2664 | return TRUE; |
||
2665 | } |
||
2666 | |||
2667 | static cairo_bool_t |
||
2668 | _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op) |
||
2669 | { |
||
2670 | cairo_bool_t status = FALSE; |
||
2671 | |||
2672 | switch ((int)op) |
||
2673 | { |
||
2674 | case CAIRO_OPERATOR_SOURCE: |
||
2675 | status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)"); |
||
2676 | break; |
||
2677 | case CAIRO_OPERATOR_OVER: |
||
2678 | status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))"); |
||
2679 | break; |
||
2680 | case CAIRO_OPERATOR_IN: |
||
2681 | status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], 0)"); |
||
2682 | break; |
||
2683 | case CAIRO_OPERATOR_DEST_OVER: |
||
2684 | status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)"); |
||
2685 | break; |
||
2686 | case CAIRO_OPERATOR_DEST_IN: |
||
2687 | status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])"); |
||
2688 | break; |
||
2689 | case CAIRO_OPERATOR_ADD: |
||
2690 | status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR)"); |
||
2691 | break; |
||
2692 | } |
||
2693 | |||
2694 | return status; |
||
2695 | } |
||
2696 | |||
2697 | static void |
||
2698 | create_templates_for_op (cairo_cogl_device_t *dev, cairo_operator_t op) |
||
2699 | { |
||
2700 | CoglPipeline *base = cogl_pipeline_new (); |
||
2701 | CoglPipeline *pipeline; |
||
2702 | CoglColor color; |
||
2703 | |||
2704 | if (!_cairo_cogl_setup_op_state (base, op)) { |
||
2705 | cogl_object_unref (base); |
||
2706 | return; |
||
2707 | } |
||
2708 | |||
2709 | dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base; |
||
2710 | |||
2711 | pipeline = cogl_pipeline_copy (base); |
||
2712 | cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture); |
||
2713 | dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_TEXTURE] = pipeline; |
||
2714 | |||
2715 | pipeline = cogl_pipeline_copy (base); |
||
2716 | cogl_pipeline_set_layer_combine (pipeline, 1, |
||
2717 | "RGBA = MODULATE (PREVIOUS, CONSTANT[A])", |
||
2718 | NULL); |
||
2719 | cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color); |
||
2720 | cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture); |
||
2721 | dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID] = pipeline; |
||
2722 | |||
2723 | pipeline = cogl_pipeline_copy (base); |
||
2724 | cogl_pipeline_set_layer_combine (pipeline, 1, |
||
2725 | "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", |
||
2726 | NULL); |
||
2727 | cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture); |
||
2728 | dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE] = pipeline; |
||
2729 | } |
||
2730 | |||
2731 | cairo_device_t * |
||
2732 | cairo_cogl_device_create (CoglContext *cogl_context) |
||
2733 | { |
||
2734 | cairo_cogl_device_t *dev = g_new0 (cairo_cogl_device_t, 1); |
||
2735 | cairo_status_t status; |
||
2736 | |||
2737 | dev->backend_vtable_initialized = FALSE; |
||
2738 | |||
2739 | dev->cogl_context = cogl_context; |
||
2740 | |||
2741 | dev->dummy_texture = cogl_texture_new_with_size (1, 1, |
||
2742 | COGL_TEXTURE_NO_SLICING, |
||
2743 | COGL_PIXEL_FORMAT_ANY); |
||
2744 | if (!dev->dummy_texture) |
||
2745 | goto ERROR; |
||
2746 | |||
2747 | memset (dev->template_pipelines, 0, sizeof (dev->template_pipelines)); |
||
2748 | create_templates_for_op (dev, CAIRO_OPERATOR_SOURCE); |
||
2749 | create_templates_for_op (dev, CAIRO_OPERATOR_OVER); |
||
2750 | create_templates_for_op (dev, CAIRO_OPERATOR_IN); |
||
2751 | create_templates_for_op (dev, CAIRO_OPERATOR_DEST_OVER); |
||
2752 | create_templates_for_op (dev, CAIRO_OPERATOR_DEST_IN); |
||
2753 | create_templates_for_op (dev, CAIRO_OPERATOR_ADD); |
||
2754 | |||
2755 | status = _cairo_cache_init (&dev->linear_cache, |
||
2756 | _cairo_cogl_linear_gradient_equal, |
||
2757 | NULL, |
||
2758 | (cairo_destroy_func_t) _cairo_cogl_linear_gradient_destroy, |
||
2759 | CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE); |
||
2760 | if (unlikely (status)) |
||
2761 | return _cairo_device_create_in_error(status); |
||
2762 | |||
2763 | status = _cairo_cache_init (&dev->path_fill_staging_cache, |
||
2764 | _cairo_cogl_path_fill_meta_equal, |
||
2765 | NULL, |
||
2766 | (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy, |
||
2767 | 1000); |
||
2768 | |||
2769 | status = _cairo_cache_init (&dev->path_stroke_staging_cache, |
||
2770 | _cairo_cogl_path_stroke_meta_equal, |
||
2771 | NULL, |
||
2772 | (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy, |
||
2773 | 1000); |
||
2774 | |||
2775 | status = _cairo_cache_init (&dev->path_fill_prim_cache, |
||
2776 | _cairo_cogl_path_fill_meta_equal, |
||
2777 | NULL, |
||
2778 | (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy, |
||
2779 | CAIRO_COGL_PATH_META_CACHE_SIZE); |
||
2780 | |||
2781 | status = _cairo_cache_init (&dev->path_stroke_prim_cache, |
||
2782 | _cairo_cogl_path_stroke_meta_equal, |
||
2783 | NULL, |
||
2784 | (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy, |
||
2785 | CAIRO_COGL_PATH_META_CACHE_SIZE); |
||
2786 | |||
2787 | _cairo_device_init (&dev->base, &_cairo_cogl_device_backend); |
||
2788 | return &dev->base; |
||
2789 | |||
2790 | ERROR: |
||
2791 | g_free (dev); |
||
2792 | return NULL; |
||
2793 | } |
||
2794 | slim_hidden_def (cairo_cogl_device_create); |
||
2795 | |||
2796 | void |
||
2797 | cairo_cogl_surface_end_frame (cairo_surface_t *abstract_surface) |
||
2798 | { |
||
2799 | cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; |
||
2800 | cairo_surface_flush (abstract_surface); |
||
2801 | |||
2802 | //g_print ("n_clip_update_per_frame = %d\n", surface->n_clip_updates_per_frame); |
||
2803 | surface->n_clip_updates_per_frame = 0; |
||
2804 | } |
||
2805 | slim_hidden_def (cairo_cogl_surface_end_frame);>>>=>>>>>>> |