Merge branch 'pango-vbo' into 1.0-integration

This makes it cache the geometry of PangoLayouts into a VBO as
described in bug #1572.
This commit is contained in:
Neil Roberts 2009-05-21 16:51:16 +01:00
commit d513c1c540
4 changed files with 559 additions and 61 deletions

View File

@ -1,4 +1,5 @@
source_c = \
cogl-pango-display-list.c \
cogl-pango-fontmap.c \
cogl-pango-render.c \
cogl-pango-glyph-cache.c
@ -6,6 +7,7 @@ source_c = \
source_h = cogl-pango.h
source_h_priv = \
cogl-pango-display-list.h \
cogl-pango-private.h \
cogl-pango-glyph-cache.h

View File

@ -0,0 +1,356 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Neil Roberts <neil@linux.intel.com>
*
* Copyright (C) 2009 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#include <cogl/cogl.h>
#include <string.h>
#include "cogl-pango-display-list.h"
typedef enum
{
COGL_PANGO_DISPLAY_LIST_TEXTURE,
COGL_PANGO_DISPLAY_LIST_RECTANGLE,
COGL_PANGO_DISPLAY_LIST_TRAPEZOID
} CoglPangoDisplayListNodeType;
typedef struct _CoglPangoDisplayListNode CoglPangoDisplayListNode;
typedef struct _CoglPangoDisplayListVertex CoglPangoDisplayListVertex;
#ifdef HAVE_CLUTTER_GLX
#define COGL_PANGO_DISPLAY_LIST_DRAW_MODE GL_QUADS
#else
/* GLES doesn't support GL_QUADS so we use GL_TRIANGLES instead */
#define COGL_PANGO_DISPLAY_LIST_DRAW_MODE GL_TRIANGLES
#endif
struct _CoglPangoDisplayList
{
CoglColor color;
GSList *nodes;
GSList *last_node;
};
struct _CoglPangoDisplayListNode
{
CoglPangoDisplayListNodeType type;
CoglColor color;
union
{
struct
{
/* The texture to render these coords from */
CoglHandle texture;
/* Array of vertex data to render out of this texture */
GArray *verts;
/* A VBO representing those vertices */
CoglHandle vertex_buffer;
} texture;
struct
{
float x_1, y_1;
float x_2, y_2;
} rectangle;
struct
{
float y_1;
float x_11;
float x_21;
float y_2;
float x_12;
float x_22;
} trapezoid;
} d;
};
struct _CoglPangoDisplayListVertex
{
float x, y, t_x, t_y;
};
CoglPangoDisplayList *
_cogl_pango_display_list_new (void)
{
return g_slice_new0 (CoglPangoDisplayList);
}
static void
_cogl_pango_display_list_append_node (CoglPangoDisplayList *dl,
CoglPangoDisplayListNode *node)
{
if (dl->last_node)
dl->last_node = dl->last_node->next = g_slist_prepend (NULL, node);
else
dl->last_node = dl->nodes = g_slist_prepend (NULL, node);
}
void
_cogl_pango_display_list_set_color (CoglPangoDisplayList *dl,
const CoglColor *color)
{
dl->color = *color;
}
void
_cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl,
CoglHandle 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)
{
CoglPangoDisplayListNode *node;
CoglPangoDisplayListVertex *verts;
/* Add to the last node if it is a texture node with the same
target texture */
if (dl->last_node
&& (node = dl->last_node->data)->type == COGL_PANGO_DISPLAY_LIST_TEXTURE
&& node->d.texture.texture == texture
&& !memcmp (&dl->color, &node->color, sizeof (CoglColor)))
{
/* Get rid of the vertex buffer so that it will be recreated */
if (node->d.texture.vertex_buffer != COGL_INVALID_HANDLE)
{
cogl_vertex_buffer_unref (node->d.texture.vertex_buffer);
node->d.texture.vertex_buffer = COGL_INVALID_HANDLE;
}
}
else
{
/* Otherwise create a new node */
node = g_slice_new (CoglPangoDisplayListNode);
node->type = COGL_PANGO_DISPLAY_LIST_TEXTURE;
node->color = dl->color;
node->d.texture.texture = cogl_texture_ref (texture);
node->d.texture.verts
= g_array_new (FALSE, FALSE, sizeof (CoglPangoDisplayListVertex));
node->d.texture.vertex_buffer = COGL_INVALID_HANDLE;
_cogl_pango_display_list_append_node (dl, node);
}
g_array_set_size (node->d.texture.verts,
node->d.texture.verts->len + 4);
verts = &g_array_index (node->d.texture.verts,
CoglPangoDisplayListVertex,
node->d.texture.verts->len - 4);
verts->x = x_1;
verts->y = y_1;
verts->t_x = tx_1;
verts->t_y = ty_1;
verts++;
verts->x = x_1;
verts->y = y_2;
verts->t_x = tx_1;
verts->t_y = ty_2;
verts++;
verts->x = x_2;
verts->y = y_2;
verts->t_x = tx_2;
verts->t_y = ty_2;
verts++;
verts->x = x_2;
verts->y = y_1;
verts->t_x = tx_2;
verts->t_y = ty_1;
#ifndef HAVE_CLUTTER_GLX
/* GLES doesn't support GL_QUADS so we use GL_TRIANGLES instead with
two extra vertices per quad. FIXME: It might be better to use
indexed elements here but cogl vertex buffers don't currently
support storing the indices */
g_array_set_size (node->d.texture.verts,
node->d.texture.verts->len + 2);
verts = &g_array_index (node->d.texture.verts,
CoglPangoDisplayListVertex,
node->d.texture.verts->len - 6);
verts[4] = verts[0];
verts[5] = verts[2];
#endif /* HAVE_CLUTTER_GLX */
}
void
_cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl,
float x_1, float y_1,
float x_2, float y_2)
{
CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode);
node->type = COGL_PANGO_DISPLAY_LIST_RECTANGLE;
node->color = dl->color;
node->d.rectangle.x_1 = x_1;
node->d.rectangle.y_1 = y_1;
node->d.rectangle.x_2 = x_2;
node->d.rectangle.y_2 = y_2;
_cogl_pango_display_list_append_node (dl, node);
}
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)
{
CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode);
node->type = COGL_PANGO_DISPLAY_LIST_TRAPEZOID;
node->color = dl->color;
node->d.trapezoid.y_1 = y_1;
node->d.trapezoid.x_11 = x_11;
node->d.trapezoid.x_21 = x_21;
node->d.trapezoid.y_2 = y_2;
node->d.trapezoid.x_12 = x_12;
node->d.trapezoid.x_22 = x_22;
_cogl_pango_display_list_append_node (dl, node);
}
static void
_cogl_pango_display_list_render_texture (CoglHandle material,
CoglPangoDisplayListNode *node)
{
cogl_material_set_layer (material, 0, node->d.texture.texture);
cogl_material_set_color (material, &node->color);
cogl_set_source (material);
if (node->d.texture.vertex_buffer == COGL_INVALID_HANDLE)
{
CoglHandle vb = cogl_vertex_buffer_new (node->d.texture.verts->len);
cogl_vertex_buffer_add (vb, "gl_Vertex", 2, GL_FLOAT, FALSE,
sizeof (CoglPangoDisplayListVertex),
&g_array_index (node->d.texture.verts,
CoglPangoDisplayListVertex, 0).x);
cogl_vertex_buffer_add (vb, "gl_MultiTexCoord0", 2, GL_FLOAT, FALSE,
sizeof (CoglPangoDisplayListVertex),
&g_array_index (node->d.texture.verts,
CoglPangoDisplayListVertex,
0).t_x);
cogl_vertex_buffer_submit (vb);
node->d.texture.vertex_buffer = vb;
}
cogl_vertex_buffer_draw (node->d.texture.vertex_buffer,
COGL_PANGO_DISPLAY_LIST_DRAW_MODE,
0, node->d.texture.verts->len);
}
void
_cogl_pango_display_list_render (CoglPangoDisplayList *dl,
CoglHandle glyph_material,
CoglHandle solid_material)
{
GSList *l;
for (l = dl->nodes; l; l = l->next)
{
CoglPangoDisplayListNode *node = l->data;
switch (node->type)
{
case COGL_PANGO_DISPLAY_LIST_TEXTURE:
_cogl_pango_display_list_render_texture (glyph_material, node);
break;
case COGL_PANGO_DISPLAY_LIST_RECTANGLE:
cogl_material_set_color (solid_material, &node->color);
cogl_set_source (solid_material);
cogl_rectangle (node->d.rectangle.x_1,
node->d.rectangle.y_1,
node->d.rectangle.x_2,
node->d.rectangle.y_2);
break;
case COGL_PANGO_DISPLAY_LIST_TRAPEZOID:
{
float points[8];
points[0] = node->d.trapezoid.x_11;
points[1] = node->d.trapezoid.y_1;
points[2] = node->d.trapezoid.x_12;
points[3] = node->d.trapezoid.y_2;
points[4] = node->d.trapezoid.x_22;
points[5] = node->d.trapezoid.y_2;
points[6] = node->d.trapezoid.x_21;
points[7] = node->d.trapezoid.y_1;
cogl_material_set_color (solid_material, &node->color);
cogl_set_source (solid_material);
cogl_path_polygon (points, 4);
cogl_path_fill ();
}
break;
}
}
}
static void
_cogl_pango_display_list_node_free (CoglPangoDisplayListNode *node)
{
if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE)
{
g_array_free (node->d.texture.verts, TRUE);
if (node->d.texture.texture != COGL_INVALID_HANDLE)
cogl_texture_unref (node->d.texture.texture);
if (node->d.texture.vertex_buffer != COGL_INVALID_HANDLE)
cogl_vertex_buffer_unref (node->d.texture.vertex_buffer);
}
g_slice_free (CoglPangoDisplayListNode, node);
}
void
_cogl_pango_display_list_clear (CoglPangoDisplayList *dl)
{
g_slist_foreach (dl->nodes, (GFunc) _cogl_pango_display_list_node_free, NULL);
g_slist_free (dl->nodes);
dl->nodes = NULL;
dl->last_node = NULL;
}
void
_cogl_pango_display_list_free (CoglPangoDisplayList *dl)
{
_cogl_pango_display_list_clear (dl);
g_slice_free (CoglPangoDisplayList, dl);
}

View File

@ -0,0 +1,68 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Neil Roberts <neil@linux.intel.com>
*
* Copyright (C) 2009 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __COGL_PANGO_DISPLAY_LIST_H__
#define __COGL_PANGO_DISPLAY_LIST_H__
#include <glib.h>
#include <cogl/cogl.h>
G_BEGIN_DECLS
typedef struct _CoglPangoDisplayList CoglPangoDisplayList;
CoglPangoDisplayList *_cogl_pango_display_list_new (void);
void _cogl_pango_display_list_set_color (CoglPangoDisplayList *dl,
const CoglColor *color);
void _cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl,
CoglHandle 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);
void _cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl,
float x_1, float y_1,
float x_2, float y_2);
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);
void _cogl_pango_display_list_render (CoglPangoDisplayList *dl,
CoglHandle glyph_material,
CoglHandle solid_material);
void _cogl_pango_display_list_clear (CoglPangoDisplayList *dl);
void _cogl_pango_display_list_free (CoglPangoDisplayList *dl);
G_END_DECLS
#endif /* __COGL_PANGO_DISPLAY_LIST_H__ */

View File

@ -36,6 +36,7 @@
#include "cogl-pango-private.h"
#include "cogl-pango-glyph-cache.h"
#include "cogl-pango-display-list.h"
struct _CoglPangoRenderer
{
@ -55,9 +56,8 @@ struct _CoglPangoRenderer
gboolean use_mipmapping;
/* Array of rectangles to draw from the current texture */
GArray *glyph_rectangles;
CoglHandle glyph_texture;
/* The current display list that is being built */
CoglPangoDisplayList *display_list;
};
struct _CoglPangoRendererClass
@ -65,19 +65,18 @@ struct _CoglPangoRendererClass
PangoRendererClass class_instance;
};
static void
cogl_pango_renderer_glyphs_end (CoglPangoRenderer *priv)
typedef struct _CoglPangoRendererQdata CoglPangoRendererQdata;
/* An instance of this struct gets attached to each PangoLayout to
cache the VBO and to detect changes to the layout */
struct _CoglPangoRendererQdata
{
if (priv->glyph_rectangles->len > 0)
{
float *rectangles = (float *) priv->glyph_rectangles->data;
cogl_material_set_layer (priv->glyph_material, 0, priv->glyph_texture);
cogl_set_source (priv->glyph_material);
cogl_rectangles_with_texture_coords (rectangles,
priv->glyph_rectangles->len / 8);
g_array_set_size (priv->glyph_rectangles, 0);
}
}
/* The cache of the geometry for the layout */
CoglPangoDisplayList *display_list;
/* A reference to the first line of the layout. This is just used to
detect changes */
PangoLayoutLine *first_line;
};
static void
cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv,
@ -86,25 +85,19 @@ cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv,
float y1)
{
float x2, y2;
float *p;
if (priv->glyph_rectangles->len > 0
&& priv->glyph_texture != cache_value->texture)
cogl_pango_renderer_glyphs_end (priv);
priv->glyph_texture = cache_value->texture;
g_return_if_fail (priv->display_list != NULL);
x2 = x1 + (float) cache_value->draw_width;
y2 = y1 + (float) cache_value->draw_height;
g_array_set_size (priv->glyph_rectangles, priv->glyph_rectangles->len + 8);
p = &g_array_index (priv->glyph_rectangles, float,
priv->glyph_rectangles->len - 8);
*(p++) = x1; *(p++) = y1;
*(p++) = x2; *(p++) = y2;
*(p++) = cache_value->tx1; *(p++) = cache_value->ty1;
*(p++) = cache_value->tx2; *(p++) = cache_value->ty2;
_cogl_pango_display_list_add_texture (priv->display_list,
cache_value->texture,
x1, y1, x2, y2,
cache_value->tx1,
cache_value->ty1,
cache_value->tx2,
cache_value->ty2);
}
static void cogl_pango_renderer_finalize (GObject *object);
@ -168,7 +161,6 @@ cogl_pango_renderer_init (CoglPangoRenderer *priv)
priv->glyph_cache = cogl_pango_glyph_cache_new (FALSE);
priv->mipmapped_glyph_cache = cogl_pango_glyph_cache_new (TRUE);
priv->use_mipmapping = FALSE;
priv->glyph_rectangles = g_array_new (FALSE, FALSE, sizeof (float));
}
static void
@ -191,7 +183,6 @@ cogl_pango_renderer_finalize (GObject *object)
cogl_pango_glyph_cache_free (priv->mipmapped_glyph_cache);
cogl_pango_glyph_cache_free (priv->glyph_cache);
g_array_free (priv->glyph_rectangles, TRUE);
G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object);
}
@ -213,6 +204,27 @@ cogl_pango_get_renderer_from_context (PangoContext *context)
return COGL_PANGO_RENDERER (renderer);
}
static GQuark
cogl_pango_render_get_qdata_key (void)
{
static GQuark key = 0;
if (G_UNLIKELY (key == 0))
key = g_quark_from_static_string ("CoglPangoDisplayList");
return key;
}
static void
cogl_pango_render_qdata_destroy (CoglPangoRendererQdata *qdata)
{
if (qdata->display_list)
_cogl_pango_display_list_free (qdata->display_list);
if (qdata->first_line)
pango_layout_line_unref (qdata->first_line);
g_slice_free (CoglPangoRendererQdata, qdata);
}
/**
* cogl_pango_render_layout_subpixel:
* @layout: a #PangoLayout
@ -234,15 +246,65 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout,
{
PangoContext *context;
CoglPangoRenderer *priv;
CoglPangoRendererQdata *qdata;
context = pango_layout_get_context (layout);
priv = cogl_pango_get_renderer_from_context (context);
if (G_UNLIKELY (!priv))
return;
priv->color = *color;
qdata = g_object_get_qdata (G_OBJECT (layout),
cogl_pango_render_get_qdata_key ());
pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, x, y);
if (qdata == NULL)
{
qdata = g_slice_new0 (CoglPangoRendererQdata);
g_object_set_qdata_full (G_OBJECT (layout),
cogl_pango_render_get_qdata_key (),
qdata,
(GDestroyNotify)
cogl_pango_render_qdata_destroy);
}
/* Check if the layout has changed since the last build of the
display list. This trick was suggested by Behdad Esfahbod here:
http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html */
if (qdata->display_list && qdata->first_line
&& qdata->first_line->layout != layout)
{
_cogl_pango_display_list_free (qdata->display_list);
qdata->display_list = NULL;
}
if (qdata->display_list == NULL)
{
qdata->display_list = _cogl_pango_display_list_new ();
priv->color = *color;
priv->display_list = qdata->display_list;
pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0);
priv->display_list = NULL;
}
cogl_push_matrix ();
cogl_translate (x / (gfloat) PANGO_SCALE, y / (gfloat) PANGO_SCALE, 0);
_cogl_pango_display_list_render (qdata->display_list,
priv->glyph_material,
priv->solid_material);
cogl_pop_matrix ();
/* Keep a reference to the first line of the layout so we can detect
changes */
if (qdata->first_line)
{
pango_layout_line_unref (qdata->first_line);
qdata->first_line = NULL;
}
if (pango_layout_get_line_count (layout) > 0)
{
qdata->first_line = pango_layout_get_line (layout, 0);
pango_layout_line_ref (qdata->first_line);
}
}
/**
@ -296,9 +358,19 @@ cogl_pango_render_layout_line (PangoLayoutLine *line,
if (G_UNLIKELY (!priv))
return;
priv->display_list = _cogl_pango_display_list_new ();
priv->color = *color;
pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line, x, y);
cogl_material_set_color (priv->glyph_material, color);
cogl_material_set_color (priv->solid_material, color);
_cogl_pango_display_list_render (priv->display_list,
priv->glyph_material,
priv->solid_material);
_cogl_pango_display_list_free (priv->display_list);
priv->display_list = NULL;
}
void
@ -444,8 +516,7 @@ cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer,
else
color = priv->color;
cogl_material_set_color (priv->solid_material, &color);
cogl_material_set_color (priv->glyph_material, &color);
_cogl_pango_display_list_set_color (priv->display_list, &color);
}
static void
@ -457,12 +528,13 @@ cogl_pango_renderer_draw_box (PangoRenderer *renderer,
{
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
cogl_set_source (priv->solid_material);
cogl_path_rectangle (x,
g_return_if_fail (priv->display_list != NULL);
_cogl_pango_display_list_add_rectangle (priv->display_list,
x,
y - height,
x + width,
y);
cogl_path_stroke ();
}
static void
@ -500,6 +572,8 @@ cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer,
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
float x1, x2, y1, y2;
g_return_if_fail (priv->display_list != NULL);
cogl_pango_renderer_set_color_for_part (renderer, part);
cogl_pango_renderer_get_device_units (renderer,
@ -509,8 +583,8 @@ cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer,
x + width, y + height,
&x2, &y2);
cogl_set_source (priv->solid_material);
cogl_rectangle (x1, y1, x2, y2);
_cogl_pango_display_list_add_rectangle (priv->display_list,
x1, y1, x2, y2);
}
static void
@ -526,6 +600,8 @@ cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
float points[8];
g_return_if_fail (priv->display_list != NULL);
points[0] = (x11);
points[1] = (y1);
points[2] = (x12);
@ -537,9 +613,13 @@ cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
cogl_pango_renderer_set_color_for_part (renderer, part);
cogl_set_source (priv->solid_material);
cogl_path_polygon (points, 4);
cogl_path_fill ();
_cogl_pango_display_list_add_trapezoid (priv->display_list,
y1,
x11,
x21,
y2,
x12,
x22);
}
static void
@ -570,8 +650,6 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
{
PangoFontMetrics *metrics;
cogl_pango_renderer_glyphs_end (priv);
if (font == NULL ||
(metrics = pango_font_get_metrics (font, NULL)) == NULL)
{
@ -603,15 +681,11 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
gi->glyph);
if (cache_value == NULL)
{
cogl_pango_renderer_glyphs_end (priv);
cogl_pango_renderer_draw_box (renderer,
x,
y,
PANGO_UNKNOWN_GLYPH_WIDTH,
PANGO_UNKNOWN_GLYPH_HEIGHT);
}
else
{
float width, height;
@ -628,6 +702,4 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
xi += gi->geometry.width;
}
cogl_pango_renderer_glyphs_end (priv);
}