1 /*
2 * Cogl
3 *
4 * A Low Level GPU Graphics and Utilities API
5 *
6 * Copyright (C) 2009 Intel Corporation.
7 *
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use, copy,
12 * modify, merge, publish, distribute, sublicense, and/or sell copies
13 * of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "cogl-config.h"
31 #endif
32
33 #include <glib.h>
34 #include <string.h>
35
36 #include "cogl-pango-display-list.h"
37 #include "cogl-pango-pipeline-cache.h"
38 #include "cogl/cogl-context-private.h"
39
40 typedef enum
41 {
42 COGL_PANGO_DISPLAY_LIST_TEXTURE,
43 COGL_PANGO_DISPLAY_LIST_RECTANGLE,
44 COGL_PANGO_DISPLAY_LIST_TRAPEZOID
45 } CoglPangoDisplayListNodeType;
46
47 typedef struct _CoglPangoDisplayListNode CoglPangoDisplayListNode;
48 typedef struct _CoglPangoDisplayListRectangle CoglPangoDisplayListRectangle;
49
50 struct _CoglPangoDisplayList
51 {
52 CoglBool color_override;
53 CoglColor color;
54 GSList *nodes;
55 GSList *last_node;
56 CoglPangoPipelineCache *pipeline_cache;
57 };
58
59 /* This matches the format expected by cogl_rectangles_with_texture_coords */
60 struct _CoglPangoDisplayListRectangle
61 {
62 float x_1, y_1, x_2, y_2;
63 float s_1, t_1, s_2, t_2;
64 };
65
66 struct _CoglPangoDisplayListNode
67 {
68 CoglPangoDisplayListNodeType type;
69
70 CoglBool color_override;
71 CoglColor color;
72
73 CoglPipeline *pipeline;
74
75 union
76 {
77 struct
78 {
79 /* The texture to render these coords from */
80 CoglTexture *texture;
81 /* Array of rectangles in the format expected by
82 cogl_rectangles_with_texture_coords */
83 GArray *rectangles;
84 /* A primitive representing those vertices */
85 CoglPrimitive *primitive;
86 } texture;
87
88 struct
89 {
90 float x_1, y_1;
91 float x_2, y_2;
92 } rectangle;
93
94 struct
95 {
96 CoglPrimitive *primitive;
97 } trapezoid;
98 } d;
99 };
100
101 CoglPangoDisplayList *
_cogl_pango_display_list_new(CoglPangoPipelineCache * pipeline_cache)102 _cogl_pango_display_list_new (CoglPangoPipelineCache *pipeline_cache)
103 {
104 CoglPangoDisplayList *dl = g_slice_new0 (CoglPangoDisplayList);
105
106 dl->pipeline_cache = pipeline_cache;
107
108 return dl;
109 }
110
111 static void
_cogl_pango_display_list_append_node(CoglPangoDisplayList * dl,CoglPangoDisplayListNode * node)112 _cogl_pango_display_list_append_node (CoglPangoDisplayList *dl,
113 CoglPangoDisplayListNode *node)
114 {
115 if (dl->last_node)
116 dl->last_node = dl->last_node->next = g_slist_prepend (NULL, node);
117 else
118 dl->last_node = dl->nodes = g_slist_prepend (NULL, node);
119 }
120
121 void
_cogl_pango_display_list_set_color_override(CoglPangoDisplayList * dl,const CoglColor * color)122 _cogl_pango_display_list_set_color_override (CoglPangoDisplayList *dl,
123 const CoglColor *color)
124 {
125 dl->color_override = TRUE;
126 dl->color = *color;
127 }
128
129 void
_cogl_pango_display_list_remove_color_override(CoglPangoDisplayList * dl)130 _cogl_pango_display_list_remove_color_override (CoglPangoDisplayList *dl)
131 {
132 dl->color_override = FALSE;
133 }
134
135 void
_cogl_pango_display_list_add_texture(CoglPangoDisplayList * dl,CoglTexture * texture,float x_1,float y_1,float x_2,float y_2,float tx_1,float ty_1,float tx_2,float ty_2)136 _cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl,
137 CoglTexture *texture,
138 float x_1, float y_1,
139 float x_2, float y_2,
140 float tx_1, float ty_1,
141 float tx_2, float ty_2)
142 {
143 CoglPangoDisplayListNode *node;
144 CoglPangoDisplayListRectangle *rectangle;
145
146 /* Add to the last node if it is a texture node with the same
147 target texture */
148 if (dl->last_node
149 && (node = dl->last_node->data)->type == COGL_PANGO_DISPLAY_LIST_TEXTURE
150 && node->d.texture.texture == texture
151 && (dl->color_override
152 ? (node->color_override && cogl_color_equal (&dl->color, &node->color))
153 : !node->color_override))
154 {
155 /* Get rid of the vertex buffer so that it will be recreated */
156 if (node->d.texture.primitive != NULL)
157 {
158 cogl_object_unref (node->d.texture.primitive);
159 node->d.texture.primitive = NULL;
160 }
161 }
162 else
163 {
164 /* Otherwise create a new node */
165 node = g_slice_new (CoglPangoDisplayListNode);
166
167 node->type = COGL_PANGO_DISPLAY_LIST_TEXTURE;
168 node->color_override = dl->color_override;
169 node->color = dl->color;
170 node->pipeline = NULL;
171 node->d.texture.texture = cogl_object_ref (texture);
172 node->d.texture.rectangles
173 = g_array_new (FALSE, FALSE, sizeof (CoglPangoDisplayListRectangle));
174 node->d.texture.primitive = NULL;
175
176 _cogl_pango_display_list_append_node (dl, node);
177 }
178
179 g_array_set_size (node->d.texture.rectangles,
180 node->d.texture.rectangles->len + 1);
181 rectangle = &g_array_index (node->d.texture.rectangles,
182 CoglPangoDisplayListRectangle,
183 node->d.texture.rectangles->len - 1);
184 rectangle->x_1 = x_1;
185 rectangle->y_1 = y_1;
186 rectangle->x_2 = x_2;
187 rectangle->y_2 = y_2;
188 rectangle->s_1 = tx_1;
189 rectangle->t_1 = ty_1;
190 rectangle->s_2 = tx_2;
191 rectangle->t_2 = ty_2;
192 }
193
194 void
_cogl_pango_display_list_add_rectangle(CoglPangoDisplayList * dl,float x_1,float y_1,float x_2,float y_2)195 _cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl,
196 float x_1, float y_1,
197 float x_2, float y_2)
198 {
199 CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode);
200
201 node->type = COGL_PANGO_DISPLAY_LIST_RECTANGLE;
202 node->color_override = dl->color_override;
203 node->color = dl->color;
204 node->d.rectangle.x_1 = x_1;
205 node->d.rectangle.y_1 = y_1;
206 node->d.rectangle.x_2 = x_2;
207 node->d.rectangle.y_2 = y_2;
208 node->pipeline = NULL;
209
210 _cogl_pango_display_list_append_node (dl, node);
211 }
212
213 void
_cogl_pango_display_list_add_trapezoid(CoglPangoDisplayList * dl,float y_1,float x_11,float x_21,float y_2,float x_12,float x_22)214 _cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl,
215 float y_1,
216 float x_11,
217 float x_21,
218 float y_2,
219 float x_12,
220 float x_22)
221 {
222 CoglContext *ctx = dl->pipeline_cache->ctx;
223 CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode);
224 CoglVertexP2 vertices[4] = {
225 { x_11, y_1 },
226 { x_12, y_2 },
227 { x_22, y_2 },
228 { x_21, y_1 }
229 };
230
231 node->type = COGL_PANGO_DISPLAY_LIST_TRAPEZOID;
232 node->color_override = dl->color_override;
233 node->color = dl->color;
234 node->pipeline = NULL;
235
236 node->d.trapezoid.primitive =
237 cogl_primitive_new_p2 (ctx,
238 COGL_VERTICES_MODE_TRIANGLE_FAN,
239 4,
240 vertices);
241
242 _cogl_pango_display_list_append_node (dl, node);
243 }
244
245 static void
emit_rectangles_through_journal(CoglFramebuffer * fb,CoglPipeline * pipeline,CoglPangoDisplayListNode * node)246 emit_rectangles_through_journal (CoglFramebuffer *fb,
247 CoglPipeline *pipeline,
248 CoglPangoDisplayListNode *node)
249 {
250 const float *rectangles = (const float *)node->d.texture.rectangles->data;
251
252 cogl_framebuffer_draw_textured_rectangles (fb,
253 pipeline,
254 rectangles,
255 node->d.texture.rectangles->len);
256 }
257
258 static void
emit_vertex_buffer_geometry(CoglFramebuffer * fb,CoglPipeline * pipeline,CoglPangoDisplayListNode * node)259 emit_vertex_buffer_geometry (CoglFramebuffer *fb,
260 CoglPipeline *pipeline,
261 CoglPangoDisplayListNode *node)
262 {
263 CoglContext *ctx = fb->context;
264
265 /* It's expensive to go through the Cogl journal for large runs
266 * of text in part because the journal transforms the quads in software
267 * to avoid changing the modelview matrix. So for larger runs of text
268 * we load the vertices into a VBO, and this has the added advantage
269 * that if the text doesn't change from frame to frame the VBO can
270 * be re-used avoiding the repeated cost of validating the data and
271 * mapping it into the GPU... */
272
273 if (node->d.texture.primitive == NULL)
274 {
275 CoglAttributeBuffer *buffer;
276 CoglVertexP2T2 *verts, *v;
277 int n_verts;
278 CoglBool allocated = FALSE;
279 CoglAttribute *attributes[2];
280 CoglPrimitive *prim;
281 int i;
282
283 n_verts = node->d.texture.rectangles->len * 4;
284
285 buffer
286 = cogl_attribute_buffer_new_with_size (ctx,
287 n_verts *
288 sizeof (CoglVertexP2T2));
289
290 if ((verts = cogl_buffer_map (COGL_BUFFER (buffer),
291 COGL_BUFFER_ACCESS_WRITE,
292 COGL_BUFFER_MAP_HINT_DISCARD)) == NULL)
293 {
294 verts = g_new (CoglVertexP2T2, n_verts);
295 allocated = TRUE;
296 }
297
298 v = verts;
299
300 /* Copy the rectangles into the buffer and expand into four
301 vertices instead of just two */
302 for (i = 0; i < node->d.texture.rectangles->len; i++)
303 {
304 const CoglPangoDisplayListRectangle *rectangle
305 = &g_array_index (node->d.texture.rectangles,
306 CoglPangoDisplayListRectangle, i);
307
308 v->x = rectangle->x_1;
309 v->y = rectangle->y_1;
310 v->s = rectangle->s_1;
311 v->t = rectangle->t_1;
312 v++;
313 v->x = rectangle->x_1;
314 v->y = rectangle->y_2;
315 v->s = rectangle->s_1;
316 v->t = rectangle->t_2;
317 v++;
318 v->x = rectangle->x_2;
319 v->y = rectangle->y_2;
320 v->s = rectangle->s_2;
321 v->t = rectangle->t_2;
322 v++;
323 v->x = rectangle->x_2;
324 v->y = rectangle->y_1;
325 v->s = rectangle->s_2;
326 v->t = rectangle->t_1;
327 v++;
328 }
329
330 if (allocated)
331 {
332 cogl_buffer_set_data (COGL_BUFFER (buffer),
333 0, /* offset */
334 verts,
335 sizeof (CoglVertexP2T2) * n_verts);
336 free (verts);
337 }
338 else
339 cogl_buffer_unmap (COGL_BUFFER (buffer));
340
341 attributes[0] = cogl_attribute_new (buffer,
342 "cogl_position_in",
343 sizeof (CoglVertexP2T2),
344 G_STRUCT_OFFSET (CoglVertexP2T2, x),
345 2, /* n_components */
346 COGL_ATTRIBUTE_TYPE_FLOAT);
347 attributes[1] = cogl_attribute_new (buffer,
348 "cogl_tex_coord0_in",
349 sizeof (CoglVertexP2T2),
350 G_STRUCT_OFFSET (CoglVertexP2T2, s),
351 2, /* n_components */
352 COGL_ATTRIBUTE_TYPE_FLOAT);
353
354 prim = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
355 n_verts,
356 attributes,
357 2 /* n_attributes */);
358
359 #ifdef CLUTTER_COGL_HAS_GL
360 if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUADS))
361 cogl_primitive_set_mode (prim, GL_QUADS);
362 else
363 #endif
364 {
365 /* GLES doesn't support GL_QUADS so instead we use a VBO
366 with indexed vertices to generate GL_TRIANGLES from the
367 quads */
368
369 CoglIndices *indices =
370 cogl_get_rectangle_indices (ctx, node->d.texture.rectangles->len);
371
372 cogl_primitive_set_indices (prim, indices,
373 node->d.texture.rectangles->len * 6);
374 }
375
376 node->d.texture.primitive = prim;
377
378 cogl_object_unref (buffer);
379 cogl_object_unref (attributes[0]);
380 cogl_object_unref (attributes[1]);
381 }
382
383 cogl_primitive_draw (node->d.texture.primitive,
384 fb,
385 pipeline);
386 }
387
388 static void
_cogl_framebuffer_draw_display_list_texture(CoglFramebuffer * fb,CoglPipeline * pipeline,CoglPangoDisplayListNode * node)389 _cogl_framebuffer_draw_display_list_texture (CoglFramebuffer *fb,
390 CoglPipeline *pipeline,
391 CoglPangoDisplayListNode *node)
392 {
393 /* For small runs of text like icon labels, we can get better performance
394 * going through the Cogl journal since text may then be batched together
395 * with other geometry. */
396 /* FIXME: 25 is a number I plucked out of thin air; it would be good
397 * to determine this empirically! */
398 if (node->d.texture.rectangles->len < 25)
399 emit_rectangles_through_journal (fb, pipeline, node);
400 else
401 emit_vertex_buffer_geometry (fb, pipeline, node);
402 }
403
404 void
_cogl_pango_display_list_render(CoglFramebuffer * fb,CoglPangoDisplayList * dl,const CoglColor * color)405 _cogl_pango_display_list_render (CoglFramebuffer *fb,
406 CoglPangoDisplayList *dl,
407 const CoglColor *color)
408 {
409 GSList *l;
410
411 for (l = dl->nodes; l; l = l->next)
412 {
413 CoglPangoDisplayListNode *node = l->data;
414 CoglColor draw_color;
415
416 if (node->pipeline == NULL)
417 {
418 if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE)
419 node->pipeline =
420 _cogl_pango_pipeline_cache_get (dl->pipeline_cache,
421 node->d.texture.texture);
422 else
423 node->pipeline =
424 _cogl_pango_pipeline_cache_get (dl->pipeline_cache,
425 NULL);
426 }
427
428 if (node->color_override)
429 /* Use the override color but preserve the alpha from the
430 draw color */
431 cogl_color_init_from_4ub (&draw_color,
432 cogl_color_get_red_byte (&node->color),
433 cogl_color_get_green_byte (&node->color),
434 cogl_color_get_blue_byte (&node->color),
435 cogl_color_get_alpha_byte (color));
436 else
437 draw_color = *color;
438 cogl_color_premultiply (&draw_color);
439
440 cogl_pipeline_set_color (node->pipeline, &draw_color);
441
442 switch (node->type)
443 {
444 case COGL_PANGO_DISPLAY_LIST_TEXTURE:
445 _cogl_framebuffer_draw_display_list_texture (fb, node->pipeline, node);
446 break;
447
448 case COGL_PANGO_DISPLAY_LIST_RECTANGLE:
449 cogl_framebuffer_draw_rectangle (fb,
450 node->pipeline,
451 node->d.rectangle.x_1,
452 node->d.rectangle.y_1,
453 node->d.rectangle.x_2,
454 node->d.rectangle.y_2);
455 break;
456
457 case COGL_PANGO_DISPLAY_LIST_TRAPEZOID:
458 cogl_framebuffer_draw_primitive (fb, node->pipeline,
459 node->d.trapezoid.primitive);
460 break;
461 }
462 }
463 }
464
465 static void
_cogl_pango_display_list_node_free(CoglPangoDisplayListNode * node)466 _cogl_pango_display_list_node_free (CoglPangoDisplayListNode *node)
467 {
468 if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE)
469 {
470 g_array_free (node->d.texture.rectangles, TRUE);
471 if (node->d.texture.texture != NULL)
472 cogl_object_unref (node->d.texture.texture);
473 if (node->d.texture.primitive != NULL)
474 cogl_object_unref (node->d.texture.primitive);
475 }
476 else if (node->type == COGL_PANGO_DISPLAY_LIST_TRAPEZOID)
477 cogl_object_unref (node->d.trapezoid.primitive);
478
479 if (node->pipeline)
480 cogl_object_unref (node->pipeline);
481
482 g_slice_free (CoglPangoDisplayListNode, node);
483 }
484
485 void
_cogl_pango_display_list_clear(CoglPangoDisplayList * dl)486 _cogl_pango_display_list_clear (CoglPangoDisplayList *dl)
487 {
488 g_slist_foreach (dl->nodes, (GFunc) _cogl_pango_display_list_node_free, NULL);
489 g_slist_free (dl->nodes);
490 dl->nodes = NULL;
491 dl->last_node = NULL;
492 }
493
494 void
_cogl_pango_display_list_free(CoglPangoDisplayList * dl)495 _cogl_pango_display_list_free (CoglPangoDisplayList *dl)
496 {
497 _cogl_pango_display_list_clear (dl);
498 g_slice_free (CoglPangoDisplayList, dl);
499 }
500