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

source code of gtk/subprojects/pango/pango/pango-renderer.c