1 /*
2 * Cogl
3 *
4 * A Low Level GPU Graphics and Utilities API
5 *
6 * Copyright (C) 2008 OpenedHand
7 * Copyright (C) 2012 Intel Corporation.
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use, copy,
13 * modify, merge, publish, distribute, sublicense, and/or sell copies
14 * of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 *
29 *
30 * Authors:
31 * Neil Roberts <neil@linux.intel.com>
32 * Robert Bragg <robert@linux.intel.com>
33 * Matthew Allum <mallum@openedhand.com>
34 */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #ifndef PANGO_ENABLE_BACKEND
41 #define PANGO_ENABLE_BACKEND 1
42 #endif
43
44 #ifndef PANGO_UNKNOWN_GLYPH_WIDTH
45 #define PANGO_UNKNOWN_GLYPH_WIDTH 10
46 #endif
47 #ifndef PANGO_UNKNOWN_GLYPH_HEIGHT
48 #define PANGO_UNKNOWN_GLYPH_HEIGHT 14
49 #endif
50
51 #include <pango/pango-fontmap.h>
52 #include <pango/pangocairo.h>
53 #include <pango/pango-renderer.h>
54 #include <cairo.h>
55
56 #include "cogl/cogl-debug.h"
57 #include "cogl/cogl-context-private.h"
58 #include "cogl/cogl-texture-private.h"
59 #include "cogl-pango-private.h"
60 #include "cogl-pango-glyph-cache.h"
61 #include "cogl-pango-display-list.h"
62
63 enum
64 {
65 PROP_0,
66
67 PROP_COGL_CONTEXT,
68 PROP_LAST
69 };
70
71 typedef struct
72 {
73 CoglPangoGlyphCache *glyph_cache;
74 CoglPangoPipelineCache *pipeline_cache;
75 } CoglPangoRendererCaches;
76
77 struct _CoglPangoRenderer
78 {
79 PangoRenderer parent_instance;
80
81 CoglContext *ctx;
82
83 /* Two caches of glyphs as textures and their corresponding pipeline
84 caches, one with mipmapped textures and one without */
85 CoglPangoRendererCaches no_mipmap_caches;
86 CoglPangoRendererCaches mipmap_caches;
87
88 CoglBool use_mipmapping;
89
90 /* The current display list that is being built */
91 CoglPangoDisplayList *display_list;
92 };
93
94 struct _CoglPangoRendererClass
95 {
96 PangoRendererClass class_instance;
97 };
98
99 typedef struct _CoglPangoLayoutQdata CoglPangoLayoutQdata;
100
101 /* An instance of this struct gets attached to each PangoLayout to
102 cache the VBO and to detect changes to the layout */
103 struct _CoglPangoLayoutQdata
104 {
105 CoglPangoRenderer *renderer;
106 /* The cache of the geometry for the layout */
107 CoglPangoDisplayList *display_list;
108 /* A reference to the first line of the layout. This is just used to
109 detect changes */
110 PangoLayoutLine *first_line;
111 /* Whether mipmapping was previously used to render this layout. We
112 need to regenerate the display list if the mipmapping value is
113 changed because it will be using a different set of textures */
114 CoglBool mipmapping_used;
115 };
116
117 static void
118 _cogl_pango_ensure_glyph_cache_for_layout_line (PangoLayoutLine *line);
119
120 typedef struct
121 {
122 CoglPangoDisplayList *display_list;
123 float x1, y1, x2, y2;
124 } CoglPangoRendererSliceCbData;
125
126 PangoRenderer *
_cogl_pango_renderer_new(CoglContext * context)127 _cogl_pango_renderer_new (CoglContext *context)
128 {
129 return PANGO_RENDERER (g_object_new (COGL_PANGO_TYPE_RENDERER,
130 "context", context, NULL));
131 }
132
133 static void
cogl_pango_renderer_slice_cb(CoglTexture * texture,const float * slice_coords,const float * virtual_coords,void * user_data)134 cogl_pango_renderer_slice_cb (CoglTexture *texture,
135 const float *slice_coords,
136 const float *virtual_coords,
137 void *user_data)
138 {
139 CoglPangoRendererSliceCbData *data = user_data;
140
141 /* Note: this assumes that there is only one slice containing the
142 whole texture and it doesn't attempt to split up the vertex
143 coordinates based on the virtual_coords */
144
145 _cogl_pango_display_list_add_texture (data->display_list,
146 texture,
147 data->x1,
148 data->y1,
149 data->x2,
150 data->y2,
151 slice_coords[0],
152 slice_coords[1],
153 slice_coords[2],
154 slice_coords[3]);
155 }
156
157 static void
cogl_pango_renderer_draw_glyph(CoglPangoRenderer * priv,CoglPangoGlyphCacheValue * cache_value,float x1,float y1)158 cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv,
159 CoglPangoGlyphCacheValue *cache_value,
160 float x1,
161 float y1)
162 {
163 CoglPangoRendererSliceCbData data;
164
165 _COGL_RETURN_IF_FAIL (priv->display_list != NULL);
166
167 data.display_list = priv->display_list;
168 data.x1 = x1;
169 data.y1 = y1;
170 data.x2 = x1 + (float) cache_value->draw_width;
171 data.y2 = y1 + (float) cache_value->draw_height;
172
173 /* We iterate the internal sub textures of the texture so that we
174 can get a pointer to the base texture even if the texture is in
175 the global atlas. That way the display list can recognise that
176 the neighbouring glyphs are coming from the same atlas and bundle
177 them together into a single VBO */
178
179 cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (cache_value->texture),
180 cache_value->tx1,
181 cache_value->ty1,
182 cache_value->tx2,
183 cache_value->ty2,
184 COGL_PIPELINE_WRAP_MODE_REPEAT,
185 COGL_PIPELINE_WRAP_MODE_REPEAT,
186 cogl_pango_renderer_slice_cb,
187 &data);
188 }
189
190 static void cogl_pango_renderer_dispose (GObject *object);
191 static void cogl_pango_renderer_finalize (GObject *object);
192 static void cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
193 PangoFont *font,
194 PangoGlyphString *glyphs,
195 int x,
196 int y);
197 static void cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer,
198 PangoRenderPart part,
199 int x,
200 int y,
201 int width,
202 int height);
203 static void cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
204 PangoRenderPart part,
205 double y1,
206 double x11,
207 double x21,
208 double y2,
209 double x12,
210 double x22);
211
212 G_DEFINE_TYPE (CoglPangoRenderer, cogl_pango_renderer, PANGO_TYPE_RENDERER);
213
214 static void
cogl_pango_renderer_init(CoglPangoRenderer * priv)215 cogl_pango_renderer_init (CoglPangoRenderer *priv)
216 {
217 }
218
219 static void
_cogl_pango_renderer_constructed(GObject * gobject)220 _cogl_pango_renderer_constructed (GObject *gobject)
221 {
222 CoglPangoRenderer *renderer = COGL_PANGO_RENDERER (gobject);
223 CoglContext *ctx = renderer->ctx;
224
225 renderer->no_mipmap_caches.pipeline_cache =
226 _cogl_pango_pipeline_cache_new (ctx, FALSE);
227 renderer->mipmap_caches.pipeline_cache =
228 _cogl_pango_pipeline_cache_new (ctx, TRUE);
229
230 renderer->no_mipmap_caches.glyph_cache =
231 cogl_pango_glyph_cache_new (ctx, FALSE);
232 renderer->mipmap_caches.glyph_cache =
233 cogl_pango_glyph_cache_new (ctx, TRUE);
234
235 _cogl_pango_renderer_set_use_mipmapping (renderer, FALSE);
236
237 if (G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->constructed)
238 G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->constructed (gobject);
239 }
240
241 static void
cogl_pango_renderer_set_property(GObject * object,unsigned int prop_id,const GValue * value,GParamSpec * pspec)242 cogl_pango_renderer_set_property (GObject *object,
243 unsigned int prop_id,
244 const GValue *value,
245 GParamSpec *pspec)
246 {
247 CoglPangoRenderer *renderer = COGL_PANGO_RENDERER (object);
248
249 switch (prop_id)
250 {
251 case PROP_COGL_CONTEXT:
252 renderer->ctx = g_value_get_pointer (value);
253 cogl_object_ref (renderer->ctx);
254 break;
255 default:
256 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
257 break;
258 }
259 }
260
261 static void
cogl_pango_renderer_class_init(CoglPangoRendererClass * klass)262 cogl_pango_renderer_class_init (CoglPangoRendererClass *klass)
263 {
264 GObjectClass *object_class = G_OBJECT_CLASS (klass);
265 PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
266 GParamSpec *pspec;
267
268 object_class->set_property = cogl_pango_renderer_set_property;
269 object_class->constructed = _cogl_pango_renderer_constructed;
270 object_class->dispose = cogl_pango_renderer_dispose;
271 object_class->finalize = cogl_pango_renderer_finalize;
272
273 pspec = g_param_spec_pointer ("context",
274 "Context",
275 "The Cogl Context",
276 G_PARAM_WRITABLE |
277 G_PARAM_STATIC_STRINGS |
278 G_PARAM_CONSTRUCT_ONLY);
279
280 g_object_class_install_property (object_class, PROP_COGL_CONTEXT, pspec);
281
282 renderer_class->draw_glyphs = cogl_pango_renderer_draw_glyphs;
283 renderer_class->draw_rectangle = cogl_pango_renderer_draw_rectangle;
284 renderer_class->draw_trapezoid = cogl_pango_renderer_draw_trapezoid;
285 }
286
287 static void
cogl_pango_renderer_dispose(GObject * object)288 cogl_pango_renderer_dispose (GObject *object)
289 {
290 CoglPangoRenderer *priv = COGL_PANGO_RENDERER (object);
291
292 if (priv->ctx)
293 {
294 cogl_object_unref (priv->ctx);
295 priv->ctx = NULL;
296 }
297 }
298
299 static void
cogl_pango_renderer_finalize(GObject * object)300 cogl_pango_renderer_finalize (GObject *object)
301 {
302 CoglPangoRenderer *priv = COGL_PANGO_RENDERER (object);
303
304 cogl_pango_glyph_cache_free (priv->no_mipmap_caches.glyph_cache);
305 cogl_pango_glyph_cache_free (priv->mipmap_caches.glyph_cache);
306
307 _cogl_pango_pipeline_cache_free (priv->no_mipmap_caches.pipeline_cache);
308 _cogl_pango_pipeline_cache_free (priv->mipmap_caches.pipeline_cache);
309
310 G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object);
311 }
312
313 static CoglPangoRenderer *
cogl_pango_get_renderer_from_context(PangoContext * context)314 cogl_pango_get_renderer_from_context (PangoContext *context)
315 {
316 PangoFontMap *font_map;
317 CoglPangoFontMap *cogl_font_map;
318 PangoRenderer *renderer;
319
320 font_map = pango_context_get_font_map (context);
321 g_return_val_if_fail (COGL_PANGO_IS_FONT_MAP (font_map), NULL);
322
323 cogl_font_map = COGL_PANGO_FONT_MAP (font_map);
324
325 renderer = _cogl_pango_font_map_get_renderer (cogl_font_map);
326
327 g_return_val_if_fail (COGL_PANGO_IS_RENDERER (renderer), NULL);
328
329 return COGL_PANGO_RENDERER (renderer);
330 }
331
332 static GQuark
cogl_pango_layout_get_qdata_key(void)333 cogl_pango_layout_get_qdata_key (void)
334 {
335 static GQuark key = 0;
336
337 if (G_UNLIKELY (key == 0))
338 key = g_quark_from_static_string ("CoglPangoDisplayList");
339
340 return key;
341 }
342
343 static void
cogl_pango_layout_qdata_forget_display_list(CoglPangoLayoutQdata * qdata)344 cogl_pango_layout_qdata_forget_display_list (CoglPangoLayoutQdata *qdata)
345 {
346 if (qdata->display_list)
347 {
348 CoglPangoRendererCaches *caches = qdata->mipmapping_used ?
349 &qdata->renderer->mipmap_caches :
350 &qdata->renderer->no_mipmap_caches;
351
352 _cogl_pango_glyph_cache_remove_reorganize_callback
353 (caches->glyph_cache,
354 (GHookFunc) cogl_pango_layout_qdata_forget_display_list,
355 qdata);
356
357 _cogl_pango_display_list_free (qdata->display_list);
358
359 qdata->display_list = NULL;
360 }
361 }
362
363 static void
cogl_pango_render_qdata_destroy(CoglPangoLayoutQdata * qdata)364 cogl_pango_render_qdata_destroy (CoglPangoLayoutQdata *qdata)
365 {
366 cogl_pango_layout_qdata_forget_display_list (qdata);
367 if (qdata->first_line)
368 pango_layout_line_unref (qdata->first_line);
369 g_slice_free (CoglPangoLayoutQdata, qdata);
370 }
371
372 void
cogl_pango_show_layout(CoglFramebuffer * fb,PangoLayout * layout,float x,float y,const CoglColor * color)373 cogl_pango_show_layout (CoglFramebuffer *fb,
374 PangoLayout *layout,
375 float x,
376 float y,
377 const CoglColor *color)
378 {
379 PangoContext *context;
380 CoglPangoRenderer *priv;
381 CoglPangoLayoutQdata *qdata;
382
383 context = pango_layout_get_context (layout);
384 priv = cogl_pango_get_renderer_from_context (context);
385 if (G_UNLIKELY (!priv))
386 return;
387
388 qdata = g_object_get_qdata (G_OBJECT (layout),
389 cogl_pango_layout_get_qdata_key ());
390
391 if (qdata == NULL)
392 {
393 qdata = g_slice_new0 (CoglPangoLayoutQdata);
394 qdata->renderer = priv;
395 g_object_set_qdata_full (G_OBJECT (layout),
396 cogl_pango_layout_get_qdata_key (),
397 qdata,
398 (GDestroyNotify)
399 cogl_pango_render_qdata_destroy);
400 }
401
402 /* Check if the layout has changed since the last build of the
403 display list. This trick was suggested by Behdad Esfahbod here:
404 http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html */
405 if (qdata->display_list &&
406 ((qdata->first_line &&
407 qdata->first_line->layout != layout) ||
408 qdata->mipmapping_used != priv->use_mipmapping))
409 cogl_pango_layout_qdata_forget_display_list (qdata);
410
411 if (qdata->display_list == NULL)
412 {
413 CoglPangoRendererCaches *caches = priv->use_mipmapping ?
414 &priv->mipmap_caches :
415 &priv->no_mipmap_caches;
416
417 cogl_pango_ensure_glyph_cache_for_layout (layout);
418
419 qdata->display_list =
420 _cogl_pango_display_list_new (caches->pipeline_cache);
421
422 /* Register for notification of when the glyph cache changes so
423 we can rebuild the display list */
424 _cogl_pango_glyph_cache_add_reorganize_callback
425 (caches->glyph_cache,
426 (GHookFunc) cogl_pango_layout_qdata_forget_display_list,
427 qdata);
428
429 priv->display_list = qdata->display_list;
430 pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0);
431 priv->display_list = NULL;
432
433 qdata->mipmapping_used = priv->use_mipmapping;
434 }
435
436 cogl_framebuffer_push_matrix (fb);
437 cogl_framebuffer_translate (fb, x, y, 0);
438
439 _cogl_pango_display_list_render (fb,
440 qdata->display_list,
441 color);
442
443 cogl_framebuffer_pop_matrix (fb);
444
445 /* Keep a reference to the first line of the layout so we can detect
446 changes */
447 if (qdata->first_line)
448 {
449 pango_layout_line_unref (qdata->first_line);
450 qdata->first_line = NULL;
451 }
452 if (pango_layout_get_line_count (layout) > 0)
453 {
454 qdata->first_line = pango_layout_get_line (layout, 0);
455 pango_layout_line_ref (qdata->first_line);
456 }
457 }
458
459 void
cogl_pango_render_layout_subpixel(PangoLayout * layout,int x,int y,const CoglColor * color,int flags)460 cogl_pango_render_layout_subpixel (PangoLayout *layout,
461 int x,
462 int y,
463 const CoglColor *color,
464 int flags)
465 {
466 cogl_pango_show_layout (cogl_get_draw_framebuffer (),
467 layout,
468 x / (float) PANGO_SCALE,
469 y / (float) PANGO_SCALE,
470 color);
471 }
472
473 void
cogl_pango_render_layout(PangoLayout * layout,int x,int y,const CoglColor * color,int flags)474 cogl_pango_render_layout (PangoLayout *layout,
475 int x,
476 int y,
477 const CoglColor *color,
478 int flags)
479 {
480 cogl_pango_render_layout_subpixel (layout,
481 x * PANGO_SCALE,
482 y * PANGO_SCALE,
483 color,
484 flags);
485 }
486
487 void
cogl_pango_show_layout_line(CoglFramebuffer * fb,PangoLayoutLine * line,float x,float y,const CoglColor * color)488 cogl_pango_show_layout_line (CoglFramebuffer *fb,
489 PangoLayoutLine *line,
490 float x,
491 float y,
492 const CoglColor *color)
493 {
494 PangoContext *context;
495 CoglPangoRenderer *priv;
496 CoglPangoRendererCaches *caches;
497 int pango_x = x * PANGO_SCALE;
498 int pango_y = y * PANGO_SCALE;
499
500 context = pango_layout_get_context (line->layout);
501 priv = cogl_pango_get_renderer_from_context (context);
502 if (G_UNLIKELY (!priv))
503 return;
504
505 caches = (priv->use_mipmapping ?
506 &priv->mipmap_caches :
507 &priv->no_mipmap_caches);
508
509 priv->display_list = _cogl_pango_display_list_new (caches->pipeline_cache);
510
511 _cogl_pango_ensure_glyph_cache_for_layout_line (line);
512
513 pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line,
514 pango_x, pango_y);
515
516 _cogl_pango_display_list_render (fb,
517 priv->display_list,
518 color);
519
520 _cogl_pango_display_list_free (priv->display_list);
521 priv->display_list = NULL;
522 }
523
524 void
cogl_pango_render_layout_line(PangoLayoutLine * line,int x,int y,const CoglColor * color)525 cogl_pango_render_layout_line (PangoLayoutLine *line,
526 int x,
527 int y,
528 const CoglColor *color)
529 {
530 cogl_pango_show_layout_line (cogl_get_draw_framebuffer (),
531 line,
532 x / (float) PANGO_SCALE,
533 y / (float) PANGO_SCALE,
534 color);
535 }
536
537 void
_cogl_pango_renderer_clear_glyph_cache(CoglPangoRenderer * renderer)538 _cogl_pango_renderer_clear_glyph_cache (CoglPangoRenderer *renderer)
539 {
540 cogl_pango_glyph_cache_clear (renderer->mipmap_caches.glyph_cache);
541 cogl_pango_glyph_cache_clear (renderer->no_mipmap_caches.glyph_cache);
542 }
543
544 void
_cogl_pango_renderer_set_use_mipmapping(CoglPangoRenderer * renderer,CoglBool value)545 _cogl_pango_renderer_set_use_mipmapping (CoglPangoRenderer *renderer,
546 CoglBool value)
547 {
548 renderer->use_mipmapping = value;
549 }
550
551 CoglBool
_cogl_pango_renderer_get_use_mipmapping(CoglPangoRenderer * renderer)552 _cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer)
553 {
554 return renderer->use_mipmapping;
555 }
556
557 static CoglPangoGlyphCacheValue *
cogl_pango_renderer_get_cached_glyph(PangoRenderer * renderer,CoglBool create,PangoFont * font,PangoGlyph glyph)558 cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer,
559 CoglBool create,
560 PangoFont *font,
561 PangoGlyph glyph)
562 {
563 CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
564 CoglPangoRendererCaches *caches = (priv->use_mipmapping ?
565 &priv->mipmap_caches :
566 &priv->no_mipmap_caches);
567
568 return cogl_pango_glyph_cache_lookup (caches->glyph_cache,
569 create, font, glyph);
570 }
571
572 static void
cogl_pango_renderer_set_dirty_glyph(PangoFont * font,PangoGlyph glyph,CoglPangoGlyphCacheValue * value)573 cogl_pango_renderer_set_dirty_glyph (PangoFont *font,
574 PangoGlyph glyph,
575 CoglPangoGlyphCacheValue *value)
576 {
577 cairo_surface_t *surface;
578 cairo_t *cr;
579 cairo_scaled_font_t *scaled_font;
580 cairo_glyph_t cairo_glyph;
581 cairo_format_t format_cairo;
582 CoglPixelFormat format_cogl;
583
584 COGL_NOTE (PANGO, "redrawing glyph %i", glyph);
585
586 /* Glyphs that don't take up any space will end up without a
587 texture. These should never become dirty so they shouldn't end up
588 here */
589 _COGL_RETURN_IF_FAIL (value->texture != NULL);
590
591 if (_cogl_texture_get_format (value->texture) == COGL_PIXEL_FORMAT_A_8)
592 {
593 format_cairo = CAIRO_FORMAT_A8;
594 format_cogl = COGL_PIXEL_FORMAT_A_8;
595 }
596 else
597 {
598 format_cairo = CAIRO_FORMAT_ARGB32;
599
600 /* Cairo stores the data in native byte order as ARGB but Cogl's
601 pixel formats specify the actual byte order. Therefore we
602 need to use a different format depending on the
603 architecture */
604 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
605 format_cogl = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
606 #else
607 format_cogl = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
608 #endif
609 }
610
611 surface = cairo_image_surface_create (format_cairo,
612 value->draw_width,
613 value->draw_height);
614 cr = cairo_create (surface);
615
616 scaled_font = pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (font));
617 cairo_set_scaled_font (cr, scaled_font);
618
619 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
620
621 cairo_glyph.x = -value->draw_x;
622 cairo_glyph.y = -value->draw_y;
623 /* The PangoCairo glyph numbers directly map to Cairo glyph
624 numbers */
625 cairo_glyph.index = glyph;
626 cairo_show_glyphs (cr, &cairo_glyph, 1);
627
628 cairo_destroy (cr);
629 cairo_surface_flush (surface);
630
631 /* Copy the glyph to the texture */
632 cogl_texture_set_region (value->texture,
633 0, /* src_x */
634 0, /* src_y */
635 value->tx_pixel, /* dst_x */
636 value->ty_pixel, /* dst_y */
637 value->draw_width, /* dst_width */
638 value->draw_height, /* dst_height */
639 value->draw_width, /* width */
640 value->draw_height, /* height */
641 format_cogl,
642 cairo_image_surface_get_stride (surface),
643 cairo_image_surface_get_data (surface));
644
645 cairo_surface_destroy (surface);
646 }
647
648 static void
_cogl_pango_ensure_glyph_cache_for_layout_line_internal(PangoLayoutLine * line)649 _cogl_pango_ensure_glyph_cache_for_layout_line_internal (PangoLayoutLine *line)
650 {
651 PangoContext *context;
652 PangoRenderer *renderer;
653 GSList *l;
654
655 context = pango_layout_get_context (line->layout);
656 renderer =
657 PANGO_RENDERER (cogl_pango_get_renderer_from_context (context));
658
659 for (l = line->runs; l; l = l->next)
660 {
661 PangoLayoutRun *run = l->data;
662 PangoGlyphString *glyphs = run->glyphs;
663 int i;
664
665 for (i = 0; i < glyphs->num_glyphs; i++)
666 {
667 PangoGlyphInfo *gi = &glyphs->glyphs[i];
668
669 /* If the glyph isn't cached then this will reserve
670 space for it now. We won't actually draw the glyph
671 yet because reserving space could cause all of the
672 other glyphs to be moved so we might as well redraw
673 them all later once we know that the position is
674 settled */
675 cogl_pango_renderer_get_cached_glyph (renderer, TRUE,
676 run->item->analysis.font,
677 gi->glyph);
678 }
679 }
680 }
681
682 static void
_cogl_pango_set_dirty_glyphs(CoglPangoRenderer * priv)683 _cogl_pango_set_dirty_glyphs (CoglPangoRenderer *priv)
684 {
685 _cogl_pango_glyph_cache_set_dirty_glyphs
686 (priv->mipmap_caches.glyph_cache, cogl_pango_renderer_set_dirty_glyph);
687 _cogl_pango_glyph_cache_set_dirty_glyphs
688 (priv->no_mipmap_caches.glyph_cache, cogl_pango_renderer_set_dirty_glyph);
689 }
690
691 static void
_cogl_pango_ensure_glyph_cache_for_layout_line(PangoLayoutLine * line)692 _cogl_pango_ensure_glyph_cache_for_layout_line (PangoLayoutLine *line)
693 {
694 PangoContext *context;
695 CoglPangoRenderer *priv;
696
697 context = pango_layout_get_context (line->layout);
698 priv = cogl_pango_get_renderer_from_context (context);
699
700 _cogl_pango_ensure_glyph_cache_for_layout_line_internal (line);
701
702 /* Now that we know all of the positions are settled we'll fill in
703 any dirty glyphs */
704 _cogl_pango_set_dirty_glyphs (priv);
705 }
706
707 void
cogl_pango_ensure_glyph_cache_for_layout(PangoLayout * layout)708 cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout)
709 {
710 PangoContext *context;
711 CoglPangoRenderer *priv;
712 PangoLayoutIter *iter;
713
714 context = pango_layout_get_context (layout);
715 priv = cogl_pango_get_renderer_from_context (context);
716
717 _COGL_RETURN_IF_FAIL (PANGO_IS_LAYOUT (layout));
718
719 if ((iter = pango_layout_get_iter (layout)) == NULL)
720 return;
721
722 do
723 {
724 PangoLayoutLine *line;
725
726 line = pango_layout_iter_get_line_readonly (iter);
727
728 _cogl_pango_ensure_glyph_cache_for_layout_line_internal (line);
729 }
730 while (pango_layout_iter_next_line (iter));
731
732 pango_layout_iter_free (iter);
733
734 /* Now that we know all of the positions are settled we'll fill in
735 any dirty glyphs */
736 _cogl_pango_set_dirty_glyphs (priv);
737 }
738
739 static void
cogl_pango_renderer_set_color_for_part(PangoRenderer * renderer,PangoRenderPart part)740 cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer,
741 PangoRenderPart part)
742 {
743 PangoColor *pango_color = pango_renderer_get_color (renderer, part);
744 CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
745
746 if (pango_color)
747 {
748 CoglColor color;
749
750 cogl_color_init_from_4ub (&color,
751 pango_color->red >> 8,
752 pango_color->green >> 8,
753 pango_color->blue >> 8,
754 0xff);
755
756 _cogl_pango_display_list_set_color_override (priv->display_list, &color);
757 }
758 else
759 _cogl_pango_display_list_remove_color_override (priv->display_list);
760 }
761
762 static void
cogl_pango_renderer_draw_box(PangoRenderer * renderer,int x,int y,int width,int height)763 cogl_pango_renderer_draw_box (PangoRenderer *renderer,
764 int x,
765 int y,
766 int width,
767 int height)
768 {
769 CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
770
771 _COGL_RETURN_IF_FAIL (priv->display_list != NULL);
772
773 _cogl_pango_display_list_add_rectangle (priv->display_list,
774 x,
775 y - height,
776 x + width,
777 y);
778 }
779
780 static void
cogl_pango_renderer_get_device_units(PangoRenderer * renderer,int xin,int yin,float * xout,float * yout)781 cogl_pango_renderer_get_device_units (PangoRenderer *renderer,
782 int xin,
783 int yin,
784 float *xout,
785 float *yout)
786 {
787 const PangoMatrix *matrix;
788
789 if ((matrix = pango_renderer_get_matrix (renderer)))
790 {
791 /* Convert user-space coords to device coords */
792 *xout = ((xin * matrix->xx + yin * matrix->xy)
793 / PANGO_SCALE + matrix->x0);
794 *yout = ((yin * matrix->yy + xin * matrix->yx)
795 / PANGO_SCALE + matrix->y0);
796 }
797 else
798 {
799 *xout = PANGO_PIXELS (xin);
800 *yout = PANGO_PIXELS (yin);
801 }
802 }
803
804 static void
cogl_pango_renderer_draw_rectangle(PangoRenderer * renderer,PangoRenderPart part,int x,int y,int width,int height)805 cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer,
806 PangoRenderPart part,
807 int x,
808 int y,
809 int width,
810 int height)
811 {
812 CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
813 float x1, x2, y1, y2;
814
815 _COGL_RETURN_IF_FAIL (priv->display_list != NULL);
816
817 cogl_pango_renderer_set_color_for_part (renderer, part);
818
819 cogl_pango_renderer_get_device_units (renderer,
820 x, y,
821 &x1, &y1);
822 cogl_pango_renderer_get_device_units (renderer,
823 x + width, y + height,
824 &x2, &y2);
825
826 _cogl_pango_display_list_add_rectangle (priv->display_list,
827 x1, y1, x2, y2);
828 }
829
830 static void
cogl_pango_renderer_draw_trapezoid(PangoRenderer * renderer,PangoRenderPart part,double y1,double x11,double x21,double y2,double x12,double x22)831 cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
832 PangoRenderPart part,
833 double y1,
834 double x11,
835 double x21,
836 double y2,
837 double x12,
838 double x22)
839 {
840 CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
841
842 _COGL_RETURN_IF_FAIL (priv->display_list != NULL);
843
844 cogl_pango_renderer_set_color_for_part (renderer, part);
845
846 _cogl_pango_display_list_add_trapezoid (priv->display_list,
847 y1,
848 x11,
849 x21,
850 y2,
851 x12,
852 x22);
853 }
854
855 static void
cogl_pango_renderer_draw_glyphs(PangoRenderer * renderer,PangoFont * font,PangoGlyphString * glyphs,int xi,int yi)856 cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
857 PangoFont *font,
858 PangoGlyphString *glyphs,
859 int xi,
860 int yi)
861 {
862 CoglPangoRenderer *priv = (CoglPangoRenderer *) renderer;
863 CoglPangoGlyphCacheValue *cache_value;
864 int i;
865
866 cogl_pango_renderer_set_color_for_part (renderer,
867 PANGO_RENDER_PART_FOREGROUND);
868
869 for (i = 0; i < glyphs->num_glyphs; i++)
870 {
871 PangoGlyphInfo *gi = glyphs->glyphs + i;
872 float x, y;
873
874 cogl_pango_renderer_get_device_units (renderer,
875 xi + gi->geometry.x_offset,
876 yi + gi->geometry.y_offset,
877 &x, &y);
878
879 if ((gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
880 {
881 if (font == NULL)
882 {
883 cogl_pango_renderer_draw_box (renderer,
884 x,
885 y,
886 PANGO_UNKNOWN_GLYPH_WIDTH,
887 PANGO_UNKNOWN_GLYPH_HEIGHT);
888 }
889 else
890 {
891 PangoRectangle ink_rect;
892
893 pango_font_get_glyph_extents (font, gi->glyph, &ink_rect, NULL);
894 pango_extents_to_pixels (&ink_rect, NULL);
895
896 cogl_pango_renderer_draw_box (renderer,
897 x + ink_rect.x,
898 y + ink_rect.y + ink_rect.height,
899 ink_rect.width,
900 ink_rect.height);
901 }
902 }
903 else
904 {
905 /* Get the texture containing the glyph */
906 cache_value =
907 cogl_pango_renderer_get_cached_glyph (renderer,
908 FALSE,
909 font,
910 gi->glyph);
911
912 /* cogl_pango_ensure_glyph_cache_for_layout should always be
913 called before rendering a layout so we should never have
914 a dirty glyph here */
915 g_assert (cache_value == NULL || !cache_value->dirty);
916
917 if (cache_value == NULL)
918 {
919 cogl_pango_renderer_draw_box (renderer,
920 x,
921 y,
922 PANGO_UNKNOWN_GLYPH_WIDTH,
923 PANGO_UNKNOWN_GLYPH_HEIGHT);
924 }
925 else if (cache_value->texture)
926 {
927 x += (float)(cache_value->draw_x);
928 y += (float)(cache_value->draw_y);
929
930 cogl_pango_renderer_draw_glyph (priv, cache_value, x, y);
931 }
932 }
933
934 xi += gi->geometry.width;
935 }
936 }
937