4 * An OpenGL based 'interactive canvas' library.
6 * Authored By Neil Roberts <neil@linux.intel.com>
8 * Copyright (C) 2009 Intel Corporation.
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
29 #include <cogl/cogl.h>
32 #include "cogl-pango-display-list.h"
33 #include "cogl/cogl-context-private.h"
37 COGL_PANGO_DISPLAY_LIST_TEXTURE,
38 COGL_PANGO_DISPLAY_LIST_RECTANGLE,
39 COGL_PANGO_DISPLAY_LIST_TRAPEZOID
40 } CoglPangoDisplayListNodeType;
42 typedef struct _CoglPangoDisplayListNode CoglPangoDisplayListNode;
43 typedef struct _CoglPangoDisplayListRectangle CoglPangoDisplayListRectangle;
45 struct _CoglPangoDisplayList
47 gboolean color_override;
51 CoglPangoPipelineCache *pipeline_cache;
54 /* This matches the format expected by cogl_rectangles_with_texture_coords */
55 struct _CoglPangoDisplayListRectangle
57 float x_1, y_1, x_2, y_2;
58 float s_1, t_1, s_2, t_2;
61 struct _CoglPangoDisplayListNode
63 CoglPangoDisplayListNodeType type;
65 gboolean color_override;
68 CoglPipeline *pipeline;
74 /* The texture to render these coords from */
76 /* Array of rectangles in the format expected by
77 cogl_rectangles_with_texture_coords */
79 /* A primitive representing those vertices */
80 CoglPrimitive *primitive;
101 CoglPangoDisplayList *
102 _cogl_pango_display_list_new (CoglPangoPipelineCache *pipeline_cache)
104 CoglPangoDisplayList *dl = g_slice_new0 (CoglPangoDisplayList);
106 dl->pipeline_cache = pipeline_cache;
112 _cogl_pango_display_list_append_node (CoglPangoDisplayList *dl,
113 CoglPangoDisplayListNode *node)
116 dl->last_node = dl->last_node->next = g_slist_prepend (NULL, node);
118 dl->last_node = dl->nodes = g_slist_prepend (NULL, node);
122 _cogl_pango_display_list_set_color_override (CoglPangoDisplayList *dl,
123 const CoglColor *color)
125 dl->color_override = TRUE;
130 _cogl_pango_display_list_remove_color_override (CoglPangoDisplayList *dl)
132 dl->color_override = FALSE;
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)
143 CoglPangoDisplayListNode *node;
144 CoglPangoDisplayListRectangle *rectangle;
146 /* Add to the last node if it is a texture node with the same
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))
155 /* Get rid of the vertex buffer so that it will be recreated */
156 if (node->d.texture.primitive != NULL)
158 cogl_object_unref (node->d.texture.primitive);
159 node->d.texture.primitive = NULL;
164 /* Otherwise create a new node */
165 node = g_slice_new (CoglPangoDisplayListNode);
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;
176 _cogl_pango_display_list_append_node (dl, node);
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;
195 _cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl,
196 float x_1, float y_1,
197 float x_2, float y_2)
199 CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode);
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;
210 _cogl_pango_display_list_append_node (dl, node);
214 _cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl,
222 CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode);
224 node->type = COGL_PANGO_DISPLAY_LIST_TRAPEZOID;
225 node->color_override = dl->color_override;
226 node->color = dl->color;
227 node->d.trapezoid.y_1 = y_1;
228 node->d.trapezoid.x_11 = x_11;
229 node->d.trapezoid.x_21 = x_21;
230 node->d.trapezoid.y_2 = y_2;
231 node->d.trapezoid.x_12 = x_12;
232 node->d.trapezoid.x_22 = x_22;
233 node->pipeline = NULL;
235 _cogl_pango_display_list_append_node (dl, node);
239 emit_rectangles_through_journal (CoglPangoDisplayListNode *node)
241 cogl_rectangles_with_texture_coords ((float *)
242 node->d.texture.rectangles->data,
243 node->d.texture.rectangles->len);
247 emit_vertex_buffer_geometry (CoglPangoDisplayListNode *node)
249 _COGL_GET_CONTEXT (ctx, NO_RETVAL);
251 /* It's expensive to go through the Cogl journal for large runs
252 * of text in part because the journal transforms the quads in software
253 * to avoid changing the modelview matrix. So for larger runs of text
254 * we load the vertices into a VBO, and this has the added advantage
255 * that if the text doesn't change from frame to frame the VBO can
256 * be re-used avoiding the repeated cost of validating the data and
257 * mapping it into the GPU... */
259 if (node->d.texture.primitive == NULL)
261 CoglAttributeBuffer *buffer;
262 CoglVertexP2T2 *verts, *v;
264 gboolean allocated = FALSE;
265 CoglAttribute *attributes[2];
269 n_verts = node->d.texture.rectangles->len * 4;
272 = cogl_attribute_buffer_new (ctx,
273 n_verts * sizeof (CoglVertexP2T2), NULL);
275 if ((verts = cogl_buffer_map (COGL_BUFFER (buffer),
276 COGL_BUFFER_ACCESS_WRITE,
277 COGL_BUFFER_MAP_HINT_DISCARD)) == NULL)
279 verts = g_new (CoglVertexP2T2, n_verts);
285 /* Copy the rectangles into the buffer and expand into four
286 vertices instead of just two */
287 for (i = 0; i < node->d.texture.rectangles->len; i++)
289 const CoglPangoDisplayListRectangle *rectangle
290 = &g_array_index (node->d.texture.rectangles,
291 CoglPangoDisplayListRectangle, i);
293 v->x = rectangle->x_1;
294 v->y = rectangle->y_1;
295 v->s = rectangle->s_1;
296 v->t = rectangle->t_1;
298 v->x = rectangle->x_1;
299 v->y = rectangle->y_2;
300 v->s = rectangle->s_1;
301 v->t = rectangle->t_2;
303 v->x = rectangle->x_2;
304 v->y = rectangle->y_2;
305 v->s = rectangle->s_2;
306 v->t = rectangle->t_2;
308 v->x = rectangle->x_2;
309 v->y = rectangle->y_1;
310 v->s = rectangle->s_2;
311 v->t = rectangle->t_1;
317 cogl_buffer_set_data (COGL_BUFFER (buffer),
320 sizeof (CoglVertexP2T2) * n_verts);
324 cogl_buffer_unmap (COGL_BUFFER (buffer));
326 attributes[0] = cogl_attribute_new (buffer,
328 sizeof (CoglVertexP2T2),
329 G_STRUCT_OFFSET (CoglVertexP2T2, x),
330 2, /* n_components */
331 COGL_ATTRIBUTE_TYPE_FLOAT);
332 attributes[1] = cogl_attribute_new (buffer,
333 "cogl_tex_coord0_in",
334 sizeof (CoglVertexP2T2),
335 G_STRUCT_OFFSET (CoglVertexP2T2, s),
336 2, /* n_components */
337 COGL_ATTRIBUTE_TYPE_FLOAT);
339 prim = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
342 2 /* n_attributes */);
344 #ifdef CLUTTER_COGL_HAS_GL
345 if (ctx->driver == COGL_DRIVER_GL)
346 cogl_primitive_set_mode (prim, GL_QUADS);
350 /* GLES doesn't support GL_QUADS so instead we use a VBO
351 with indexed vertices to generate GL_TRIANGLES from the
354 CoglIndices *indices =
355 cogl_get_rectangle_indices (ctx, node->d.texture.rectangles->len);
357 cogl_primitive_set_indices (prim, indices,
358 node->d.texture.rectangles->len * 6);
361 node->d.texture.primitive = prim;
363 cogl_object_unref (buffer);
364 cogl_object_unref (attributes[0]);
365 cogl_object_unref (attributes[1]);
368 cogl_framebuffer_draw_primitive (cogl_get_draw_framebuffer (),
370 node->d.texture.primitive);
374 _cogl_pango_display_list_render_texture (CoglPangoDisplayListNode *node)
376 /* For small runs of text like icon labels, we can get better performance
377 * going through the Cogl journal since text may then be batched together
378 * with other geometry. */
379 /* FIXME: 25 is a number I plucked out of thin air; it would be good
380 * to determine this empirically! */
381 if (node->d.texture.rectangles->len < 25)
382 emit_rectangles_through_journal (node);
384 emit_vertex_buffer_geometry (node);
388 _cogl_pango_display_list_render (CoglPangoDisplayList *dl,
389 const CoglColor *color)
393 for (l = dl->nodes; l; l = l->next)
395 CoglPangoDisplayListNode *node = l->data;
396 CoglColor draw_color;
398 if (node->pipeline == NULL)
400 if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE)
402 _cogl_pango_pipeline_cache_get (dl->pipeline_cache,
403 node->d.texture.texture);
406 _cogl_pango_pipeline_cache_get (dl->pipeline_cache,
410 if (node->color_override)
411 /* Use the override color but preserve the alpha from the
413 cogl_color_init_from_4ub (&draw_color,
414 cogl_color_get_red_byte (&node->color),
415 cogl_color_get_green_byte (&node->color),
416 cogl_color_get_blue_byte (&node->color),
417 cogl_color_get_alpha_byte (color));
420 cogl_color_premultiply (&draw_color);
422 cogl_pipeline_set_color (node->pipeline, &draw_color);
423 cogl_push_source (node->pipeline);
427 case COGL_PANGO_DISPLAY_LIST_TEXTURE:
428 _cogl_pango_display_list_render_texture (node);
431 case COGL_PANGO_DISPLAY_LIST_RECTANGLE:
432 cogl_rectangle (node->d.rectangle.x_1,
433 node->d.rectangle.y_1,
434 node->d.rectangle.x_2,
435 node->d.rectangle.y_2);
438 case COGL_PANGO_DISPLAY_LIST_TRAPEZOID:
443 points[0] = node->d.trapezoid.x_11;
444 points[1] = node->d.trapezoid.y_1;
445 points[2] = node->d.trapezoid.x_12;
446 points[3] = node->d.trapezoid.y_2;
447 points[4] = node->d.trapezoid.x_22;
448 points[5] = node->d.trapezoid.y_2;
449 points[6] = node->d.trapezoid.x_21;
450 points[7] = node->d.trapezoid.y_1;
452 path = cogl_path_new ();
453 cogl_path_polygon (path, points, 4);
454 cogl_path_fill (path);
455 cogl_object_unref (path);
465 _cogl_pango_display_list_node_free (CoglPangoDisplayListNode *node)
467 if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE)
469 g_array_free (node->d.texture.rectangles, TRUE);
470 if (node->d.texture.texture != NULL)
471 cogl_object_unref (node->d.texture.texture);
472 if (node->d.texture.primitive != NULL)
473 cogl_object_unref (node->d.texture.primitive);
477 cogl_object_unref (node->pipeline);
479 g_slice_free (CoglPangoDisplayListNode, node);
483 _cogl_pango_display_list_clear (CoglPangoDisplayList *dl)
485 g_slist_foreach (dl->nodes, (GFunc) _cogl_pango_display_list_node_free, NULL);
486 g_slist_free (dl->nodes);
488 dl->last_node = NULL;
492 _cogl_pango_display_list_free (CoglPangoDisplayList *dl)
494 _cogl_pango_display_list_clear (dl);
495 g_slice_free (CoglPangoDisplayList, dl);