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