Git init
[external/pango1.0.git] / pango / pango-renderer.c
1 /* Pango
2  * pango-renderer.h: Base class for rendering
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 #include <stdlib.h>
24
25 #include "pango-renderer.h"
26 #include "pango-impl-utils.h"
27
28 #define N_RENDER_PARTS 4
29
30 #define PANGO_IS_RENDERER_FAST(renderer) (renderer != NULL)
31 #define IS_VALID_PART(part) ((guint)part < N_RENDER_PARTS)
32
33 typedef struct _LineState LineState;
34 typedef struct _Point Point;
35
36 struct _Point
37 {
38   double x, y;
39 };
40
41 struct _LineState
42 {
43   PangoUnderline underline;
44   PangoRectangle underline_rect;
45
46   gboolean strikethrough;
47   PangoRectangle strikethrough_rect;
48
49   int logical_rect_end;
50 };
51
52 struct _PangoRendererPrivate
53 {
54   PangoColor color[N_RENDER_PARTS];
55   gboolean color_set[N_RENDER_PARTS];
56
57   PangoLayoutLine *line;
58   LineState *line_state;
59 };
60
61 static void pango_renderer_finalize                     (GObject          *gobject);
62 static void pango_renderer_default_draw_glyphs          (PangoRenderer    *renderer,
63                                                          PangoFont        *font,
64                                                          PangoGlyphString *glyphs,
65                                                          int               x,
66                                                          int               y);
67 static void pango_renderer_default_draw_glyph_item      (PangoRenderer    *renderer,
68                                                          const char       *text,
69                                                          PangoGlyphItem   *glyph_item,
70                                                          int               x,
71                                                          int               y);
72 static void pango_renderer_default_draw_rectangle       (PangoRenderer    *renderer,
73                                                          PangoRenderPart   part,
74                                                          int               x,
75                                                          int               y,
76                                                          int               width,
77                                                          int               height);
78 static void pango_renderer_default_draw_error_underline (PangoRenderer    *renderer,
79                                                          int               x,
80                                                          int               y,
81                                                          int               width,
82                                                          int               height);
83 static void pango_renderer_default_prepare_run          (PangoRenderer    *renderer,
84                                                          PangoLayoutRun   *run);
85
86 static void pango_renderer_prepare_run (PangoRenderer  *renderer,
87                                         PangoLayoutRun *run);
88
89 static void
90 to_device (PangoMatrix *matrix,
91            double       x,
92            double       y,
93            Point       *result)
94 {
95   if (matrix)
96     {
97       result->x = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0;
98       result->y = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0;
99     }
100   else
101     {
102       result->x = x / PANGO_SCALE;
103       result->y = y / PANGO_SCALE;
104     }
105 }
106
107 G_DEFINE_ABSTRACT_TYPE (PangoRenderer, pango_renderer, G_TYPE_OBJECT)
108
109 static GObjectClass *parent_class;
110
111 static void
112 pango_renderer_class_init (PangoRendererClass *klass)
113 {
114   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
115
116   parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
117
118   klass->draw_glyphs = pango_renderer_default_draw_glyphs;
119   klass->draw_glyph_item = pango_renderer_default_draw_glyph_item;
120   klass->draw_rectangle = pango_renderer_default_draw_rectangle;
121   klass->draw_error_underline = pango_renderer_default_draw_error_underline;
122   klass->prepare_run = pango_renderer_default_prepare_run;
123
124   gobject_class->finalize = pango_renderer_finalize;
125
126   g_type_class_add_private (gobject_class, sizeof (PangoRendererPrivate));
127 }
128
129 static void
130 pango_renderer_init (PangoRenderer *renderer)
131 {
132   renderer->priv = G_TYPE_INSTANCE_GET_PRIVATE (renderer,
133                                                 PANGO_TYPE_RENDERER,
134                                                 PangoRendererPrivate);
135   renderer->matrix = NULL;
136 }
137
138 static void
139 pango_renderer_finalize (GObject *gobject)
140 {
141   PangoRenderer *renderer = PANGO_RENDERER (gobject);
142
143   if (renderer->matrix)
144     pango_matrix_free (renderer->matrix);
145
146   parent_class->finalize (gobject);
147 }
148
149 /**
150  * pango_renderer_draw_layout:
151  * @renderer: a #PangoRenderer
152  * @layout: a #PangoLayout
153  * @x: X position of left edge of baseline, in user space coordinates
154  *   in Pango units.
155  * @y: Y position of left edge of baseline, in user space coordinates
156  *    in Pango units.
157  *
158  * Draws @layout with the specified #PangoRenderer.
159  *
160  * Since: 1.8
161  **/
162 void
163 pango_renderer_draw_layout (PangoRenderer    *renderer,
164                             PangoLayout      *layout,
165                             int               x,
166                             int               y)
167 {
168   PangoLayoutIter *iter;
169
170   g_return_if_fail (PANGO_IS_RENDERER (renderer));
171   g_return_if_fail (PANGO_IS_LAYOUT (layout));
172
173   /* We only change the matrix if the renderer isn't already
174    * active.
175    */
176   if (!renderer->active_count)
177     {
178       PangoContext *context = pango_layout_get_context (layout);
179       pango_renderer_set_matrix (renderer,
180                                  pango_context_get_matrix (context));
181     }
182
183   pango_renderer_activate (renderer);
184
185   iter = pango_layout_get_iter (layout);
186
187   do
188     {
189       PangoRectangle   logical_rect;
190       PangoLayoutLine *line;
191       int              baseline;
192
193       line = pango_layout_iter_get_line_readonly (iter);
194
195       pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
196       baseline = pango_layout_iter_get_baseline (iter);
197
198       pango_renderer_draw_layout_line (renderer,
199                                        line,
200                                        x + logical_rect.x,
201                                        y + baseline);
202     }
203   while (pango_layout_iter_next_line (iter));
204
205   pango_layout_iter_free (iter);
206
207   pango_renderer_deactivate (renderer);
208 }
209
210 static void
211 draw_underline (PangoRenderer *renderer,
212                 LineState     *state)
213 {
214   PangoRectangle *rect = &state->underline_rect;
215   PangoUnderline underline = state->underline;
216
217   state->underline = PANGO_UNDERLINE_NONE;
218
219   switch (underline)
220     {
221     case PANGO_UNDERLINE_NONE:
222       break;
223     case PANGO_UNDERLINE_DOUBLE:
224       pango_renderer_draw_rectangle (renderer,
225                                      PANGO_RENDER_PART_UNDERLINE,
226                                      rect->x,
227                                      rect->y + 2 * rect->height,
228                                      rect->width,
229                                      rect->height);
230       /* Fall through */
231     case PANGO_UNDERLINE_SINGLE:
232     case PANGO_UNDERLINE_LOW:
233       pango_renderer_draw_rectangle (renderer,
234                                      PANGO_RENDER_PART_UNDERLINE,
235                                      rect->x,
236                                      rect->y,
237                                      rect->width,
238                                      rect->height);
239       break;
240     case PANGO_UNDERLINE_ERROR:
241       pango_renderer_draw_error_underline (renderer,
242                                            rect->x,
243                                            rect->y,
244                                            rect->width,
245                                            3 * rect->height);
246       break;
247     }
248 }
249
250 static void
251 draw_strikethrough (PangoRenderer *renderer,
252                     LineState     *state)
253 {
254   PangoRectangle *rect = &state->strikethrough_rect;
255   gboolean strikethrough = state->strikethrough;
256
257   state->strikethrough = FALSE;
258
259   if (strikethrough)
260     pango_renderer_draw_rectangle (renderer,
261                                    PANGO_RENDER_PART_STRIKETHROUGH,
262                                    rect->x,
263                                    rect->y,
264                                    rect->width,
265                                    rect->height);
266 }
267
268 static void
269 handle_line_state_change (PangoRenderer  *renderer,
270                           PangoRenderPart part)
271 {
272   LineState *state = renderer->priv->line_state;
273   if (!state)
274     return;
275
276   if (part == PANGO_RENDER_PART_UNDERLINE &&
277       state->underline != PANGO_UNDERLINE_NONE)
278     {
279       PangoRectangle *rect = &state->underline_rect;
280
281       rect->width = state->logical_rect_end - rect->x;
282       draw_underline (renderer, state);
283       state->underline = renderer->underline;
284       rect->x = state->logical_rect_end;
285       rect->width = 0;
286     }
287
288   if (part == PANGO_RENDER_PART_STRIKETHROUGH &&
289       state->strikethrough)
290     {
291       PangoRectangle *rect = &state->strikethrough_rect;
292
293       rect->width = state->logical_rect_end - rect->x;
294       draw_strikethrough (renderer, state);
295       state->strikethrough = renderer->strikethrough;
296       rect->x = state->logical_rect_end;
297       rect->width = 0;
298     }
299 }
300
301 static void
302 add_underline (PangoRenderer    *renderer,
303                LineState        *state,
304                PangoFontMetrics *metrics,
305                int               base_x,
306                int               base_y,
307                PangoRectangle   *ink_rect,
308                PangoRectangle   *logical_rect)
309 {
310   PangoRectangle *current_rect = &state->underline_rect;
311   PangoRectangle new_rect;
312
313   int underline_thickness = pango_font_metrics_get_underline_thickness (metrics);
314   int underline_position = pango_font_metrics_get_underline_position (metrics);
315
316   new_rect.x = base_x + logical_rect->x;
317   new_rect.width = logical_rect->width;
318   new_rect.height = underline_thickness;
319   new_rect.y = base_y;
320
321   switch (renderer->underline)
322     {
323     case PANGO_UNDERLINE_NONE:
324       g_assert_not_reached ();
325       break;
326     case PANGO_UNDERLINE_SINGLE:
327     case PANGO_UNDERLINE_DOUBLE:
328     case PANGO_UNDERLINE_ERROR:
329       new_rect.y -= underline_position;
330       break;
331     case PANGO_UNDERLINE_LOW:
332       new_rect.y += ink_rect->y + ink_rect->height + underline_thickness;
333       break;
334     }
335
336   if (renderer->underline == state->underline &&
337       new_rect.y == current_rect->y &&
338       new_rect.height == current_rect->height)
339     {
340       current_rect->width = new_rect.x + new_rect.width - current_rect->x;
341     }
342   else
343     {
344       draw_underline (renderer, state);
345
346       *current_rect = new_rect;
347       state->underline = renderer->underline;
348     }
349 }
350
351 static void
352 add_strikethrough (PangoRenderer    *renderer,
353                    LineState        *state,
354                    PangoFontMetrics *metrics,
355                    int               base_x,
356                    int               base_y,
357                    PangoRectangle   *ink_rect G_GNUC_UNUSED,
358                    PangoRectangle   *logical_rect)
359 {
360   PangoRectangle *current_rect = &state->strikethrough_rect;
361   PangoRectangle new_rect;
362
363   int strikethrough_thickness = pango_font_metrics_get_strikethrough_thickness (metrics);
364   int strikethrough_position = pango_font_metrics_get_strikethrough_position (metrics);
365
366   new_rect.x = base_x + logical_rect->x;
367   new_rect.width = logical_rect->width;
368   new_rect.y = base_y - strikethrough_position;
369   new_rect.height = strikethrough_thickness;
370
371   if (state->strikethrough &&
372       new_rect.y == current_rect->y &&
373       new_rect.height == current_rect->height)
374     {
375       current_rect->width = new_rect.x + new_rect.width - current_rect->x;
376     }
377   else
378     {
379       draw_strikethrough (renderer, state);
380
381       *current_rect = new_rect;
382       state->strikethrough = TRUE;
383     }
384 }
385
386 static void
387 get_item_properties (PangoItem       *item,
388                      gint            *rise,
389                      PangoAttrShape **shape_attr)
390 {
391   GSList *l;
392
393   if (rise)
394     *rise = 0;
395
396   if (shape_attr)
397     *shape_attr = NULL;
398
399   for (l = item->analysis.extra_attrs; l; l = l->next)
400     {
401       PangoAttribute *attr = l->data;
402
403       switch ((int) attr->klass->type)
404         {
405         case PANGO_ATTR_SHAPE:
406           if (shape_attr)
407             *shape_attr = (PangoAttrShape *)attr;
408           break;
409
410         case PANGO_ATTR_RISE:
411           if (rise)
412             *rise = ((PangoAttrInt *)attr)->value;
413           break;
414
415         default:
416           break;
417         }
418     }
419 }
420
421 static void
422 draw_shaped_glyphs (PangoRenderer    *renderer,
423                     PangoGlyphString *glyphs,
424                     PangoAttrShape   *attr,
425                     int               x,
426                     int               y)
427 {
428   PangoRendererClass *class = PANGO_RENDERER_GET_CLASS (renderer);
429   int i;
430
431   if (!class->draw_shape)
432     return;
433
434   for (i = 0; i < glyphs->num_glyphs; i++)
435     {
436       PangoGlyphInfo *gi = &glyphs->glyphs[i];
437
438       class->draw_shape (renderer, attr, x, y);
439
440       x += gi->geometry.width;
441     }
442 }
443
444
445 /**
446  * pango_renderer_draw_layout_line:
447  * @renderer: a #PangoRenderer
448  * @line: a #PangoLayoutLine
449  * @x: X position of left edge of baseline, in user space coordinates
450  *   in Pango units.
451  * @y: Y position of left edge of baseline, in user space coordinates
452  *    in Pango units.
453  *
454  * Draws @line with the specified #PangoRenderer.
455  *
456  * Since: 1.8
457  **/
458 void
459 pango_renderer_draw_layout_line (PangoRenderer    *renderer,
460                                  PangoLayoutLine  *line,
461                                  int               x,
462                                  int               y)
463 {
464   int x_off = 0;
465   int glyph_string_width;
466   LineState state;
467   GSList *l;
468   gboolean got_overall = FALSE;
469   PangoRectangle overall_rect;
470   const char *text;
471
472   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
473
474   /* We only change the matrix if the renderer isn't already
475    * active.
476    */
477   if (!renderer->active_count)
478     pango_renderer_set_matrix (renderer,
479                                G_LIKELY (line->layout) ?
480                                pango_context_get_matrix
481                                (pango_layout_get_context (line->layout)) :
482                                NULL);
483
484   pango_renderer_activate (renderer);
485
486   renderer->priv->line = line;
487   renderer->priv->line_state = &state;
488
489   state.underline = PANGO_UNDERLINE_NONE;
490   state.strikethrough = FALSE;
491
492   text = G_LIKELY (line->layout) ? pango_layout_get_text (line->layout) : NULL;
493
494   for (l = line->runs; l; l = l->next)
495     {
496       PangoFontMetrics *metrics;
497       gint rise;
498       PangoLayoutRun *run = l->data;
499       PangoAttrShape *shape_attr;
500       PangoRectangle ink_rect, *ink = NULL;
501       PangoRectangle logical_rect, *logical = NULL;
502
503       if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE)
504         logical = &logical_rect;
505
506       pango_renderer_prepare_run (renderer, run);
507
508       get_item_properties (run->item, &rise, &shape_attr);
509
510       if (shape_attr)
511         {
512           ink = &ink_rect;
513           logical = &logical_rect;
514           _pango_shape_get_extents (run->glyphs->num_glyphs,
515                                     &shape_attr->ink_rect,
516                                     &shape_attr->logical_rect,
517                                     ink,
518                                     logical);
519           glyph_string_width = logical->width;
520         }
521       else
522         {
523           if (renderer->underline != PANGO_UNDERLINE_NONE ||
524               renderer->strikethrough)
525             {
526               ink = &ink_rect;
527               logical = &logical_rect;
528             }
529           if (G_UNLIKELY (ink || logical))
530             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
531                                         ink, logical);
532           if (logical)
533             glyph_string_width = logical_rect.width;
534           else
535             glyph_string_width = pango_glyph_string_get_width (run->glyphs);
536         }
537
538       state.logical_rect_end = x + x_off + glyph_string_width;
539
540       if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE)
541         {
542           gboolean is_hinted = ((logical_rect.y | logical_rect.height) & (PANGO_SCALE - 1)) == 0;
543           int adjustment = logical_rect.y + logical_rect.height / 2;
544
545           if (is_hinted)
546             adjustment = PANGO_UNITS_ROUND (adjustment);
547
548           rise += adjustment;
549         }
550
551
552       if (renderer->priv->color_set[PANGO_RENDER_PART_BACKGROUND])
553         {
554           if (!got_overall)
555             {
556               pango_layout_line_get_extents (line, NULL, &overall_rect);
557               got_overall = TRUE;
558             }
559
560           pango_renderer_draw_rectangle (renderer,
561                                          PANGO_RENDER_PART_BACKGROUND,
562                                          x + x_off,
563                                          y + overall_rect.y,
564                                          glyph_string_width,
565                                          overall_rect.height);
566         }
567
568       if (shape_attr)
569         {
570           draw_shaped_glyphs (renderer, run->glyphs, shape_attr, x + x_off, y - rise);
571         }
572       else
573         {
574           pango_renderer_draw_glyph_item (renderer,
575                                           text,
576                                           run,
577                                           x + x_off, y - rise);
578         }
579
580       if (renderer->underline != PANGO_UNDERLINE_NONE ||
581           renderer->strikethrough)
582         {
583           metrics = pango_font_get_metrics (run->item->analysis.font,
584                                             run->item->analysis.language);
585
586           if (renderer->underline != PANGO_UNDERLINE_NONE)
587             add_underline (renderer, &state,metrics,
588                            x + x_off, y - rise,
589                            ink, logical);
590
591           if (renderer->strikethrough)
592             add_strikethrough (renderer, &state, metrics,
593                                x + x_off, y - rise,
594                                ink, logical);
595
596           pango_font_metrics_unref (metrics);
597         }
598
599       if (renderer->underline == PANGO_UNDERLINE_NONE &&
600           state.underline != PANGO_UNDERLINE_NONE)
601         draw_underline (renderer, &state);
602
603       if (!renderer->strikethrough && state.strikethrough)
604         draw_strikethrough (renderer, &state);
605
606       x_off += glyph_string_width;
607     }
608
609   /* Finish off any remaining underlines
610    */
611   draw_underline (renderer, &state);
612   draw_strikethrough (renderer, &state);
613
614   renderer->priv->line_state = NULL;
615   renderer->priv->line = NULL;
616
617   pango_renderer_deactivate (renderer);
618 }
619
620 /**
621  * pango_renderer_draw_glyphs:
622  * @renderer: a #PangoRenderer
623  * @font: a #PangoFont
624  * @glyphs: a #PangoGlyphString
625  * @x: X position of left edge of baseline, in user space coordinates
626  *   in Pango units.
627  * @y: Y position of left edge of baseline, in user space coordinates
628  *    in Pango units.
629  *
630  * Draws the glyphs in @glyphs with the specified #PangoRenderer.
631  *
632  * Since: 1.8
633  **/
634 void
635 pango_renderer_draw_glyphs (PangoRenderer    *renderer,
636                             PangoFont        *font,
637                             PangoGlyphString *glyphs,
638                             int               x,
639                             int               y)
640 {
641   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
642
643   pango_renderer_activate (renderer);
644
645   PANGO_RENDERER_GET_CLASS (renderer)->draw_glyphs (renderer, font, glyphs, x, y);
646
647   pango_renderer_deactivate (renderer);
648 }
649
650 static void
651 pango_renderer_default_draw_glyphs (PangoRenderer    *renderer,
652                                     PangoFont        *font,
653                                     PangoGlyphString *glyphs,
654                                     int               x,
655                                     int               y)
656 {
657   int i;
658   int x_position = 0;
659
660   for (i = 0; i < glyphs->num_glyphs; i++)
661     {
662       PangoGlyphInfo *gi = &glyphs->glyphs[i];
663       Point p;
664
665       to_device (renderer->matrix,
666                  x + x_position + gi->geometry.x_offset,
667                  y +              gi->geometry.y_offset,
668                  &p);
669
670       pango_renderer_draw_glyph (renderer, font, gi->glyph, p.x, p.y);
671
672       x_position += gi->geometry.width;
673     }
674 }
675
676 /**
677  * pango_renderer_draw_glyph_item:
678  * @renderer: a #PangoRenderer
679  * @text: the UTF-8 text that @glyph_item refers to, or %NULL
680  * @glyph_item: a #PangoGlyphItem
681  * @x: X position of left edge of baseline, in user space coordinates
682  *   in Pango units.
683  * @y: Y position of left edge of baseline, in user space coordinates
684  *    in Pango units.
685  *
686  * Draws the glyphs in @glyph_item with the specified #PangoRenderer,
687  * embedding the text associated with the glyphs in the output if the
688  * output format supports it (PDF for example).
689  *
690  * Note that @text is the start of the text for layout, which is then
691  * indexed by <literal>@glyph_item->item->offset</literal>.
692  *
693  * If @text is %NULL, this simply calls pango_renderer_draw_glyphs().
694  *
695  * The default implementation of this method simply falls back to
696  * pango_renderer_draw_glyphs().
697  *
698  * Since: 1.22
699  **/
700 void
701 pango_renderer_draw_glyph_item (PangoRenderer    *renderer,
702                                 const char       *text,
703                                 PangoGlyphItem   *glyph_item,
704                                 int               x,
705                                 int               y)
706 {
707   if (G_UNLIKELY (text))
708     {
709       pango_renderer_draw_glyphs (renderer,
710                                   glyph_item->item->analysis.font,
711                                   glyph_item->glyphs,
712                                   x, y);
713       return;
714     }
715
716   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
717
718   pango_renderer_activate (renderer);
719
720   PANGO_RENDERER_GET_CLASS (renderer)->draw_glyph_item (renderer, text, glyph_item, x, y);
721
722   pango_renderer_deactivate (renderer);
723 }
724
725 static void
726 pango_renderer_default_draw_glyph_item (PangoRenderer    *renderer,
727                                         const char       *text G_GNUC_UNUSED,
728                                         PangoGlyphItem   *glyph_item,
729                                         int               x,
730                                         int               y)
731 {
732   pango_renderer_draw_glyphs (renderer,
733                               glyph_item->item->analysis.font,
734                               glyph_item->glyphs,
735                               x, y);
736 }
737
738 /**
739  * pango_renderer_draw_rectangle:
740  * @renderer: a #PangoRenderer
741  * @part: type of object this rectangle is part of
742  * @x: X position at which to draw rectangle, in user space coordinates in Pango units
743  * @y: Y position at which to draw rectangle, in user space coordinates in Pango units
744  * @width: width of rectangle in Pango units in user space coordinates
745  * @height: height of rectangle in Pango units in user space coordinates
746  *
747  * Draws an axis-aligned rectangle in user space coordinates with the
748  * specified #PangoRenderer.
749  *
750  * This should be called while @renderer is already active.  Use
751  * pango_renderer_activate() to activate a renderer.
752  *
753  * Since: 1.8
754  **/
755 void
756 pango_renderer_draw_rectangle (PangoRenderer   *renderer,
757                                PangoRenderPart  part,
758                                int              x,
759                                int              y,
760                                int              width,
761                                int              height)
762 {
763   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
764   g_return_if_fail (IS_VALID_PART (part));
765   g_return_if_fail (renderer->active_count > 0);
766
767   PANGO_RENDERER_GET_CLASS (renderer)->draw_rectangle (renderer, part, x, y, width, height);
768 }
769
770 static int
771 compare_points (const void *a,
772                 const void *b)
773 {
774   const Point *pa = a;
775   const Point *pb = b;
776
777   if (pa->y < pb->y)
778     return -1;
779   else if (pa->y > pb->y)
780     return 1;
781   else if (pa->x < pb->x)
782     return -1;
783   else if (pa->x > pb->x)
784     return 1;
785   else
786     return 0;
787 }
788
789 static void
790 draw_rectangle (PangoRenderer   *renderer,
791                 PangoMatrix     *matrix,
792                 PangoRenderPart  part,
793                 int              x,
794                 int              y,
795                 int              width,
796                 int              height)
797 {
798   Point points[4];
799
800   /* Convert the points to device coordinates, and sort
801    * in ascending Y order. (Ordering by X for ties)
802    */
803   to_device (matrix, x, y, &points[0]);
804   to_device (matrix, x + width, y, &points[1]);
805   to_device (matrix, x, y + height, &points[2]);
806   to_device (matrix, x + width, y + height, &points[3]);
807
808   qsort (points, 4, sizeof (Point), compare_points);
809
810   /* There are essentially three cases. (There is a fourth
811    * case where trapezoid B is degenerate and we just have
812    * two triangles, but we don't need to handle it separately.)
813    *
814    *     1            2             3
815    *
816    *     ______       /\           /\
817    *    /     /      /A \         /A \
818    *   /  B  /      /____\       /____\
819    *  /_____/      /  B  /       \  B  \
820    *              /_____/         \_____\
821    *              \ C  /           \ C  /
822    *               \  /             \  /
823    *                \/               \/
824    */
825   if (points[0].y == points[1].y)
826     {
827      /* Case 1 (pure shear) */
828       pango_renderer_draw_trapezoid (renderer, part,                                      /* B */
829                                      points[0].y, points[0].x, points[1].x,
830                                      points[2].y, points[2].x, points[3].x);
831     }
832   else if (points[1].x < points[2].x)
833     {
834       /* Case 2 */
835       double tmp_width = ((points[2].x - points[0].x) * (points[1].y - points[0].y)) / (points[2].y - points[0].y);
836       double base_width = tmp_width + points[0].x - points[1].x;
837
838       pango_renderer_draw_trapezoid (renderer, part,                                      /* A */
839                                      points[0].y, points[0].x, points[0].x,
840                                      points[1].y, points[1].x, points[1].x + base_width);
841       pango_renderer_draw_trapezoid (renderer, part,                                      /* B */
842                                      points[1].y, points[1].x, points[1].x + base_width,
843                                      points[2].y, points[2].x - base_width, points[2].x);
844       pango_renderer_draw_trapezoid (renderer, part,                                      /* C */
845                                      points[2].y, points[2].x - base_width, points[2].x,
846                                      points[3].y, points[3].x, points[3].x);
847     }
848   else
849     {
850       /* case 3 */
851       double tmp_width = ((points[0].x - points[2].x) * (points[1].y - points[0].y)) / (points[2].y - points[0].y);
852       double base_width = tmp_width + points[1].x - points[0].x;
853
854       pango_renderer_draw_trapezoid (renderer, part,                                     /* A */
855                                      points[0].y, points[0].x, points[0].x,
856                                      points[1].y,  points[1].x - base_width, points[1].x);
857       pango_renderer_draw_trapezoid (renderer, part,                                     /* B */
858                                      points[1].y, points[1].x - base_width, points[1].x,
859                                      points[2].y, points[2].x, points[2].x + base_width);
860       pango_renderer_draw_trapezoid (renderer, part,                                     /* C */
861                                      points[2].y, points[2].x, points[2].x + base_width,
862                                      points[3].y, points[3].x, points[3].x);
863     }
864 }
865
866 static void
867 pango_renderer_default_draw_rectangle (PangoRenderer  *renderer,
868                                        PangoRenderPart part,
869                                        int             x,
870                                        int             y,
871                                        int             width,
872                                        int             height)
873 {
874   draw_rectangle (renderer, renderer->matrix, part, x, y, width, height);
875 }
876
877 /**
878  * pango_renderer_draw_error_underline:
879  * @renderer: a #PangoRenderer
880  * @x: X coordinate of underline, in Pango units in user coordinate system
881  * @y: Y coordinate of underline, in Pango units in user coordinate system
882  * @width: width of underline, in Pango units in user coordinate system
883  * @height: height of underline, in Pango units in user coordinate system
884  *
885  * Draw a squiggly line that approximately covers the given rectangle
886  * in the style of an underline used to indicate a spelling error.
887  * (The width of the underline is rounded to an integer number
888  * of up/down segments and the resulting rectangle is centered
889  * in the original rectangle)
890  *
891  * This should be called while @renderer is already active.  Use
892  * pango_renderer_activate() to activate a renderer.
893  *
894  * Since: 1.8
895  **/
896 void
897 pango_renderer_draw_error_underline (PangoRenderer *renderer,
898                                      int            x,
899                                      int            y,
900                                      int            width,
901                                      int            height)
902 {
903   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
904   g_return_if_fail (renderer->active_count > 0);
905
906   PANGO_RENDERER_GET_CLASS (renderer)->draw_error_underline (renderer, x, y, width, height);
907 }
908
909 /* We are drawing an error underline that looks like one of:
910  *
911  *  /\      /\      /\        /\      /\               -
912  * /  \    /  \    /  \      /  \    /  \              |
913  * \   \  /\   \  /   /      \   \  /\   \             |
914  *  \   \/B \   \/ C /        \   \/B \   \            | height = HEIGHT_SQUARES * square
915  *   \ A \  /\ A \  /          \ A \  /\ A \           |
916  *    \   \/  \   \/            \   \/  \   \          |
917  *     \  /    \  /              \  /    \  /          |
918  *      \/      \/                \/      \/           -
919  *      |---|
920  *    unit_width = (HEIGHT_SQUARES - 1) * square
921  *
922  * To do this conveniently, we work in a coordinate system where A,B,C
923  * are axis aligned rectangles. (If fonts were square, the diagrams
924  * would be clearer)
925  *
926  *             (0,0)
927  *              /\      /\
928  *             /  \    /  \
929  *            /\  /\  /\  /
930  *           /  \/  \/  \/
931  *          /    \  /\  /
932  *      Y axis    \/  \/
933  *                 \  /\
934  *                  \/  \
935  *                       \ X axis
936  *
937  * Note that the long side in this coordinate system is HEIGHT_SQUARES + 1
938  * units long
939  *
940  * The diagrams above are shown with HEIGHT_SQUARES an integer, but
941  * that is actually incidental; the value 2.5 below seems better than
942  * either HEIGHT_SQUARES=3 (a little long and skinny) or
943  * HEIGHT_SQUARES=2 (a bit short and stubby)
944  */
945
946 #define HEIGHT_SQUARES 2.5
947
948 static void
949 get_total_matrix (PangoMatrix       *total,
950                   const PangoMatrix *global,
951                   int                x,
952                   int                y,
953                   int                square)
954 {
955   PangoMatrix local;
956   gdouble scale = 0.5 * square;
957
958   /* The local matrix translates from the axis aligned coordinate system
959    * to the original user space coordinate system.
960    */
961   local.xx = scale;
962   local.xy = - scale;
963   local.yx = scale;
964   local.yy = scale;
965   local.x0 = 0;
966   local.y0 = 0;
967
968   *total = *global;
969   pango_matrix_concat (total, &local);
970
971   total->x0 = (global->xx * x + global->xy * y) / PANGO_SCALE + global->x0;
972   total->y0 = (global->yx * x + global->yy * y) / PANGO_SCALE + global->y0;
973 }
974
975 static void
976 pango_renderer_default_draw_error_underline (PangoRenderer *renderer,
977                                              int            x,
978                                              int            y,
979                                              int            width,
980                                              int            height)
981 {
982   int square = height / HEIGHT_SQUARES;
983   int unit_width = (HEIGHT_SQUARES - 1) * square;
984   int width_units = (width + unit_width / 2) / unit_width;
985   static const PangoMatrix identity = PANGO_MATRIX_INIT;
986   const PangoMatrix *matrix;
987   double dx, dx0, dy0;
988   PangoMatrix total;
989   int i;
990
991   x += (width - width_units * unit_width) / 2;
992   width = width_units * unit_width;
993
994   if (renderer->matrix)
995     matrix = renderer->matrix;
996   else
997     matrix = &identity;
998
999   get_total_matrix (&total, matrix, x, y, square);
1000   dx = unit_width * 2;
1001   dx0 = (matrix->xx * dx) / PANGO_SCALE;
1002   dy0 = (matrix->yx * dx) / PANGO_SCALE;
1003
1004   i = (width_units - 1) / 2;
1005   while (TRUE)
1006     {
1007       draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* A */
1008                       0,                      0,
1009                       HEIGHT_SQUARES * 2 - 1, 1);
1010
1011       if (i <= 0)
1012         break;
1013       i--;
1014
1015       draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* B */
1016                       HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 3),
1017                       1,                      HEIGHT_SQUARES * 2 - 3);
1018
1019       total.x0 += dx0;
1020       total.y0 += dy0;
1021     }
1022   if (width_units % 2 == 0)
1023     {
1024       draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* C */
1025                       HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 2),
1026                       1,                      HEIGHT_SQUARES * 2 - 2);
1027     }
1028 }
1029
1030 /**
1031  * pango_renderer_draw_trapezoid:
1032  * @renderer: a #PangoRenderer
1033  * @part: type of object this trapezoid is part of
1034  * @y1_: Y coordinate of top of trapezoid
1035  * @x11: X coordinate of left end of top of trapezoid
1036  * @x21: X coordinate of right end of top of trapezoid
1037  * @y2: Y coordinate of bottom of trapezoid
1038  * @x12: X coordinate of left end of bottom of trapezoid
1039  * @x22: X coordinate of right end of bottom of trapezoid
1040  *
1041  * Draws a trapezoid with the parallel sides aligned with the X axis
1042  * using the given #PangoRenderer; coordinates are in device space.
1043  *
1044  * Since: 1.8
1045  **/
1046 void
1047 pango_renderer_draw_trapezoid (PangoRenderer  *renderer,
1048                                PangoRenderPart part,
1049                                double          y1_,
1050                                double          x11,
1051                                double          x21,
1052                                double          y2,
1053                                double          x12,
1054                                double          x22)
1055 {
1056   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1057   g_return_if_fail (renderer->active_count > 0);
1058
1059   if (PANGO_RENDERER_GET_CLASS (renderer)->draw_trapezoid)
1060     PANGO_RENDERER_GET_CLASS (renderer)->draw_trapezoid (renderer, part,
1061                                                          y1_, x11, x21,
1062                                                          y2, x12, x22);
1063 }
1064
1065 /**
1066  * pango_renderer_draw_glyph:
1067  * @renderer: a #PangoRenderer
1068  * @font: a #PangoFont
1069  * @glyph: the glyph index of a single glyph
1070  * @x: X coordinate of left edge of baseline of glyph
1071  * @y: Y coordinate of left edge of baseline of glyph
1072  *
1073  * Draws a single glyph with coordinates in device space.
1074  *
1075  * Since: 1.8
1076  **/
1077 void
1078 pango_renderer_draw_glyph (PangoRenderer *renderer,
1079                            PangoFont     *font,
1080                            PangoGlyph     glyph,
1081                            double         x,
1082                            double         y)
1083 {
1084   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1085   g_return_if_fail (renderer->active_count > 0);
1086
1087   if (glyph == PANGO_GLYPH_EMPTY) /* glyph PANGO_GLYPH_EMPTY never renders */
1088     return;
1089
1090   if (PANGO_RENDERER_GET_CLASS (renderer)->draw_glyph)
1091     PANGO_RENDERER_GET_CLASS (renderer)->draw_glyph (renderer, font, glyph, x, y);
1092 }
1093
1094 /**
1095  * pango_renderer_activate:
1096  * @renderer: a #PangoRenderer
1097  *
1098  * Does initial setup before rendering operations on @renderer.
1099  * pango_renderer_deactivate() should be called when done drawing.
1100  * Calls such as pango_renderer_draw_layout() automatically
1101  * activate the layout before drawing on it. Calls to
1102  * pango_renderer_activate() and pango_renderer_deactivate() can
1103  * be nested and the renderer will only be initialized and
1104  * deinitialized once.
1105  *
1106  * Since: 1.8
1107  **/
1108 void
1109 pango_renderer_activate (PangoRenderer *renderer)
1110 {
1111   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1112
1113   renderer->active_count++;
1114   if (renderer->active_count == 1)
1115     {
1116       if (PANGO_RENDERER_GET_CLASS (renderer)->begin)
1117         PANGO_RENDERER_GET_CLASS (renderer)->begin (renderer);
1118     }
1119 }
1120
1121 /**
1122  * pango_renderer_deactivate:
1123  * @renderer: a #PangoRenderer
1124  *
1125  * Cleans up after rendering operations on @renderer. See
1126  * docs for pango_renderer_activate().
1127  *
1128  * Since: 1.8
1129  **/
1130 void
1131 pango_renderer_deactivate (PangoRenderer *renderer)
1132 {
1133   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1134   g_return_if_fail (renderer->active_count > 0);
1135
1136   if (renderer->active_count == 1)
1137     {
1138       if (PANGO_RENDERER_GET_CLASS (renderer)->end)
1139         PANGO_RENDERER_GET_CLASS (renderer)->end (renderer);
1140     }
1141   renderer->active_count--;
1142 }
1143
1144 /**
1145  * pango_renderer_set_color:
1146  * @renderer: a #PangoRenderer
1147  * @part: the part to change the color of
1148  * @color: the new color or %NULL to unset the current color
1149  *
1150  * Sets the color for part of the rendering.
1151  *
1152  * Since: 1.8
1153  **/
1154 void
1155 pango_renderer_set_color (PangoRenderer    *renderer,
1156                           PangoRenderPart   part,
1157                           const PangoColor *color)
1158 {
1159   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1160   g_return_if_fail (IS_VALID_PART (part));
1161
1162   if ((!color && !renderer->priv->color_set[part]) ||
1163       (color && renderer->priv->color_set[part] &&
1164        renderer->priv->color[part].red == color->red &&
1165        renderer->priv->color[part].green == color->green &&
1166        renderer->priv->color[part].blue == color->blue))
1167     return;
1168
1169   pango_renderer_part_changed (renderer, part);
1170
1171   if (color)
1172     {
1173       renderer->priv->color_set[part] = TRUE;
1174       renderer->priv->color[part] = *color;
1175     }
1176   else
1177     {
1178       renderer->priv->color_set[part] = FALSE;
1179     }
1180 }
1181
1182 /**
1183  * pango_renderer_get_color:
1184  * @renderer: a #PangoRenderer
1185  * @part: the part to get the color for
1186  *
1187  * Gets the current rendering color for the specified part.
1188  *
1189  * Return value: the color for the specified part, or %NULL
1190  *  if it hasn't been set and should be inherited from the
1191  *  environment.
1192  *
1193  * Since: 1.8
1194  **/
1195 PangoColor *
1196 pango_renderer_get_color (PangoRenderer   *renderer,
1197                           PangoRenderPart  part)
1198 {
1199   g_return_val_if_fail (PANGO_IS_RENDERER_FAST (renderer), NULL);
1200   g_return_val_if_fail (IS_VALID_PART (part), NULL);
1201
1202   if (renderer->priv->color_set[part])
1203     return &renderer->priv->color[part];
1204   else
1205     return NULL;
1206 }
1207
1208 /**
1209  * pango_renderer_part_changed:
1210  * @renderer: a #PangoRenderer
1211  * @part: the part for which rendering has changed.
1212  *
1213  * Informs Pango that the way that the rendering is done
1214  * for @part has changed in a way that would prevent multiple
1215  * pieces being joined together into one drawing call. For
1216  * instance, if a subclass of #PangoRenderer was to add a stipple
1217  * option for drawing underlines, it needs to call
1218  *
1219  * <informalexample><programlisting>
1220  * pango_renderer_part_changed (render, PANGO_RENDER_PART_UNDERLINE);
1221  * </programlisting></informalexample>
1222  *
1223  * When the stipple changes or underlines with different stipples
1224  * might be joined together. Pango automatically calls this for
1225  * changes to colors. (See pango_renderer_set_color())
1226  *
1227  * Since: 1.8
1228  **/
1229 void
1230 pango_renderer_part_changed (PangoRenderer    *renderer,
1231                              PangoRenderPart   part)
1232 {
1233   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1234   g_return_if_fail (IS_VALID_PART (part));
1235   g_return_if_fail (renderer->active_count > 0);
1236
1237   handle_line_state_change (renderer, part);
1238
1239   if (PANGO_RENDERER_GET_CLASS (renderer)->part_changed)
1240     PANGO_RENDERER_GET_CLASS (renderer)->part_changed (renderer, part);
1241 }
1242
1243 /**
1244  * pango_renderer_prepare_run:
1245  * @renderer: a #PangoRenderer
1246  * @run: a #PangoLayoutRun
1247  *
1248  * Set up the state of the #PangoRenderer for rendering @run.
1249  *
1250  * Since: 1.8
1251  **/
1252 static void
1253 pango_renderer_prepare_run (PangoRenderer  *renderer,
1254                             PangoLayoutRun *run)
1255 {
1256   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1257
1258   PANGO_RENDERER_GET_CLASS (renderer)->prepare_run (renderer, run);
1259 }
1260
1261 static void
1262 pango_renderer_default_prepare_run (PangoRenderer  *renderer,
1263                                     PangoLayoutRun *run)
1264 {
1265   PangoColor *fg_color = NULL;
1266   PangoColor *bg_color = NULL;
1267   PangoColor *underline_color = NULL;
1268   PangoColor *strikethrough_color = NULL;
1269   GSList *l;
1270
1271   renderer->underline = PANGO_UNDERLINE_NONE;
1272   renderer->strikethrough = FALSE;
1273
1274   for (l = run->item->analysis.extra_attrs; l; l = l->next)
1275     {
1276       PangoAttribute *attr = l->data;
1277
1278       switch ((int) attr->klass->type)
1279         {
1280         case PANGO_ATTR_UNDERLINE:
1281           renderer->underline = ((PangoAttrInt *)attr)->value;
1282           break;
1283
1284         case PANGO_ATTR_STRIKETHROUGH:
1285           renderer->strikethrough = ((PangoAttrInt *)attr)->value;
1286           break;
1287
1288         case PANGO_ATTR_FOREGROUND:
1289           fg_color = &((PangoAttrColor *)attr)->color;
1290           break;
1291
1292         case PANGO_ATTR_BACKGROUND:
1293           bg_color = &((PangoAttrColor *)attr)->color;
1294           break;
1295
1296         case PANGO_ATTR_UNDERLINE_COLOR:
1297           underline_color = &((PangoAttrColor *)attr)->color;
1298           break;
1299
1300         case PANGO_ATTR_STRIKETHROUGH_COLOR:
1301           strikethrough_color = &((PangoAttrColor *)attr)->color;
1302           break;
1303
1304         default:
1305           break;
1306         }
1307     }
1308
1309   if (!underline_color)
1310     underline_color = fg_color;
1311
1312   if (!strikethrough_color)
1313     strikethrough_color = fg_color;
1314
1315   pango_renderer_set_color (renderer, PANGO_RENDER_PART_FOREGROUND, fg_color);
1316   pango_renderer_set_color (renderer, PANGO_RENDER_PART_BACKGROUND, bg_color);
1317   pango_renderer_set_color (renderer, PANGO_RENDER_PART_UNDERLINE, underline_color);
1318   pango_renderer_set_color (renderer, PANGO_RENDER_PART_STRIKETHROUGH, strikethrough_color);
1319 }
1320
1321 /**
1322  * pango_renderer_set_matrix:
1323  * @renderer: a #PangoRenderer
1324  * @matrix: a #PangoMatrix, or %NULL to unset any existing matrix.
1325  *  (No matrix set is the same as setting the identity matrix.)
1326  *
1327  * Sets the transformation matrix that will be applied when rendering.
1328  *
1329  * Since: 1.8
1330  **/
1331 void
1332 pango_renderer_set_matrix (PangoRenderer     *renderer,
1333                            const PangoMatrix *matrix)
1334 {
1335   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1336
1337   pango_matrix_free (renderer->matrix);
1338   renderer->matrix = pango_matrix_copy (matrix);
1339 }
1340
1341 /**
1342  * pango_renderer_get_matrix:
1343  * @renderer: a #PangoRenderer
1344  *
1345  * Gets the transformation matrix that will be applied when
1346  * rendering. See pango_renderer_set_matrix().
1347  *
1348  * Return value: the matrix, or %NULL if no matrix has been set
1349  *  (which is the same as the identity matrix). The returned
1350  *  matrix is owned by Pango and must not be modified or
1351  *  freed.
1352  *
1353  * Since: 1.8
1354  **/
1355 G_CONST_RETURN PangoMatrix *
1356 pango_renderer_get_matrix (PangoRenderer *renderer)
1357 {
1358   g_return_val_if_fail (PANGO_IS_RENDERER (renderer), NULL);
1359
1360   return renderer->matrix;
1361 }
1362
1363 /**
1364  * pango_renderer_get_layout:
1365  * @renderer: a #PangoRenderer
1366  *
1367  * Gets the layout currently being rendered using @renderer.
1368  * Calling this function only makes sense from inside a subclass's
1369  * methods, like in its draw_shape<!---->() for example.
1370  *
1371  * The returned layout should not be modified while still being
1372  * rendered.
1373  *
1374  * Return value: the layout, or %NULL if no layout is being
1375  *  rendered using @renderer at this time.
1376  *
1377  * Since: 1.20
1378  **/
1379 PangoLayout *
1380 pango_renderer_get_layout (PangoRenderer *renderer)
1381 {
1382   if (G_UNLIKELY (renderer->priv->line == NULL))
1383     return NULL;
1384
1385   return renderer->priv->line->layout;
1386 }
1387
1388 /**
1389  * pango_renderer_get_layout_line:
1390  * @renderer: a #PangoRenderer
1391  *
1392  * Gets the layout line currently being rendered using @renderer.
1393  * Calling this function only makes sense from inside a subclass's
1394  * methods, like in its draw_shape<!---->() for example.
1395  *
1396  * The returned layout line should not be modified while still being
1397  * rendered.
1398  *
1399  * Return value: the layout line, or %NULL if no layout line is being
1400  *  rendered using @renderer at this time.
1401  *
1402  * Since: 1.20
1403  **/
1404 PangoLayoutLine *
1405 pango_renderer_get_layout_line (PangoRenderer     *renderer)
1406 {
1407   return renderer->priv->line;
1408 }