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 |
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 | |
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 |
132 | pango_renderer_init (PangoRenderer *renderer) |
133 | { |
134 | renderer->priv = pango_renderer_get_instance_private (self: renderer); |
135 | renderer->matrix = NULL; |
136 | } |
137 | |
138 | static void |
139 | pango_renderer_finalize (GObject *gobject) |
140 | { |
141 | PangoRenderer *renderer = PANGO_RENDERER (gobject); |
142 | |
143 | if (renderer->matrix) |
144 | pango_matrix_free (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 | */ |
165 | void |
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 | 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 | |
213 | static void |
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 | 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 | |
258 | static void |
259 | draw_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 | |
284 | static void |
285 | draw_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 | |
307 | static void |
308 | handle_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 | |
350 | static void |
351 | add_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 | |
414 | static void |
415 | add_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 | |
467 | static void |
468 | add_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 | |
503 | static void |
504 | get_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 | |
529 | static void |
530 | draw_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 | */ |
570 | void |
571 | pango_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 | */ |
763 | void |
764 | pango_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 | |
779 | static void |
780 | pango_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 | */ |
836 | void |
837 | pango_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 | |
861 | static void |
862 | pango_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 | */ |
893 | void |
894 | pango_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 | |
908 | static int |
909 | compare_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 | |
927 | static void |
928 | draw_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 | |
1004 | static void |
1005 | pango_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 | */ |
1035 | void |
1036 | pango_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 | |
1087 | static void |
1088 | get_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 | |
1114 | static void |
1115 | pango_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 | */ |
1191 | void |
1192 | pango_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 | */ |
1222 | void |
1223 | pango_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 | */ |
1255 | void |
1256 | pango_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 | */ |
1278 | void |
1279 | pango_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 | */ |
1304 | void |
1305 | pango_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 | */ |
1345 | PangoColor * |
1346 | pango_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 | */ |
1371 | void |
1372 | pango_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 | */ |
1402 | guint16 |
1403 | pango_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 | */ |
1435 | void |
1436 | pango_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 | */ |
1458 | static void |
1459 | pango_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 | |
1467 | static void |
1468 | pango_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 | */ |
1567 | void |
1568 | pango_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 | */ |
1592 | const PangoMatrix * |
1593 | pango_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 | */ |
1617 | PangoLayout * |
1618 | pango_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 | */ |
1643 | PangoLayoutLine * |
1644 | pango_renderer_get_layout_line (PangoRenderer *renderer) |
1645 | { |
1646 | return renderer->priv->line; |
1647 | } |
1648 | |