Git init
[external/pango1.0.git] / pango / pangocairo-render.c
1 /* Pango
2  * pangocairo-render.c: Rendering routines to Cairo surfaces
3  *
4  * Copyright (C) 2004 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include <math.h>
25
26 #include "pangocairo-private.h"
27 #include "pango-glyph-item.h"
28
29 typedef struct _PangoCairoRendererClass PangoCairoRendererClass;
30
31 #define PANGO_CAIRO_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_CAIRO_RENDERER, PangoCairoRendererClass))
32 #define PANGO_IS_CAIRO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_CAIRO_RENDERER))
33 #define PANGO_CAIRO_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_CAIRO_RENDERER, PangoCairoRendererClass))
34
35 struct _PangoCairoRenderer
36 {
37   PangoRenderer parent_instance;
38
39   cairo_t *cr;
40   gboolean do_path;
41   gboolean has_show_text_glyphs;
42   double x_offset, y_offset;
43
44   /* house-keeping options */
45   gboolean is_cached_renderer;
46   gboolean cr_had_current_point;
47 };
48
49 struct _PangoCairoRendererClass
50 {
51   PangoRendererClass parent_class;
52 };
53
54 G_DEFINE_TYPE (PangoCairoRenderer, pango_cairo_renderer, PANGO_TYPE_RENDERER)
55
56 static void
57 set_color (PangoCairoRenderer *crenderer,
58            PangoRenderPart     part)
59 {
60   PangoColor *color = pango_renderer_get_color ((PangoRenderer *) (crenderer), part);
61
62   if (color)
63     cairo_set_source_rgb (crenderer->cr,
64                           color->red / 65535.,
65                           color->green / 65535.,
66                           color->blue / 65535.);
67 }
68
69 /* note: modifies crenderer->cr without doing cairo_save/restore() */
70 static void
71 _pango_cairo_renderer_draw_frame (PangoCairoRenderer *crenderer,
72                                   double              x,
73                                   double              y,
74                                   double              width,
75                                   double              height,
76                                   double              line_width,
77                                   gboolean            invalid)
78 {
79   cairo_t *cr = crenderer->cr;
80
81   if (crenderer->do_path)
82     {
83       double d2 = line_width * .5, d = line_width;
84
85       /* we draw an outer box in one winding direction and an inner one in the
86        * opposite direction.  This works for both cairo windings rules.
87        *
88        * what we really want is cairo_stroke_to_path(), but that's not
89        * implemented in cairo yet.
90        */
91
92       /* outer */
93       cairo_rectangle (cr, x-d2, y-d2, width+d, height+d);
94
95       /* inner */
96       if (invalid)
97         {
98           /* delicacies of computing the joint... this is REALLY slow */
99
100           double alpha, tan_alpha2, cos_alpha;
101           double sx, sy;
102
103           alpha = atan2 (height, width);
104
105           tan_alpha2 = tan (alpha * .5);
106           if (tan_alpha2 < 1e-5 || (sx = d2 / tan_alpha2, 2. * sx > width - d))
107             sx = (width - d) * .5;
108
109           cos_alpha = cos (alpha);
110           if (cos_alpha < 1e-5 || (sy = d2 / cos_alpha, 2. * sy > height - d))
111             sy = (height - d) * .5;
112
113           /* top triangle */
114           cairo_new_sub_path (cr);
115           cairo_line_to (cr, x+width-sx, y+d2);
116           cairo_line_to (cr, x+sx, y+d2);
117           cairo_line_to (cr, x+.5*width, y+.5*height-sy);
118           cairo_close_path (cr);
119
120           /* bottom triangle */
121           cairo_new_sub_path (cr);
122           cairo_line_to (cr, x+width-sx, y+height-d2);
123           cairo_line_to (cr, x+.5*width, y+.5*height+sy);
124           cairo_line_to (cr, x+sx, y+height-d2);
125           cairo_close_path (cr);
126
127
128           alpha = G_PI_2 - alpha;
129           tan_alpha2 = tan (alpha * .5);
130           if (tan_alpha2 < 1e-5 || (sy = d2 / tan_alpha2, 2. * sy > height - d))
131             sy = (width - d) * .5;
132
133           cos_alpha = cos (alpha);
134           if (cos_alpha < 1e-5 || (sx = d2 / cos_alpha, 2. * sx > width - d))
135             sx = (width - d) * .5;
136
137           /* left triangle */
138           cairo_new_sub_path (cr);
139           cairo_line_to (cr, x+d2, y+sy);
140           cairo_line_to (cr, x+d2, y+height-sy);
141           cairo_line_to (cr, x+.5*width-sx, y+.5*height);
142           cairo_close_path (cr);
143
144           /* right triangle */
145           cairo_new_sub_path (cr);
146           cairo_line_to (cr, x+width-d2, y+sy);
147           cairo_line_to (cr, x+.5*width+sx, y+.5*height);
148           cairo_line_to (cr, x+width-d2, y+height-sy);
149           cairo_close_path (cr);
150         }
151       else
152         cairo_rectangle (cr, x+width-d2, y+d2, - (width-d), height-d);
153     }
154   else
155     {
156       cairo_rectangle (cr, x, y, width, height);
157
158       if (invalid)
159         {
160           /* draw an X */
161
162           cairo_new_sub_path (cr);
163           cairo_move_to (cr, x, y);
164           cairo_rel_line_to (cr, width, height);
165
166           cairo_new_sub_path (cr);
167           cairo_move_to (cr, x + width, y);
168           cairo_rel_line_to (cr, -width, height);
169
170           cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
171         }
172
173       cairo_set_line_width (cr, line_width);
174       cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
175       cairo_set_miter_limit (cr, 2.);
176       cairo_stroke (cr);
177     }
178 }
179
180 static void
181 _pango_cairo_renderer_draw_box_glyph (PangoCairoRenderer *crenderer,
182                                       PangoGlyphInfo     *gi,
183                                       double              cx,
184                                       double              cy,
185                                       gboolean            invalid)
186 {
187   cairo_save (crenderer->cr);
188
189   _pango_cairo_renderer_draw_frame (crenderer,
190                                     cx + 1.5,
191                                     cy + 1.5 - PANGO_UNKNOWN_GLYPH_HEIGHT,
192                                     (double)gi->geometry.width / PANGO_SCALE - 3.0,
193                                     PANGO_UNKNOWN_GLYPH_HEIGHT - 3.0,
194                                     1.0,
195                                     invalid);
196
197   cairo_restore (crenderer->cr);
198 }
199
200 static void
201 _pango_cairo_renderer_draw_unknown_glyph (PangoCairoRenderer *crenderer,
202                                           PangoFont          *font,
203                                           PangoGlyphInfo     *gi,
204                                           double              cx,
205                                           double              cy)
206 {
207   char buf[7];
208   double x0, y0;
209   int row, col;
210   int rows, cols;
211   char hexbox_string[2] = {0, 0};
212   PangoCairoFontHexBoxInfo *hbi;
213   gunichar ch;
214   gboolean invalid_input;
215
216   cairo_save (crenderer->cr);
217
218   ch = gi->glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
219   invalid_input = G_UNLIKELY (gi->glyph == PANGO_GLYPH_INVALID_INPUT || ch > 0x10FFFF);
220
221   hbi = _pango_cairo_font_get_hex_box_info ((PangoCairoFont *)font);
222   if (!hbi || !_pango_cairo_font_install ((PangoFont *)(hbi->font), crenderer->cr))
223     {
224       _pango_cairo_renderer_draw_box_glyph (crenderer, gi, cx, cy, invalid_input);
225       goto done;
226     }
227
228   rows = hbi->rows;
229   if (G_UNLIKELY (invalid_input))
230     {
231       cols = 1;
232     }
233   else
234     {
235       cols = (ch > 0xffff ? 6 : 4) / rows;
236       g_snprintf (buf, sizeof(buf), (ch > 0xffff) ? "%06X" : "%04X", ch);
237     }
238
239   _pango_cairo_renderer_draw_frame (crenderer,
240                                     cx + hbi->pad_x * 1.5,
241                                     cy + hbi->box_descent - hbi->box_height + hbi->pad_y * 0.5,
242                                     (double)gi->geometry.width / PANGO_SCALE - 3 * hbi->pad_x,
243                                     (hbi->box_height - hbi->pad_y),
244                                     hbi->line_width,
245                                     invalid_input);
246
247   if (invalid_input)
248     goto done;
249
250   x0 = cx + hbi->pad_x * 3.0;
251   y0 = cy + hbi->box_descent - hbi->pad_y * 2;
252
253   for (row = 0; row < rows; row++)
254     {
255       double y = y0 - (rows - 1 - row) * (hbi->digit_height + hbi->pad_y);
256       for (col = 0; col < cols; col++)
257         {
258           double x = x0 + col * (hbi->digit_width + hbi->pad_x);
259
260           cairo_move_to (crenderer->cr, x, y);
261
262           hexbox_string[0] = buf[row * cols + col];
263
264           if (crenderer->do_path)
265               cairo_text_path (crenderer->cr, hexbox_string);
266           else
267               cairo_show_text (crenderer->cr, hexbox_string);
268         }
269     }
270
271 done:
272   cairo_restore (crenderer->cr);
273 }
274
275 #ifndef STACK_BUFFER_SIZE
276 #define STACK_BUFFER_SIZE (512 * sizeof (int))
277 #endif
278
279 #define STACK_ARRAY_LENGTH(T) (STACK_BUFFER_SIZE / sizeof(T))
280
281 static void
282 pango_cairo_renderer_show_text_glyphs (PangoRenderer        *renderer,
283                                        const char           *text,
284                                        int                   text_len,
285                                        PangoGlyphString     *glyphs,
286                                        cairo_text_cluster_t *clusters,
287                                        int                   num_clusters,
288                                        gboolean              backward,
289                                        PangoFont            *font,
290                                        int                   x,
291                                        int                   y)
292 {
293   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
294
295   int i, count;
296   int x_position = 0;
297   cairo_glyph_t *cairo_glyphs;
298   cairo_glyph_t stack_glyphs[STACK_ARRAY_LENGTH (cairo_glyph_t)];
299   double base_x = crenderer->x_offset + (double)x / PANGO_SCALE;
300   double base_y = crenderer->y_offset + (double)y / PANGO_SCALE;
301
302   cairo_save (crenderer->cr);
303   if (!crenderer->do_path)
304     set_color (crenderer, PANGO_RENDER_PART_FOREGROUND);
305
306   if (!_pango_cairo_font_install (font, crenderer->cr))
307     {
308       for (i = 0; i < glyphs->num_glyphs; i++)
309         {
310           PangoGlyphInfo *gi = &glyphs->glyphs[i];
311
312           if (gi->glyph != PANGO_GLYPH_EMPTY)
313             {
314               double cx = base_x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
315               double cy = gi->geometry.y_offset == 0 ?
316                           base_y :
317                           base_y + (double)(gi->geometry.y_offset) / PANGO_SCALE;
318
319               _pango_cairo_renderer_draw_unknown_glyph (crenderer, font, gi, cx, cy);
320             }
321           x_position += gi->geometry.width;
322         }
323
324       goto done;
325     }
326
327   if (glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_glyphs))
328     cairo_glyphs = g_new (cairo_glyph_t, glyphs->num_glyphs);
329   else
330     cairo_glyphs = stack_glyphs;
331
332   count = 0;
333   for (i = 0; i < glyphs->num_glyphs; i++)
334     {
335       PangoGlyphInfo *gi = &glyphs->glyphs[i];
336
337       if (gi->glyph != PANGO_GLYPH_EMPTY)
338         {
339           double cx = base_x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
340           double cy = gi->geometry.y_offset == 0 ?
341                       base_y :
342                       base_y + (double)(gi->geometry.y_offset) / PANGO_SCALE;
343
344           if (gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG)
345             _pango_cairo_renderer_draw_unknown_glyph (crenderer, font, gi, cx, cy);
346           else
347             {
348               cairo_glyphs[count].index = gi->glyph;
349               cairo_glyphs[count].x = cx;
350               cairo_glyphs[count].y = cy;
351               count++;
352             }
353         }
354       x_position += gi->geometry.width;
355     }
356
357   if (G_UNLIKELY (crenderer->do_path))
358     cairo_glyph_path (crenderer->cr, cairo_glyphs, count);
359   else
360     if (G_UNLIKELY (clusters))
361       cairo_show_text_glyphs (crenderer->cr,
362                               text, text_len,
363                               cairo_glyphs, count,
364                               clusters, num_clusters,
365                               backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : 0);
366     else
367       cairo_show_glyphs (crenderer->cr, cairo_glyphs, count);
368
369   if (cairo_glyphs != stack_glyphs)
370     g_free (cairo_glyphs);
371
372 done:
373   cairo_restore (crenderer->cr);
374 }
375
376 static void
377 pango_cairo_renderer_draw_glyphs (PangoRenderer     *renderer,
378                                   PangoFont         *font,
379                                   PangoGlyphString  *glyphs,
380                                   int                x,
381                                   int                y)
382 {
383   pango_cairo_renderer_show_text_glyphs (renderer,
384                                          NULL, 0,
385                                          glyphs,
386                                          NULL, 0,
387                                          FALSE,
388                                          font,
389                                          x, y);
390 }
391
392 static void
393 pango_cairo_renderer_draw_glyph_item (PangoRenderer     *renderer,
394                                       const char        *text,
395                                       PangoGlyphItem    *glyph_item,
396                                       int                x,
397                                       int                y)
398 {
399   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
400   PangoFont          *font      = glyph_item->item->analysis.font;
401   PangoGlyphString   *glyphs    = glyph_item->glyphs;
402   PangoItem          *item      = glyph_item->item;
403   gboolean            backward  = (item->analysis.level & 1) != 0;
404
405   PangoGlyphItemIter   iter;
406   cairo_text_cluster_t *cairo_clusters;
407   cairo_text_cluster_t stack_clusters[STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
408   int num_clusters;
409
410   if (!crenderer->has_show_text_glyphs || crenderer->do_path)
411     {
412       pango_cairo_renderer_show_text_glyphs (renderer,
413                                              NULL, 0,
414                                              glyphs,
415                                              NULL, 0,
416                                              FALSE,
417                                              font,
418                                              x, y);
419       return;
420     }
421
422   if (glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_clusters))
423     cairo_clusters = g_new (cairo_text_cluster_t, glyphs->num_glyphs);
424   else
425     cairo_clusters = stack_clusters;
426
427   num_clusters = 0;
428   if (pango_glyph_item_iter_init_start (&iter, glyph_item, text))
429     {
430       do {
431         int num_bytes, num_glyphs, i;
432
433         num_bytes  = iter.end_index - iter.start_index;
434         num_glyphs = backward ? iter.start_glyph - iter.end_glyph : iter.end_glyph - iter.start_glyph;
435
436         if (num_bytes < 1)
437           g_warning ("pango_cairo_renderer_draw_glyph_item: bad cluster has num_bytess %d", num_bytes);
438         if (num_glyphs < 1)
439           g_warning ("pango_cairo_renderer_draw_glyph_item: bad cluster has num_glyphs %d", num_glyphs);
440
441         /* Discount empty and unknown glyphs */
442         for (i = MIN (iter.start_glyph, iter.end_glyph+1);
443              i < MAX (iter.start_glyph+1, iter.end_glyph);
444              i++)
445           {
446             PangoGlyphInfo *gi = &glyphs->glyphs[i];
447
448             if (gi->glyph == PANGO_GLYPH_EMPTY ||
449                 gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG)
450               num_glyphs--;
451           }
452
453         cairo_clusters[num_clusters].num_bytes  = num_bytes;
454         cairo_clusters[num_clusters].num_glyphs = num_glyphs;
455         num_clusters++;
456       } while (pango_glyph_item_iter_next_cluster (&iter));
457     }
458
459   pango_cairo_renderer_show_text_glyphs (renderer,
460                                          text + item->offset, item->length,
461                                          glyphs,
462                                          cairo_clusters, num_clusters,
463                                          backward,
464                                          font,
465                                          x, y);
466
467   if (cairo_clusters != stack_clusters)
468     g_free (cairo_clusters);
469 }
470
471 static void
472 pango_cairo_renderer_draw_rectangle (PangoRenderer     *renderer,
473                                      PangoRenderPart    part,
474                                      int                x,
475                                      int                y,
476                                      int                width,
477                                      int                height)
478 {
479   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
480
481   if (!crenderer->do_path)
482     {
483       cairo_save (crenderer->cr);
484
485       set_color (crenderer, part);
486     }
487
488   cairo_rectangle (crenderer->cr,
489                    crenderer->x_offset + (double)x / PANGO_SCALE,
490                    crenderer->y_offset + (double)y / PANGO_SCALE,
491                    (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
492
493   if (!crenderer->do_path)
494     {
495       cairo_fill (crenderer->cr);
496
497       cairo_restore (crenderer->cr);
498     }
499 }
500
501 static void
502 pango_cairo_renderer_draw_trapezoid (PangoRenderer     *renderer,
503                                      PangoRenderPart    part,
504                                      double             y1_,
505                                      double             x11,
506                                      double             x21,
507                                      double             y2,
508                                      double             x12,
509                                      double             x22)
510 {
511   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
512   cairo_t *cr;
513   double x, y;
514
515   cr = crenderer->cr;
516
517   cairo_save (cr);
518
519   if (!crenderer->do_path)
520     set_color (crenderer, part);
521
522   x = crenderer->x_offset,
523   y = crenderer->y_offset;
524   cairo_user_to_device_distance (cr, &x, &y);
525   cairo_identity_matrix (cr);
526   cairo_translate (cr, x, y);
527
528   cairo_move_to (cr, x11, y1_);
529   cairo_line_to (cr, x21, y1_);
530   cairo_line_to (cr, x22, y2);
531   cairo_line_to (cr, x12, y2);
532   cairo_close_path (cr);
533
534   if (!crenderer->do_path)
535     cairo_fill (cr);
536
537   cairo_restore (cr);
538 }
539
540 /* Draws an error underline that looks like one of:
541  *              H       E                H
542  *     /\      /\      /\        /\      /\               -
543  *   A/  \    /  \    /  \     A/  \    /  \              |
544  *    \   \  /    \  /   /D     \   \  /    \             |
545  *     \   \/  C   \/   /        \   \/   C  \            | height = HEIGHT_SQUARES * square
546  *      \      /\  F   /          \  F   /\   \           |
547  *       \    /  \    /            \    /  \   \G         |
548  *        \  /    \  /              \  /    \  /          |
549  *         \/      \/                \/      \/           -
550  *         B                         B
551  *         |---|
552  *       unit_width = (HEIGHT_SQUARES - 1) * square
553  *
554  * The x, y, width, height passed in give the desired bounding box;
555  * x/width are adjusted to make the underline a integer number of units
556  * wide.
557  */
558 #define HEIGHT_SQUARES 2.5
559
560 static void
561 draw_error_underline (cairo_t *cr,
562                       double   x,
563                       double   y,
564                       double   width,
565                       double   height)
566 {
567   double square = height / HEIGHT_SQUARES;
568   double unit_width = (HEIGHT_SQUARES - 1) * square;
569   double double_width = 2 * unit_width;
570   int width_units = (width + unit_width / 2) / unit_width;
571   double y_top, y_bottom;
572   double x_left, x_middle, x_right;
573   int i;
574
575   x += (width - width_units * unit_width) / 2;
576   width = width_units * unit_width;
577
578   y_top = y;
579   y_bottom = y + height;
580
581   /* Bottom of squiggle */
582   x_middle = x + unit_width;
583   x_right  = x + double_width;
584   cairo_move_to (cr, x - square / 2, y_top + square / 2); /* A */
585   for (i = 0; i < width_units-2; i += 2)
586     {
587       cairo_line_to (cr, x_middle, y_bottom); /* B */
588       cairo_line_to (cr, x_right, y_top + square); /* C */
589
590       x_middle += double_width;
591       x_right  += double_width;
592     }
593   cairo_line_to (cr, x_middle, y_bottom); /* B */
594
595   if (i + 1 == width_units)
596     cairo_line_to (cr, x_middle + square / 2, y_bottom - square / 2); /* G */
597   else if (i + 2 == width_units) {
598     cairo_line_to (cr, x_right + square / 2, y_top + square / 2); /* D */
599     cairo_line_to (cr, x_right, y_top); /* E */
600   }
601
602   /* Top of squiggle */
603   x_left = x_middle - unit_width;
604   for (; i >= 0; i -= 2)
605     {
606       cairo_line_to (cr, x_middle, y_bottom - square); /* F */
607       cairo_line_to (cr, x_left, y_top);   /* H */
608
609       x_left   -= double_width;
610       x_middle -= double_width;
611     }
612 }
613
614 static void
615 pango_cairo_renderer_draw_error_underline (PangoRenderer *renderer,
616                                            int            x,
617                                            int            y,
618                                            int            width,
619                                            int            height)
620 {
621   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
622   cairo_t *cr = crenderer->cr;
623
624   if (!crenderer->do_path)
625     {
626       cairo_save (cr);
627
628       set_color (crenderer, PANGO_RENDER_PART_UNDERLINE);
629
630       cairo_new_path (cr);
631     }
632
633   draw_error_underline (cr,
634                         crenderer->x_offset + (double)x / PANGO_SCALE,
635                         crenderer->y_offset + (double)y / PANGO_SCALE,
636                         (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
637
638   if (!crenderer->do_path)
639     {
640       cairo_fill (cr);
641
642       cairo_restore (cr);
643     }
644 }
645
646 static void
647 pango_cairo_renderer_draw_shape (PangoRenderer  *renderer,
648                                  PangoAttrShape *attr,
649                                  int             x,
650                                  int             y)
651 {
652   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
653   cairo_t *cr = crenderer->cr;
654   PangoLayout *layout;
655   PangoCairoShapeRendererFunc shape_renderer;
656   gpointer                    shape_renderer_data;
657   double base_x, base_y;
658
659   layout = pango_renderer_get_layout (renderer);
660
661   if (!layout)
662         return;
663
664   shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout),
665                                                            &shape_renderer_data);
666
667   if (!shape_renderer)
668     return;
669
670   base_x = crenderer->x_offset + (double)x / PANGO_SCALE;
671   base_y = crenderer->y_offset + (double)y / PANGO_SCALE;
672
673   cairo_save (cr);
674   if (!crenderer->do_path)
675     set_color (crenderer, PANGO_RENDER_PART_FOREGROUND);
676
677   cairo_move_to (cr, base_x, base_y);
678
679   shape_renderer (cr, attr, crenderer->do_path, shape_renderer_data);
680
681   cairo_restore (cr);
682 }
683
684 static void
685 pango_cairo_renderer_init (PangoCairoRenderer *renderer G_GNUC_UNUSED)
686 {
687 }
688
689 static void
690 pango_cairo_renderer_class_init (PangoCairoRendererClass *klass)
691 {
692   PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
693
694   renderer_class->draw_glyphs = pango_cairo_renderer_draw_glyphs;
695   renderer_class->draw_glyph_item = pango_cairo_renderer_draw_glyph_item;
696   renderer_class->draw_rectangle = pango_cairo_renderer_draw_rectangle;
697   renderer_class->draw_trapezoid = pango_cairo_renderer_draw_trapezoid;
698   renderer_class->draw_error_underline = pango_cairo_renderer_draw_error_underline;
699   renderer_class->draw_shape = pango_cairo_renderer_draw_shape;
700 }
701
702 static PangoCairoRenderer *cached_renderer = NULL;
703 G_LOCK_DEFINE_STATIC (cached_renderer);
704
705 static PangoCairoRenderer *
706 acquire_renderer (void)
707 {
708   PangoCairoRenderer *renderer;
709
710   if (G_LIKELY (G_TRYLOCK (cached_renderer)))
711     {
712       if (G_UNLIKELY (!cached_renderer))
713         {
714           cached_renderer = g_object_new (PANGO_TYPE_CAIRO_RENDERER, NULL);
715           cached_renderer->is_cached_renderer = TRUE;
716         }
717
718       renderer = cached_renderer;
719     }
720   else
721     {
722       renderer = g_object_new (PANGO_TYPE_CAIRO_RENDERER, NULL);
723     }
724
725   return renderer;
726 }
727
728 static void
729 release_renderer (PangoCairoRenderer *renderer)
730 {
731   if (G_LIKELY (renderer->is_cached_renderer))
732     {
733       renderer->cr = NULL;
734       renderer->do_path = FALSE;
735       renderer->has_show_text_glyphs = FALSE;
736       renderer->x_offset = 0.;
737       renderer->y_offset = 0.;
738
739       G_UNLOCK (cached_renderer);
740     }
741   else
742     g_object_unref (renderer);
743 }
744
745 static void
746 save_current_point (PangoCairoRenderer *renderer)
747 {
748   renderer->cr_had_current_point = cairo_has_current_point (renderer->cr);
749   cairo_get_current_point (renderer->cr, &renderer->x_offset, &renderer->y_offset);
750
751   /* abuse save_current_point() to cache cairo_has_show_text_glyphs() result */
752   renderer->has_show_text_glyphs = cairo_surface_has_show_text_glyphs (cairo_get_target (renderer->cr));
753 }
754
755 static void
756 restore_current_point (PangoCairoRenderer *renderer)
757 {
758   if (renderer->cr_had_current_point)
759     /* XXX should do cairo_set_current_point() when we have that function */
760     cairo_move_to (renderer->cr, renderer->x_offset, renderer->y_offset);
761   else
762     cairo_new_sub_path (renderer->cr);
763 }
764
765
766 /* convenience wrappers using the default renderer */
767
768
769 static void
770 _pango_cairo_do_glyph_string (cairo_t          *cr,
771                               PangoFont        *font,
772                               PangoGlyphString *glyphs,
773                               gboolean          do_path)
774 {
775   PangoCairoRenderer *crenderer = acquire_renderer ();
776   PangoRenderer *renderer = (PangoRenderer *) crenderer;
777
778   crenderer->cr = cr;
779   crenderer->do_path = do_path;
780   save_current_point (crenderer);
781
782   if (!do_path)
783     {
784       /* unset all part colors, since when drawing just a glyph string,
785        * prepare_run() isn't called.
786        */
787
788       pango_renderer_activate (renderer);
789
790       pango_renderer_set_color (renderer, PANGO_RENDER_PART_FOREGROUND, NULL);
791       pango_renderer_set_color (renderer, PANGO_RENDER_PART_BACKGROUND, NULL);
792       pango_renderer_set_color (renderer, PANGO_RENDER_PART_UNDERLINE, NULL);
793       pango_renderer_set_color (renderer, PANGO_RENDER_PART_STRIKETHROUGH, NULL);
794     }
795
796   pango_renderer_draw_glyphs (renderer, font, glyphs, 0, 0);
797
798   if (!do_path)
799     {
800       pango_renderer_deactivate (renderer);
801     }
802
803   restore_current_point (crenderer);
804
805   release_renderer (crenderer);
806 }
807
808 static void
809 _pango_cairo_do_glyph_item (cairo_t          *cr,
810                             const char       *text,
811                             PangoGlyphItem   *glyph_item,
812                             gboolean          do_path)
813 {
814   PangoCairoRenderer *crenderer = acquire_renderer ();
815   PangoRenderer *renderer = (PangoRenderer *) crenderer;
816
817   crenderer->cr = cr;
818   crenderer->do_path = do_path;
819   save_current_point (crenderer);
820
821   if (!do_path)
822     {
823       /* unset all part colors, since when drawing just a glyph string,
824        * prepare_run() isn't called.
825        */
826
827       pango_renderer_activate (renderer);
828
829       pango_renderer_set_color (renderer, PANGO_RENDER_PART_FOREGROUND, NULL);
830       pango_renderer_set_color (renderer, PANGO_RENDER_PART_BACKGROUND, NULL);
831       pango_renderer_set_color (renderer, PANGO_RENDER_PART_UNDERLINE, NULL);
832       pango_renderer_set_color (renderer, PANGO_RENDER_PART_STRIKETHROUGH, NULL);
833     }
834
835   pango_renderer_draw_glyph_item (renderer, text, glyph_item, 0, 0);
836
837   if (!do_path)
838     {
839       pango_renderer_deactivate (renderer);
840     }
841
842   restore_current_point (crenderer);
843
844   release_renderer (crenderer);
845 }
846
847 static void
848 _pango_cairo_do_layout_line (cairo_t          *cr,
849                              PangoLayoutLine  *line,
850                              gboolean          do_path)
851 {
852   PangoCairoRenderer *crenderer = acquire_renderer ();
853   PangoRenderer *renderer = (PangoRenderer *) crenderer;
854
855   crenderer->cr = cr;
856   crenderer->do_path = do_path;
857   save_current_point (crenderer);
858
859   pango_renderer_draw_layout_line (renderer, line, 0, 0);
860
861   restore_current_point (crenderer);
862
863   release_renderer (crenderer);
864 }
865
866 static void
867 _pango_cairo_do_layout (cairo_t     *cr,
868                         PangoLayout *layout,
869                         gboolean     do_path)
870 {
871   PangoCairoRenderer *crenderer = acquire_renderer ();
872   PangoRenderer *renderer = (PangoRenderer *) crenderer;
873
874   crenderer->cr = cr;
875   crenderer->do_path = do_path;
876   save_current_point (crenderer);
877
878   pango_renderer_draw_layout (renderer, layout, 0, 0);
879
880   restore_current_point (crenderer);
881
882   release_renderer (crenderer);
883 }
884
885 static void
886 _pango_cairo_do_error_underline (cairo_t *cr,
887                                  double   x,
888                                  double   y,
889                                  double   width,
890                                  double   height,
891                                  gboolean do_path)
892 {
893   /* We don't use a renderer here, for a simple reason:
894    * the only renderer we can get is the default renderer, that
895    * is all implemented here, so we shortcircuit and make our
896    * life way easier.
897    */
898
899   if (!do_path)
900     cairo_new_path (cr);
901
902   draw_error_underline (cr, x, y, width, height);
903
904   if (!do_path)
905     cairo_fill (cr);
906 }
907
908
909 /* public wrapper of above to show or append path */
910
911
912 /**
913  * pango_cairo_show_glyph_string:
914  * @cr: a Cairo context
915  * @font: a #PangoFont from a #PangoCairoFontMap
916  * @glyphs: a #PangoGlyphString
917  *
918  * Draws the glyphs in @glyphs in the specified cairo context.
919  * The origin of the glyphs (the left edge of the baseline) will
920  * be drawn at the current point of the cairo context.
921  *
922  * Since: 1.10
923  **/
924 void
925 pango_cairo_show_glyph_string (cairo_t          *cr,
926                                PangoFont        *font,
927                                PangoGlyphString *glyphs)
928 {
929   g_return_if_fail (cr != NULL);
930   g_return_if_fail (glyphs != NULL);
931
932   _pango_cairo_do_glyph_string (cr, font, glyphs, FALSE);
933 }
934
935
936 /**
937  * pango_cairo_show_glyph_item:
938  * @cr: a Cairo context
939  * @text: the UTF-8 text that @glyph_item refers to
940  * @glyph_item: a #PangoGlyphItem
941  *
942  * Draws the glyphs in @glyph_item in the specified cairo context,
943  * embedding the text associated with the glyphs in the output if the
944  * output format supports it (PDF for example), otherwise it acts
945  * similar to pango_cairo_show_glyph_string().
946  *
947  * The origin of the glyphs (the left edge of the baseline) will
948  * be drawn at the current point of the cairo context.
949  *
950  * Note that @text is the start of the text for layout, which is then
951  * indexed by <literal>@glyph_item->item->offset</literal>.
952  *
953  * Since: 1.22
954  **/
955 void
956 pango_cairo_show_glyph_item (cairo_t          *cr,
957                              const char       *text,
958                              PangoGlyphItem   *glyph_item)
959 {
960   g_return_if_fail (cr != NULL);
961   g_return_if_fail (text != NULL);
962   g_return_if_fail (glyph_item != NULL);
963
964   _pango_cairo_do_glyph_item (cr, text, glyph_item, FALSE);
965 }
966
967 /**
968  * pango_cairo_show_layout_line:
969  * @cr: a Cairo context
970  * @line: a #PangoLayoutLine
971  *
972  * Draws a #PangoLayoutLine in the specified cairo context.
973  * The origin of the glyphs (the left edge of the line) will
974  * be drawn at the current point of the cairo context.
975  *
976  * Since: 1.10
977  **/
978 void
979 pango_cairo_show_layout_line (cairo_t          *cr,
980                               PangoLayoutLine  *line)
981 {
982   g_return_if_fail (cr != NULL);
983   g_return_if_fail (line != NULL);
984
985   _pango_cairo_do_layout_line (cr, line, FALSE);
986 }
987
988 /**
989  * pango_cairo_show_layout:
990  * @cr: a Cairo context
991  * @layout: a Pango layout
992  *
993  * Draws a #PangoLayout in the specified cairo context.
994  * The top-left corner of the #PangoLayout will be drawn
995  * at the current point of the cairo context.
996  *
997  * Since: 1.10
998  **/
999 void
1000 pango_cairo_show_layout (cairo_t     *cr,
1001                          PangoLayout *layout)
1002 {
1003   g_return_if_fail (cr != NULL);
1004   g_return_if_fail (PANGO_IS_LAYOUT (layout));
1005
1006   _pango_cairo_do_layout (cr, layout, FALSE);
1007 }
1008
1009 /**
1010  * pango_cairo_show_error_underline:
1011  * @cr: a Cairo context
1012  * @x: The X coordinate of one corner of the rectangle
1013  * @y: The Y coordinate of one corner of the rectangle
1014  * @width: Non-negative width of the rectangle
1015  * @height: Non-negative height of the rectangle
1016  *
1017  * Draw a squiggly line in the specified cairo context that approximately
1018  * covers the given rectangle in the style of an underline used to indicate a
1019  * spelling error.  (The width of the underline is rounded to an integer
1020  * number of up/down segments and the resulting rectangle is centered in the
1021  * original rectangle)
1022  *
1023  * Since: 1.14
1024  **/
1025 void
1026 pango_cairo_show_error_underline (cairo_t *cr,
1027                                   double  x,
1028                                   double  y,
1029                                   double  width,
1030                                   double  height)
1031 {
1032   g_return_if_fail (cr != NULL);
1033   g_return_if_fail ((width >= 0) && (height >= 0));
1034
1035   _pango_cairo_do_error_underline (cr, x, y, width, height, FALSE);
1036 }
1037
1038 /**
1039  * pango_cairo_glyph_string_path
1040  * @cr: a Cairo context
1041  * @font: a #PangoFont from a #PangoCairoFontMap
1042  * @glyphs: a #PangoGlyphString
1043  *
1044  * Adds the glyphs in @glyphs to the current path in the specified
1045  * cairo context. The origin of the glyphs (the left edge of the baseline)
1046  * will be at the current point of the cairo context.
1047  *
1048  * Since: 1.10
1049  **/
1050 void
1051 pango_cairo_glyph_string_path (cairo_t          *cr,
1052                                PangoFont        *font,
1053                                PangoGlyphString *glyphs)
1054 {
1055   g_return_if_fail (cr != NULL);
1056   g_return_if_fail (glyphs != NULL);
1057
1058   _pango_cairo_do_glyph_string (cr, font, glyphs, TRUE);
1059 }
1060
1061 /**
1062  * pango_cairo_layout_line_path:
1063  * @cr: a Cairo context
1064  * @line: a #PangoLayoutLine
1065  *
1066  * Adds the text in #PangoLayoutLine to the current path in the
1067  * specified cairo context.  The origin of the glyphs (the left edge
1068  * of the line) will be at the current point of the cairo context.
1069  *
1070  * Since: 1.10
1071  **/
1072 void
1073 pango_cairo_layout_line_path (cairo_t          *cr,
1074                               PangoLayoutLine  *line)
1075 {
1076   g_return_if_fail (cr != NULL);
1077   g_return_if_fail (line != NULL);
1078
1079   _pango_cairo_do_layout_line (cr, line, TRUE);
1080 }
1081
1082 /**
1083  * pango_cairo_layout_path:
1084  * @cr: a Cairo context
1085  * @layout: a Pango layout
1086  *
1087  * Adds the text in a #PangoLayout to the current path in the
1088  * specified cairo context.  The top-left corner of the #PangoLayout
1089  * will be at the current point of the cairo context.
1090  *
1091  * Since: 1.10
1092  **/
1093 void
1094 pango_cairo_layout_path (cairo_t     *cr,
1095                          PangoLayout *layout)
1096 {
1097   g_return_if_fail (cr != NULL);
1098   g_return_if_fail (PANGO_IS_LAYOUT (layout));
1099
1100   _pango_cairo_do_layout (cr, layout, TRUE);
1101 }
1102
1103 /**
1104  * pango_cairo_error_underline_path:
1105  * @cr: a Cairo context
1106  * @x: The X coordinate of one corner of the rectangle
1107  * @y: The Y coordinate of one corner of the rectangle
1108  * @width: Non-negative width of the rectangle
1109  * @height: Non-negative height of the rectangle
1110  *
1111  * Add a squiggly line to the current path in the specified cairo context that
1112  * approximately covers the given rectangle in the style of an underline used
1113  * to indicate a spelling error.  (The width of the underline is rounded to an
1114  * integer number of up/down segments and the resulting rectangle is centered
1115  * in the original rectangle)
1116  *
1117  * Since: 1.14
1118  **/
1119 void
1120 pango_cairo_error_underline_path (cairo_t *cr,
1121                                   double   x,
1122                                   double   y,
1123                                   double   width,
1124                                   double   height)
1125 {
1126   g_return_if_fail (cr != NULL);
1127   g_return_if_fail ((width >= 0) && (height >= 0));
1128
1129   _pango_cairo_do_error_underline (cr, x, y, width, height, TRUE);
1130 }