4
* An OpenGL based 'interactive canvas' library.
6
* Authored By Matthew Allum <mallum@openedhand.com>
8
* Copyright (C) 2008 OpenedHand
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/>.
28
#ifndef PANGO_ENABLE_BACKEND
29
#define PANGO_ENABLE_BACKEND 1
32
#include <pango/pango-fontmap.h>
33
#include <pango/pangocairo.h>
34
#include <pango/pango-renderer.h>
37
#include "cogl-pango-private.h"
38
#include "cogl-pango-glyph-cache.h"
39
#include "cogl-pango-display-list.h"
41
struct _CoglPangoRenderer
43
PangoRenderer parent_instance;
45
/* The material used to texture from the glyph cache with */
46
CoglHandle glyph_material;
47
/* The material used for solid fills. (boxes, rectangles + trapezoids) */
48
CoglHandle solid_material;
50
/* Caches of glyphs as textures */
51
CoglPangoGlyphCache *glyph_cache;
53
/* The current display list that is being built */
54
CoglPangoDisplayList *display_list;
57
struct _CoglPangoRendererClass
59
PangoRendererClass class_instance;
62
typedef struct _CoglPangoRendererQdata CoglPangoRendererQdata;
64
/* An instance of this struct gets attached to each PangoLayout to
65
cache the VBO and to detect changes to the layout */
66
struct _CoglPangoRendererQdata
68
/* The cache of the geometry for the layout */
69
CoglPangoDisplayList *display_list;
70
/* A reference to the first line of the layout. This is just used to
72
PangoLayoutLine *first_line;
76
cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv,
77
CoglPangoGlyphCacheValue *cache_value,
83
g_return_if_fail (priv->display_list != NULL);
85
x2 = x1 + (float) cache_value->draw_width;
86
y2 = y1 + (float) cache_value->draw_height;
88
_cogl_pango_display_list_add_texture (priv->display_list,
97
static void cogl_pango_renderer_finalize (GObject *object);
98
static void cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
100
PangoGlyphString *glyphs,
103
static void cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer,
104
PangoRenderPart part,
109
static void cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
110
PangoRenderPart part,
118
G_DEFINE_TYPE (CoglPangoRenderer, cogl_pango_renderer, PANGO_TYPE_RENDERER);
121
cogl_pango_renderer_init (CoglPangoRenderer *priv)
123
priv->glyph_material = cogl_material_new ();
125
/* The default combine mode of materials is to modulate (A x B) the texture
126
* RGBA channels with the RGBA channels of the previous layer (which in our
127
* case is just the font color)
129
* Since the RGB for an alpha texture is defined as 0, this gives us:
131
* result.rgb = color.rgb * 0
132
* result.a = color.a * texture.a
134
* What we want is premultiplied rgba values:
136
* result.rgba = color.rgb * texture.a
137
* result.a = color.a * texture.a
139
cogl_material_set_layer_combine (priv->glyph_material, 0, /* layer */
140
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
143
priv->solid_material = cogl_material_new ();
145
priv->glyph_cache = cogl_pango_glyph_cache_new ();
147
_cogl_pango_renderer_set_use_mipmapping (priv, FALSE);
151
cogl_pango_renderer_class_init (CoglPangoRendererClass *klass)
153
GObjectClass *object_class = G_OBJECT_CLASS (klass);
154
PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
156
object_class->finalize = cogl_pango_renderer_finalize;
158
renderer_class->draw_glyphs = cogl_pango_renderer_draw_glyphs;
159
renderer_class->draw_rectangle = cogl_pango_renderer_draw_rectangle;
160
renderer_class->draw_trapezoid = cogl_pango_renderer_draw_trapezoid;
164
cogl_pango_renderer_finalize (GObject *object)
166
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (object);
168
cogl_pango_glyph_cache_free (priv->glyph_cache);
170
G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object);
173
static CoglPangoRenderer *
174
cogl_pango_get_renderer_from_context (PangoContext *context)
176
PangoFontMap *font_map;
177
PangoRenderer *renderer;
178
CoglPangoFontMap *font_map_priv;
180
font_map = pango_context_get_font_map (context);
181
g_return_val_if_fail (COGL_PANGO_IS_FONT_MAP (font_map), NULL);
183
font_map_priv = COGL_PANGO_FONT_MAP (font_map);
184
renderer = cogl_pango_font_map_get_renderer (font_map_priv);
185
g_return_val_if_fail (COGL_PANGO_IS_RENDERER (renderer), NULL);
187
return COGL_PANGO_RENDERER (renderer);
191
cogl_pango_render_get_qdata_key (void)
193
static GQuark key = 0;
195
if (G_UNLIKELY (key == 0))
196
key = g_quark_from_static_string ("CoglPangoDisplayList");
202
cogl_pango_render_qdata_destroy (CoglPangoRendererQdata *qdata)
204
if (qdata->display_list)
205
_cogl_pango_display_list_free (qdata->display_list);
206
if (qdata->first_line)
207
pango_layout_line_unref (qdata->first_line);
208
g_slice_free (CoglPangoRendererQdata, qdata);
212
* cogl_pango_render_layout_subpixel:
213
* @layout: a #PangoLayout
216
* @color: color to use when rendering the layout
217
* @flags: flags to pass to the renderer
224
cogl_pango_render_layout_subpixel (PangoLayout *layout,
227
const CoglColor *color,
230
PangoContext *context;
231
CoglPangoRenderer *priv;
232
CoglPangoRendererQdata *qdata;
234
context = pango_layout_get_context (layout);
235
priv = cogl_pango_get_renderer_from_context (context);
236
if (G_UNLIKELY (!priv))
239
qdata = g_object_get_qdata (G_OBJECT (layout),
240
cogl_pango_render_get_qdata_key ());
244
qdata = g_slice_new0 (CoglPangoRendererQdata);
245
g_object_set_qdata_full (G_OBJECT (layout),
246
cogl_pango_render_get_qdata_key (),
249
cogl_pango_render_qdata_destroy);
252
/* Check if the layout has changed since the last build of the
253
display list. This trick was suggested by Behdad Esfahbod here:
254
http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html */
255
if (qdata->display_list && qdata->first_line
256
&& qdata->first_line->layout != layout)
258
_cogl_pango_display_list_free (qdata->display_list);
259
qdata->display_list = NULL;
262
if (qdata->display_list == NULL)
264
qdata->display_list = _cogl_pango_display_list_new ();
266
priv->display_list = qdata->display_list;
267
pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0);
268
priv->display_list = NULL;
272
cogl_translate (x / (gfloat) PANGO_SCALE, y / (gfloat) PANGO_SCALE, 0);
273
_cogl_pango_display_list_render (qdata->display_list,
275
priv->glyph_material,
276
priv->solid_material);
279
/* Keep a reference to the first line of the layout so we can detect
281
if (qdata->first_line)
283
pango_layout_line_unref (qdata->first_line);
284
qdata->first_line = NULL;
286
if (pango_layout_get_line_count (layout) > 0)
288
qdata->first_line = pango_layout_get_line (layout, 0);
289
pango_layout_line_ref (qdata->first_line);
294
* cogl_pango_render_layout:
295
* @layout: a #PangoLayout
296
* @x: X coordinate to render the layout at
297
* @y: Y coordinate to render the layout at
298
* @color: color to use when rendering the layout
299
* @flags: flags to pass to the renderer
306
cogl_pango_render_layout (PangoLayout *layout,
309
const CoglColor *color,
312
cogl_pango_render_layout_subpixel (layout,
320
* cogl_pango_render_layout_line:
321
* @line: a #PangoLayoutLine
322
* @x: X coordinate to render the line at
323
* @y: Y coordinate to render the line at
324
* @color: color to use when rendering the line
326
* Renders @line at the given coordinates using the given color.
331
cogl_pango_render_layout_line (PangoLayoutLine *line,
334
const CoglColor *color)
336
PangoContext *context;
337
CoglPangoRenderer *priv;
339
context = pango_layout_get_context (line->layout);
340
priv = cogl_pango_get_renderer_from_context (context);
341
if (G_UNLIKELY (!priv))
344
priv->display_list = _cogl_pango_display_list_new ();
346
pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line, x, y);
348
_cogl_pango_display_list_render (priv->display_list,
350
priv->glyph_material,
351
priv->solid_material);
353
_cogl_pango_display_list_free (priv->display_list);
354
priv->display_list = NULL;
358
_cogl_pango_renderer_clear_glyph_cache (CoglPangoRenderer *renderer)
360
cogl_pango_glyph_cache_clear (renderer->glyph_cache);
364
_cogl_pango_renderer_set_use_mipmapping (CoglPangoRenderer *renderer,
368
cogl_material_set_layer_filters (renderer->glyph_material, 0,
369
COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR,
370
COGL_MATERIAL_FILTER_LINEAR);
372
cogl_material_set_layer_filters (renderer->glyph_material, 0,
373
COGL_MATERIAL_FILTER_LINEAR,
374
COGL_MATERIAL_FILTER_LINEAR);
378
_cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer)
380
const GList *layers = cogl_material_get_layers (renderer->glyph_material);
382
g_return_val_if_fail (layers != NULL, FALSE);
384
return (cogl_material_layer_get_min_filter (layers->data)
385
== COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR);
388
static CoglPangoGlyphCacheValue *
389
cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer,
393
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
394
CoglPangoGlyphCacheValue *value;
396
value = cogl_pango_glyph_cache_lookup (priv->glyph_cache, font, glyph);
399
cairo_surface_t *surface;
401
cairo_scaled_font_t *scaled_font;
402
PangoRectangle ink_rect;
403
cairo_glyph_t cairo_glyph;
405
pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
406
pango_extents_to_pixels (&ink_rect, NULL);
408
surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
411
cr = cairo_create (surface);
413
scaled_font = pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (font));
414
cairo_set_scaled_font (cr, scaled_font);
416
cairo_glyph.x = -ink_rect.x;
417
cairo_glyph.y = -ink_rect.y;
418
/* The PangoCairo glyph numbers directly map to Cairo glyph
420
cairo_glyph.index = glyph;
421
cairo_show_glyphs (cr, &cairo_glyph, 1);
424
cairo_surface_flush (surface);
426
/* Copy the glyph to the cache */
428
cogl_pango_glyph_cache_set (priv->glyph_cache, font, glyph,
429
cairo_image_surface_get_data (surface),
430
cairo_image_surface_get_width (surface),
431
cairo_image_surface_get_height (surface),
432
cairo_image_surface_get_stride (surface),
433
ink_rect.x, ink_rect.y);
435
cairo_surface_destroy (surface);
437
COGL_NOTE (PANGO, "cache fail %i", glyph);
441
COGL_NOTE (PANGO, "cache success %i", glyph);
448
cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout)
450
PangoContext *context;
451
PangoRenderer *renderer;
452
PangoLayoutIter *iter;
454
g_return_if_fail (PANGO_IS_LAYOUT (layout));
456
if ((iter = pango_layout_get_iter (layout)) == NULL)
459
context = pango_layout_get_context (layout);
461
PANGO_RENDERER (cogl_pango_get_renderer_from_context (context));
465
PangoLayoutLine *line;
468
line = pango_layout_iter_get_line_readonly (iter);
470
for (l = line->runs; l; l = l->next)
472
PangoLayoutRun *run = l->data;
473
PangoGlyphString *glyphs = run->glyphs;
476
for (i = 0; i < glyphs->num_glyphs; i++)
478
PangoGlyphInfo *gi = &glyphs->glyphs[i];
480
cogl_pango_renderer_get_cached_glyph (renderer,
481
run->item->analysis.font,
486
while (pango_layout_iter_next_line (iter));
488
pango_layout_iter_free (iter);
492
cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer,
493
PangoRenderPart part)
495
PangoColor *pango_color = pango_renderer_get_color (renderer, part);
496
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
502
cogl_color_set_from_4ub (&color,
503
pango_color->red >> 8,
504
pango_color->green >> 8,
505
pango_color->blue >> 8,
508
_cogl_pango_display_list_set_color_override (priv->display_list, &color);
511
_cogl_pango_display_list_remove_color_override (priv->display_list);
515
cogl_pango_renderer_draw_box (PangoRenderer *renderer,
521
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
523
g_return_if_fail (priv->display_list != NULL);
525
_cogl_pango_display_list_add_rectangle (priv->display_list,
533
cogl_pango_renderer_get_device_units (PangoRenderer *renderer,
539
const PangoMatrix *matrix;
541
if ((matrix = pango_renderer_get_matrix (renderer)))
543
/* Convert user-space coords to device coords */
544
*xout = ((xin * matrix->xx + yin * matrix->xy)
545
/ PANGO_SCALE + matrix->x0);
546
*yout = ((yin * matrix->yy + xin * matrix->yx)
547
/ PANGO_SCALE + matrix->y0);
551
*xout = PANGO_PIXELS (xin);
552
*yout = PANGO_PIXELS (yin);
557
cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer,
558
PangoRenderPart part,
564
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
565
float x1, x2, y1, y2;
567
g_return_if_fail (priv->display_list != NULL);
569
cogl_pango_renderer_set_color_for_part (renderer, part);
571
cogl_pango_renderer_get_device_units (renderer,
574
cogl_pango_renderer_get_device_units (renderer,
575
x + width, y + height,
578
_cogl_pango_display_list_add_rectangle (priv->display_list,
583
cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
584
PangoRenderPart part,
592
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
595
g_return_if_fail (priv->display_list != NULL);
602
points[5] = points[3];
604
points[7] = points[1];
606
cogl_pango_renderer_set_color_for_part (renderer, part);
608
_cogl_pango_display_list_add_trapezoid (priv->display_list,
618
cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
620
PangoGlyphString *glyphs,
624
CoglPangoRenderer *priv = (CoglPangoRenderer *) renderer;
625
CoglPangoGlyphCacheValue *cache_value;
628
cogl_pango_renderer_set_color_for_part (renderer,
629
PANGO_RENDER_PART_FOREGROUND);
631
for (i = 0; i < glyphs->num_glyphs; i++)
633
PangoGlyphInfo *gi = glyphs->glyphs + i;
636
cogl_pango_renderer_get_device_units (renderer,
637
xi + gi->geometry.x_offset,
638
yi + gi->geometry.y_offset,
641
if ((gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
643
PangoFontMetrics *metrics;
646
(metrics = pango_font_get_metrics (font, NULL)) == NULL)
648
cogl_pango_renderer_draw_box (renderer,
651
PANGO_UNKNOWN_GLYPH_WIDTH,
652
PANGO_UNKNOWN_GLYPH_HEIGHT);
656
cogl_pango_renderer_draw_box (renderer,
659
metrics->approximate_char_width
661
metrics->ascent / PANGO_SCALE);
663
pango_font_metrics_unref (metrics);
668
/* Get the texture containing the glyph. This will create
669
the cache entry if there isn't already one */
671
cogl_pango_renderer_get_cached_glyph (renderer,
675
if (cache_value == NULL)
676
cogl_pango_renderer_draw_box (renderer,
679
PANGO_UNKNOWN_GLYPH_WIDTH,
680
PANGO_UNKNOWN_GLYPH_HEIGHT);
685
x += (float)(cache_value->draw_x);
686
y += (float)(cache_value->draw_y);
688
width = x + (float)(cache_value->draw_width);
689
height = y + (float)(cache_value->draw_height);
691
cogl_pango_renderer_draw_glyph (priv, cache_value, x, y);
695
xi += gi->geometry.width;