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