1 /*
2 * Copyright (C) 2003,2008 Red Hat, Inc.
3 *
4 * This is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Library General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19
20 #include <config.h>
21
22 #include <sys/param.h>
23 #include <string.h>
24 #include <gtk/gtk.h>
25 #include <glib.h>
26 #include "debug.h"
27 #include "vtebg.h"
28 #include "vtedraw.h"
29 #include "vte-private.h"
30
31 #include <pango/pangocairo.h>
32
33
34 /* Overview:
35 *
36 *
37 * This file implements vte rendering using pangocairo. Note that this does
38 * NOT implement any kind of complex text rendering. That's not currently a
39 * goal.
40 *
41 * The aim is to be super-fast and avoid unneeded work as much as possible.
42 * Here is an overview of how that is accomplished:
43 *
44 * - We attach a font_info to the draw. A font_info has all the information
45 * to quickly draw text.
46 *
47 * - A font_info keeps uses unistr_font_info structs that represent all
48 * information needed to quickly draw a single vteunistr. The font_info
49 * creates those unistr_font_info structs on demand and caches them
50 * indefinitely. It uses a direct array for the ASCII range and a hash
51 * table for the rest.
52 *
53 *
54 * Fast rendering of unistrs:
55 *
56 * A unistr_font_info (uinfo) calls Pango to set text for the unistr upon
57 * initialization and then caches information needed to draw the results
58 * later. It uses three different internal representations and respectively
59 * three drawing paths:
60 *
61 * - COVERAGE_USE_CAIRO_GLYPH:
62 * Keeping a single glyph index and a cairo scaled-font. This is the
63 * fastest way to draw text as it bypasses Pango completely and allows
64 * for stuffing multiple glyphs into a single cairo_show_glyphs() request
65 * (if scaled-fonts match). This method is used if the glyphs used for
66 * the vteunistr as determined by Pango consists of a single regular glyph
67 * positioned at 0,0 using a regular font. This method is used for more
68 * than 99% of the cases. Only exceptional cases fall through to the
69 * other two methods.
70 *
71 * - COVERAGE_USE_PANGO_GLYPH_STRING:
72 * Keeping a pango glyphstring and a pango font. This is slightly slower
73 * than the previous case as drawing each glyph goes through pango
74 * separately and causes a separate cairo_show_glyphs() call. This method
75 * is used when the previous method cannot be used but the glyphs for the
76 * character all use a single font. This is the method used for hexboxes
77 * and "empty" characters like U+200C ZERO WIDTH NON-JOINER for example.
78 *
79 * - COVERAGE_USE_PANGO_LAYOUT_LINE:
80 * Keeping a pango layout line. This method is used only in the very
81 * weird and exceptional case that a single vteunistr uses more than one
82 * font to be drawn. This happens for example if some diacretics is not
83 * available in the font chosen for the base character.
84 *
85 *
86 * Caching of font infos:
87 *
88 * To avoid recreating font info structs for the same font again and again we
89 * do the following:
90 *
91 * - Use a global cache to share font info structs across different widgets.
92 * We use pango language, cairo font options, resolution, and font description
93 * as the key for our hash table.
94 *
95 * - When a font info struct is no longer used by any widget, we delay
96 * destroying it for a while (FONT_CACHE_TIMEOUT seconds). This is
97 * supposed to serve two purposes:
98 *
99 * * Destroying a terminal widget and creating it again right after will
100 * reuse the font info struct from the previous widget.
101 *
102 * * Zooming in and out a terminal reuses the font info structs.
103 *
104 *
105 * Pre-caching ASCII letters:
106 *
107 * When initializing a font info struct we measure a string consisting of all
108 * ASCII letters and some other ASCII characters. Since we have a shaped pango
109 * layout at hand, we walk over it and cache unistr font info for the ASCII
110 * letters if we can do that easily using COVERAGE_USE_CAIRO_GLYPH. This
111 * means that we precache all ASCII letters without any extra pango shaping
112 * involved.
113 */
114
115
116
117 #define FONT_CACHE_TIMEOUT (30) /* seconds */
118
119
120 /* All shared data structures are implicitly protected by GDK mutex, because
121 * that's how vte.c works and we only get called from there. */
122
123
124 /* cairo_show_glyphs accepts runs up to 102 glyphs before it allocates a
125 * temporary array.
126 *
127 * Setting this to a large value can cause dramatic slow-downs for some
128 * xservers (notably fglrx), see bug #410534.
129 *
130 * Moreover, setting it larger than %VTE_DRAW_MAX_LENGTH is nonsensical,
131 * as the higher layers will not submit runs longer than that value.
132 */
133 #define MAX_RUN_LENGTH 100
134
135
136 enum unistr_coverage {
137 /* in increasing order of speed */
138 COVERAGE_UNKNOWN = 0, /* we don't know about the character yet */
139 COVERAGE_USE_PANGO_LAYOUT_LINE, /* use a PangoLayoutLine for the character */
140 COVERAGE_USE_PANGO_GLYPH_STRING, /* use a PangoGlyphString for the character */
141 COVERAGE_USE_CAIRO_GLYPH /* use a cairo_glyph_t for the character */
142 };
143
144 union unistr_font_info {
145 /* COVERAGE_USE_PANGO_LAYOUT_LINE */
146 struct {
147 PangoLayoutLine *line;
148 } using_pango_layout_line;
149 /* COVERAGE_USE_PANGO_GLYPH_STRING */
150 struct {
151 PangoFont *font;
152 PangoGlyphString *glyph_string;
153 } using_pango_glyph_string;
154 /* COVERAGE_USE_CAIRO_GLYPH */
155 struct {
156 cairo_scaled_font_t *scaled_font;
157 unsigned int glyph_index;
158 } using_cairo_glyph;
159 };
160
161 struct unistr_info {
162 guchar coverage;
163 guchar has_unknown_chars;
164 guint16 width;
165 union unistr_font_info ufi;
166 };
167
168 static struct unistr_info *
unistr_info_create(void)169 unistr_info_create (void)
170 {
171 return g_slice_new0 (struct unistr_info);
172 }
173
174 static void
unistr_info_finish(struct unistr_info * uinfo)175 unistr_info_finish (struct unistr_info *uinfo)
176 {
177 union unistr_font_info *ufi = &uinfo->ufi;
178
179 switch (uinfo->coverage) {
180 default:
181 case COVERAGE_UNKNOWN:
182 break;
183 case COVERAGE_USE_PANGO_LAYOUT_LINE:
184 /* we hold a manual reference on layout */
185 g_object_unref (ufi->using_pango_layout_line.line->layout);
186 ufi->using_pango_layout_line.line->layout = NULL;
187 pango_layout_line_unref (ufi->using_pango_layout_line.line);
188 ufi->using_pango_layout_line.line = NULL;
189 break;
190 case COVERAGE_USE_PANGO_GLYPH_STRING:
191 if (ufi->using_pango_glyph_string.font)
192 g_object_unref (ufi->using_pango_glyph_string.font);
193 ufi->using_pango_glyph_string.font = NULL;
194 pango_glyph_string_free (ufi->using_pango_glyph_string.glyph_string);
195 ufi->using_pango_glyph_string.glyph_string = NULL;
196 break;
197 case COVERAGE_USE_CAIRO_GLYPH:
198 cairo_scaled_font_destroy (ufi->using_cairo_glyph.scaled_font);
199 ufi->using_cairo_glyph.scaled_font = NULL;
200 break;
201 }
202 }
203
204 static void
unistr_info_destroy(struct unistr_info * uinfo)205 unistr_info_destroy (struct unistr_info *uinfo)
206 {
207 unistr_info_finish (uinfo);
208 g_slice_free (struct unistr_info, uinfo);
209 }
210
211 struct font_info {
212 /* lifecycle */
213 int ref_count;
214 guint destroy_timeout; /* only used when ref_count == 0 */
215
216 /* reusable layout set with font and everything set */
217 PangoLayout *layout;
218
219 /* cache of character info */
220 struct unistr_info ascii_unistr_info[128];
221 GHashTable *other_unistr_info;
222
223 /* cell metrics */
224 gint width, height, ascent;
225
226 /* reusable string for UTF-8 conversion */
227 GString *string;
228
229 #ifdef VTE_DEBUG
230 /* profiling info */
231 int coverage_count[4];
232 #endif
233 };
234
235
236 static struct unistr_info *
font_info_find_unistr_info(struct font_info * info,vteunistr c)237 font_info_find_unistr_info (struct font_info *info,
238 vteunistr c)
239 {
240 struct unistr_info *uinfo;
241
242 if (G_LIKELY (c < G_N_ELEMENTS (info->ascii_unistr_info)))
243 return &info->ascii_unistr_info[c];
244
245 if (G_UNLIKELY (info->other_unistr_info == NULL))
246 info->other_unistr_info = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) unistr_info_destroy);
247
248 uinfo = g_hash_table_lookup (info->other_unistr_info, GINT_TO_POINTER (c));
249 if (G_LIKELY (uinfo))
250 return uinfo;
251
252 uinfo = unistr_info_create ();
253 g_hash_table_insert (info->other_unistr_info, GINT_TO_POINTER (c), uinfo);
254 return uinfo;
255 }
256
257
258 static void
font_info_cache_ascii(struct font_info * info)259 font_info_cache_ascii (struct font_info *info)
260 {
261 PangoLayoutLine *line;
262 PangoGlyphItemIter iter;
263 PangoGlyphItem *glyph_item;
264 PangoGlyphString *glyph_string;
265 PangoFont *pango_font;
266 cairo_scaled_font_t *scaled_font;
267 const char *text;
268 gboolean more;
269 PangoLanguage *language;
270 gboolean latin_uses_default_language;
271
272 /* We have info->layout holding most ASCII characters. We want to
273 * cache as much info as we can about the ASCII letters so we don't
274 * have to look them up again later */
275
276 /* Don't cache if unknown glyphs found in layout */
277 if (pango_layout_get_unknown_glyphs_count (info->layout) != 0)
278 return;
279
280 language = pango_context_get_language (pango_layout_get_context (info->layout));
281 if (language == NULL)
282 language = pango_language_get_default ();
283 latin_uses_default_language = pango_language_includes_script (language, PANGO_SCRIPT_LATIN);
284
285 text = pango_layout_get_text (info->layout);
286
287 line = pango_layout_get_line_readonly (info->layout, 0);
288
289 /* Don't cache if more than one font used for the line */
290 if (G_UNLIKELY (!line || !line->runs || line->runs->next))
291 return;
292
293 glyph_item = line->runs->data;
294 glyph_string = glyph_item->glyphs;
295 pango_font = glyph_item->item->analysis.font;
296 if (!pango_font)
297 return;
298 scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) pango_font);
299 if (!scaled_font)
300 return;
301
302 for (more = pango_glyph_item_iter_init_start (&iter, glyph_item, text);
303 more;
304 more = pango_glyph_item_iter_next_cluster (&iter))
305 {
306 struct unistr_info *uinfo;
307 union unistr_font_info *ufi;
308 PangoGlyphGeometry *geometry;
309 PangoGlyph glyph;
310 vteunistr c;
311
312 /* Only cache simple clusters */
313 if (iter.start_char +1 != iter.end_char ||
314 iter.start_index+1 != iter.end_index ||
315 iter.start_glyph+1 != iter.end_glyph)
316 continue;
317
318 c = text[iter.start_index];
319 glyph = glyph_string->glyphs[iter.start_glyph].glyph;
320 geometry = &glyph_string->glyphs[iter.start_glyph].geometry;
321
322 /* If not using the default locale language, only cache non-common
323 * characters as common characters get their font from their neighbors
324 * and we don't want to force Latin on them. */
325 if (!latin_uses_default_language &&
326 pango_script_for_unichar (c) <= PANGO_SCRIPT_INHERITED)
327 continue;
328
329 /* Only cache simple glyphs */
330 if (!(glyph <= 0xFFFF) || (geometry->x_offset | geometry->y_offset) != 0)
331 continue;
332
333 uinfo = font_info_find_unistr_info (info, c);
334 if (G_UNLIKELY (uinfo->coverage != COVERAGE_UNKNOWN))
335 continue;
336
337 ufi = &uinfo->ufi;
338
339 uinfo->width = PANGO_PIXELS_CEIL (geometry->width);
340 uinfo->has_unknown_chars = FALSE;
341
342 uinfo->coverage = COVERAGE_USE_CAIRO_GLYPH;
343
344 ufi->using_cairo_glyph.scaled_font = cairo_scaled_font_reference (scaled_font);
345 ufi->using_cairo_glyph.glyph_index = glyph;
346
347 #ifdef VTE_DEBUG
348 info->coverage_count[0]++;
349 info->coverage_count[uinfo->coverage]++;
350 #endif
351 }
352
353 #ifdef VTE_DEBUG
354 _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
355 "vtepangocairo: %p cached %d ASCII letters\n",
356 info, info->coverage_count[0]);
357 #endif
358 }
359
360 static void
font_info_measure_font(struct font_info * info)361 font_info_measure_font (struct font_info *info)
362 {
363 PangoRectangle logical;
364
365 /* Estimate for ASCII characters. */
366 pango_layout_set_text (info->layout, VTE_DRAW_SINGLE_WIDE_CHARACTERS, -1);
367 pango_layout_get_extents (info->layout, NULL, &logical);
368 /* We don't do CEIL for width since we are averaging;
369 * rounding is more accurate */
370 info->width = PANGO_PIXELS (howmany (logical.width, strlen(VTE_DRAW_SINGLE_WIDE_CHARACTERS)));
371 info->height = PANGO_PIXELS_CEIL (logical.height);
372 info->ascent = PANGO_PIXELS_CEIL (pango_layout_get_baseline (info->layout));
373
374 /* Now that we shaped the entire ASCII character string, cache glyph
375 * info for them */
376 font_info_cache_ascii (info);
377
378
379 if (info->height == 0) {
380 info->height = PANGO_PIXELS_CEIL (logical.height);
381 }
382 if (info->ascent == 0) {
383 info->ascent = PANGO_PIXELS_CEIL (pango_layout_get_baseline (info->layout));
384 }
385
386 _vte_debug_print (VTE_DEBUG_MISC,
387 "vtepangocairo: %p font metrics = %dx%d (%d)\n",
388 info, info->width, info->height, info->ascent);
389 }
390
391
392 static struct font_info *
font_info_allocate(PangoContext * context)393 font_info_allocate (PangoContext *context)
394 {
395 struct font_info *info;
396 PangoTabArray *tabs;
397
398 info = g_slice_new0 (struct font_info);
399
400 _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
401 "vtepangocairo: %p allocating font_info\n",
402 info);
403
404 info->layout = pango_layout_new (context);
405 tabs = pango_tab_array_new_with_positions (1, FALSE, PANGO_TAB_LEFT, 1);
406 pango_layout_set_tabs (info->layout, tabs);
407 pango_tab_array_free (tabs);
408
409 info->string = g_string_sized_new (VTE_UTF8_BPC+1);
410
411 font_info_measure_font (info);
412
413 return info;
414 }
415
416 static void
font_info_free(struct font_info * info)417 font_info_free (struct font_info *info)
418 {
419 vteunistr i;
420
421 #ifdef VTE_DEBUG
422 _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
423 "vtepangocairo: %p freeing font_info. coverages %d = %d + %d + %d\n",
424 info,
425 info->coverage_count[0],
426 info->coverage_count[1],
427 info->coverage_count[2],
428 info->coverage_count[3]);
429 #endif
430
431 g_string_free (info->string, TRUE);
432 g_object_unref (info->layout);
433
434 for (i = 0; i < G_N_ELEMENTS (info->ascii_unistr_info); i++)
435 unistr_info_finish (&info->ascii_unistr_info[i]);
436
437 if (info->other_unistr_info) {
438 g_hash_table_destroy (info->other_unistr_info);
439 }
440
441 g_slice_free (struct font_info, info);
442 }
443
444
445 static GHashTable *font_info_for_context;
446
447 static struct font_info *
font_info_register(struct font_info * info)448 font_info_register (struct font_info *info)
449 {
450 g_hash_table_insert (font_info_for_context,
451 pango_layout_get_context (info->layout),
452 info);
453
454 return info;
455 }
456
457 static void
font_info_unregister(struct font_info * info)458 font_info_unregister (struct font_info *info)
459 {
460 g_hash_table_remove (font_info_for_context,
461 pango_layout_get_context (info->layout));
462 }
463
464
465 static struct font_info *
font_info_reference(struct font_info * info)466 font_info_reference (struct font_info *info)
467 {
468 if (!info)
469 return info;
470
471 g_return_val_if_fail (info->ref_count >= 0, info);
472
473 if (info->destroy_timeout) {
474 g_source_remove (info->destroy_timeout);
475 info->destroy_timeout = 0;
476 }
477
478 info->ref_count++;
479
480 return info;
481 }
482
483 static gboolean
font_info_destroy_delayed(struct font_info * info)484 font_info_destroy_delayed (struct font_info *info)
485 {
486 info->destroy_timeout = 0;
487
488 font_info_unregister (info);
489 font_info_free (info);
490
491 return FALSE;
492 }
493
494 static void
font_info_destroy(struct font_info * info)495 font_info_destroy (struct font_info *info)
496 {
497 if (!info)
498 return;
499
500 g_return_if_fail (info->ref_count > 0);
501
502 info->ref_count--;
503 if (info->ref_count)
504 return;
505
506 /* Delay destruction by a few seconds, in case we need it again */
507 info->destroy_timeout = gdk_threads_add_timeout_seconds (FONT_CACHE_TIMEOUT,
508 (GSourceFunc) font_info_destroy_delayed,
509 info);
510 }
511
512 static GQuark
fontconfig_timestamp_quark(void)513 fontconfig_timestamp_quark (void)
514 {
515 static GQuark quark;
516
517 if (G_UNLIKELY (!quark))
518 quark = g_quark_from_static_string ("vte-fontconfig-timestamp");
519
520 return quark;
521 }
522
523 static void
vte_pango_context_set_fontconfig_timestamp(PangoContext * context,guint fontconfig_timestamp)524 vte_pango_context_set_fontconfig_timestamp (PangoContext *context,
525 guint fontconfig_timestamp)
526 {
527 g_object_set_qdata ((GObject *) context,
528 fontconfig_timestamp_quark (),
529 GUINT_TO_POINTER (fontconfig_timestamp));
530 }
531
532 static guint
vte_pango_context_get_fontconfig_timestamp(PangoContext * context)533 vte_pango_context_get_fontconfig_timestamp (PangoContext *context)
534 {
535 return GPOINTER_TO_UINT (g_object_get_qdata ((GObject *) context,
536 fontconfig_timestamp_quark ()));
537 }
538
539 static guint
context_hash(PangoContext * context)540 context_hash (PangoContext *context)
541 {
542 return pango_units_from_double (pango_cairo_context_get_resolution (context))
543 ^ pango_font_description_hash (pango_context_get_font_description (context))
544 ^ cairo_font_options_hash (pango_cairo_context_get_font_options (context))
545 ^ GPOINTER_TO_UINT (pango_context_get_language (context))
546 ^ vte_pango_context_get_fontconfig_timestamp (context);
547 }
548
549 static gboolean
context_equal(PangoContext * a,PangoContext * b)550 context_equal (PangoContext *a,
551 PangoContext *b)
552 {
553 return pango_cairo_context_get_resolution (a) == pango_cairo_context_get_resolution (b)
554 && pango_font_description_equal (pango_context_get_font_description (a), pango_context_get_font_description (b))
555 && cairo_font_options_equal (pango_cairo_context_get_font_options (a), pango_cairo_context_get_font_options (b))
556 && pango_context_get_language (a) == pango_context_get_language (b)
557 && vte_pango_context_get_fontconfig_timestamp (a) == vte_pango_context_get_fontconfig_timestamp (b);
558 }
559
560 static struct font_info *
font_info_find_for_context(PangoContext * context)561 font_info_find_for_context (PangoContext *context)
562 {
563 struct font_info *info;
564
565 if (G_UNLIKELY (font_info_for_context == NULL))
566 font_info_for_context = g_hash_table_new ((GHashFunc) context_hash, (GEqualFunc) context_equal);
567
568 info = g_hash_table_lookup (font_info_for_context, context);
569 if (G_LIKELY (info)) {
570 _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
571 "vtepangocairo: %p found font_info in cache\n",
572 info);
573 return font_info_reference (info);
574 }
575
576 info = font_info_allocate (context);
577 info->ref_count = 1;
578 font_info_register (info);
579
580 g_object_unref (context);
581
582 return info;
583 }
584
585 /* assumes ownership/reference of context */
586 static struct font_info *
font_info_create_for_context(PangoContext * context,const PangoFontDescription * desc,VteTerminalAntiAlias antialias,PangoLanguage * language,guint fontconfig_timestamp)587 font_info_create_for_context (PangoContext *context,
588 const PangoFontDescription *desc,
589 VteTerminalAntiAlias antialias,
590 PangoLanguage *language,
591 guint fontconfig_timestamp)
592 {
593 if (!PANGO_IS_CAIRO_FONT_MAP (pango_context_get_font_map (context))) {
594 /* Ouch, Gtk+ switched over to some drawing system?
595 * Lets just create one from the default font map.
596 */
597 g_object_unref (context);
598 context = pango_font_map_create_context (pango_cairo_font_map_get_default ());
599 }
600
601 vte_pango_context_set_fontconfig_timestamp (context, fontconfig_timestamp);
602
603 pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
604
605 if (desc)
606 pango_context_set_font_description (context, desc);
607
608 pango_context_set_language (context, language);
609
610 switch (antialias) {
611 cairo_font_options_t *font_options;
612 cairo_antialias_t cr_aa;
613
614 case VTE_ANTI_ALIAS_FORCE_ENABLE:
615 case VTE_ANTI_ALIAS_FORCE_DISABLE:
616
617 if (antialias == VTE_ANTI_ALIAS_FORCE_ENABLE)
618 cr_aa = CAIRO_ANTIALIAS_DEFAULT; /* let surface decide between gray and subpixel */
619 else
620 cr_aa = CAIRO_ANTIALIAS_NONE;
621
622 font_options = cairo_font_options_copy (pango_cairo_context_get_font_options (context));
623 cairo_font_options_set_antialias (font_options, cr_aa);
624 pango_cairo_context_set_font_options (context, font_options);
625 cairo_font_options_destroy (font_options);
626
627 break;
628
629 default:
630 case VTE_ANTI_ALIAS_USE_DEFAULT:
631 /* Make sure our contexts have a font_options set. We use
632 * this invariant in our context hash and equal functions.
633 */
634 if (!pango_cairo_context_get_font_options (context)) {
635 font_options = cairo_font_options_create ();
636 pango_cairo_context_set_font_options (context, font_options);
637 cairo_font_options_destroy (font_options);
638 }
639 break;
640 }
641
642 return font_info_find_for_context (context);
643 }
644
645 static struct font_info *
font_info_create_for_screen(GdkScreen * screen,const PangoFontDescription * desc,VteTerminalAntiAlias antialias,PangoLanguage * language)646 font_info_create_for_screen (GdkScreen *screen,
647 const PangoFontDescription *desc,
648 VteTerminalAntiAlias antialias,
649 PangoLanguage *language)
650 {
651 GtkSettings *settings = gtk_settings_get_for_screen (screen);
652 int fontconfig_timestamp;
653 g_object_get (settings, "gtk-fontconfig-timestamp", &fontconfig_timestamp, NULL);
654 return font_info_create_for_context (gdk_pango_context_get_for_screen (screen),
655 desc, antialias, language, fontconfig_timestamp);
656 }
657
658 static struct font_info *
font_info_create_for_widget(GtkWidget * widget,const PangoFontDescription * desc,VteTerminalAntiAlias antialias)659 font_info_create_for_widget (GtkWidget *widget,
660 const PangoFontDescription *desc,
661 VteTerminalAntiAlias antialias)
662 {
663 GdkScreen *screen = gtk_widget_get_screen (widget);
664 PangoLanguage *language = pango_context_get_language (gtk_widget_get_pango_context (widget));
665
666 return font_info_create_for_screen (screen, desc, antialias, language);
667 }
668
669 static struct unistr_info *
font_info_get_unistr_info(struct font_info * info,vteunistr c)670 font_info_get_unistr_info (struct font_info *info,
671 vteunistr c)
672 {
673 struct unistr_info *uinfo;
674 union unistr_font_info *ufi;
675 PangoRectangle logical;
676 PangoLayoutLine *line;
677
678 uinfo = font_info_find_unistr_info (info, c);
679 if (G_LIKELY (uinfo->coverage != COVERAGE_UNKNOWN))
680 return uinfo;
681
682 ufi = &uinfo->ufi;
683
684 g_string_set_size (info->string, 0);
685 _vte_unistr_append_to_string (c, info->string);
686 pango_layout_set_text (info->layout, info->string->str, -1);
687 pango_layout_get_extents (info->layout, NULL, &logical);
688
689 uinfo->width = PANGO_PIXELS_CEIL (logical.width);
690
691 line = pango_layout_get_line_readonly (info->layout, 0);
692
693 uinfo->has_unknown_chars = pango_layout_get_unknown_glyphs_count (info->layout) != 0;
694 /* we use PangoLayoutRun rendering unless there is exactly one run in the line. */
695 if (G_UNLIKELY (!line || !line->runs || line->runs->next))
696 {
697 uinfo->coverage = COVERAGE_USE_PANGO_LAYOUT_LINE;
698
699 ufi->using_pango_layout_line.line = pango_layout_line_ref (line);
700 /* we hold a manual reference on layout. pango currently
701 * doesn't work if line->layout is NULL. ugh! */
702 pango_layout_set_text (info->layout, "", -1); /* make layout disassociate from the line */
703 ufi->using_pango_layout_line.line->layout = g_object_ref (info->layout);
704
705 } else {
706 PangoGlyphItem *glyph_item = line->runs->data;
707 PangoFont *pango_font = glyph_item->item->analysis.font;
708 PangoGlyphString *glyph_string = glyph_item->glyphs;
709
710 /* we use fast cairo path if glyph string has only one real
711 * glyph and at origin */
712 if (!uinfo->has_unknown_chars &&
713 glyph_string->num_glyphs == 1 && glyph_string->glyphs[0].glyph <= 0xFFFF &&
714 (glyph_string->glyphs[0].geometry.x_offset |
715 glyph_string->glyphs[0].geometry.y_offset) == 0)
716 {
717 cairo_scaled_font_t *scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) pango_font);
718
719 if (scaled_font) {
720 uinfo->coverage = COVERAGE_USE_CAIRO_GLYPH;
721
722 ufi->using_cairo_glyph.scaled_font = cairo_scaled_font_reference (scaled_font);
723 ufi->using_cairo_glyph.glyph_index = glyph_string->glyphs[0].glyph;
724 }
725 }
726
727 /* use pango fast path otherwise */
728 if (G_UNLIKELY (uinfo->coverage == COVERAGE_UNKNOWN)) {
729 uinfo->coverage = COVERAGE_USE_PANGO_GLYPH_STRING;
730
731 ufi->using_pango_glyph_string.font = pango_font ? g_object_ref (pango_font) : NULL;
732 ufi->using_pango_glyph_string.glyph_string = pango_glyph_string_copy (glyph_string);
733 }
734 }
735
736 /* release internal layout resources */
737 pango_layout_set_text (info->layout, "", -1);
738
739 #ifdef VTE_DEBUG
740 info->coverage_count[0]++;
741 info->coverage_count[uinfo->coverage]++;
742 #endif
743
744 return uinfo;
745 }
746
747 struct _vte_draw {
748 GtkWidget *widget;
749
750 gint started;
751
752 struct font_info *font;
753 struct font_info *font_bold;
754 cairo_pattern_t *bg_pattern;
755
756 cairo_t *cr;
757 };
758
759 struct _vte_draw *
_vte_draw_new(GtkWidget * widget)760 _vte_draw_new (GtkWidget *widget)
761 {
762 struct _vte_draw *draw;
763
764 /* Create the structure. */
765 draw = g_slice_new0 (struct _vte_draw);
766 draw->widget = g_object_ref (widget);
767
768 _vte_debug_print (VTE_DEBUG_DRAW, "draw_new\n");
769
770 return draw;
771 }
772
773 void
_vte_draw_free(struct _vte_draw * draw)774 _vte_draw_free (struct _vte_draw *draw)
775 {
776 _vte_debug_print (VTE_DEBUG_DRAW, "draw_free\n");
777
778 if (draw->bg_pattern != NULL) {
779 cairo_pattern_destroy (draw->bg_pattern);
780 draw->bg_pattern = NULL;
781 }
782
783 if (draw->font != NULL) {
784 font_info_destroy (draw->font);
785 draw->font = NULL;
786 }
787
788 if (draw->widget != NULL) {
789 g_object_unref (draw->widget);
790 }
791
792 g_slice_free (struct _vte_draw, draw);
793 }
794
795 void
_vte_draw_start(struct _vte_draw * draw)796 _vte_draw_start (struct _vte_draw *draw)
797 {
798 GdkWindow *window;
799
800 g_return_if_fail (gtk_widget_get_realized (draw->widget));
801
802 _vte_debug_print (VTE_DEBUG_DRAW, "draw_start\n");
803
804 if (draw->started == 0) {
805 window = gtk_widget_get_window(draw->widget);
806 g_object_ref (window);
807 draw->cr = gdk_cairo_create (window);
808 }
809
810 draw->started++;
811 }
812
813 void
_vte_draw_end(struct _vte_draw * draw)814 _vte_draw_end (struct _vte_draw *draw)
815 {
816 g_return_if_fail (draw->started);
817
818 draw->started--;
819 if (draw->started == 0) {
820 cairo_destroy (draw->cr);
821 draw->cr = NULL;
822 g_object_unref (gtk_widget_get_window(draw->widget));
823 }
824
825 _vte_debug_print (VTE_DEBUG_DRAW, "draw_end\n");
826 }
827
828 void
_vte_draw_set_background_solid(struct _vte_draw * draw,double red,double green,double blue,double opacity)829 _vte_draw_set_background_solid(struct _vte_draw *draw,
830 double red,
831 double green,
832 double blue,
833 double opacity)
834 {
835 if (draw->bg_pattern)
836 cairo_pattern_destroy (draw->bg_pattern);
837
838 draw->bg_pattern = cairo_pattern_create_rgba (red,
839 green,
840 blue,
841 opacity);
842 }
843
844 void
_vte_draw_set_background_image(struct _vte_draw * draw,VteBgSourceType type,GdkPixbuf * pixbuf,const char * filename,const PangoColor * color,double saturation)845 _vte_draw_set_background_image (struct _vte_draw *draw,
846 VteBgSourceType type,
847 GdkPixbuf *pixbuf,
848 const char *filename,
849 const PangoColor *color,
850 double saturation)
851 {
852 cairo_surface_t *surface;
853
854 /* Need a valid draw->cr for cairo_get_target () */
855 _vte_draw_start (draw);
856
857 surface = vte_bg_get_surface (vte_bg_get_for_screen (gtk_widget_get_screen (draw->widget)),
858 type, pixbuf, filename,
859 color, saturation,
860 cairo_get_target(draw->cr));
861
862 _vte_draw_end (draw);
863
864 if (!surface)
865 return;
866
867 if (draw->bg_pattern)
868 cairo_pattern_destroy (draw->bg_pattern);
869
870 draw->bg_pattern = cairo_pattern_create_for_surface (surface);
871 cairo_surface_destroy (surface);
872 cairo_pattern_set_extend (draw->bg_pattern, CAIRO_EXTEND_REPEAT);
873 }
874
875 void
_vte_draw_set_background_scroll(struct _vte_draw * draw,gint x,gint y)876 _vte_draw_set_background_scroll (struct _vte_draw *draw,
877 gint x, gint y)
878 {
879 cairo_matrix_t matrix;
880
881 _vte_debug_print (VTE_DEBUG_DRAW,
882 "draw_set_scroll (%d, %d)\n",
883 x, y);
884
885 g_return_if_fail (draw->bg_pattern != NULL);
886
887 cairo_matrix_init_translate (&matrix, x, y);
888 cairo_pattern_set_matrix (draw->bg_pattern, &matrix);
889 }
890
891 void
_vte_draw_clip(struct _vte_draw * draw,GdkRegion * region)892 _vte_draw_clip (struct _vte_draw *draw, GdkRegion *region)
893 {
894 _vte_debug_print (VTE_DEBUG_DRAW, "draw_clip\n");
895 gdk_cairo_region(draw->cr, region);
896 cairo_clip (draw->cr);
897 }
898
899 void
_vte_draw_clear(struct _vte_draw * draw,gint x,gint y,gint width,gint height)900 _vte_draw_clear (struct _vte_draw *draw, gint x, gint y, gint width, gint height)
901 {
902 g_return_if_fail (draw->bg_pattern != NULL);
903
904 _vte_debug_print (VTE_DEBUG_DRAW, "draw_clear (%d, %d, %d, %d)\n",
905 x,y,width, height);
906
907 cairo_rectangle (draw->cr, x, y, width, height);
908 cairo_set_operator (draw->cr, CAIRO_OPERATOR_SOURCE);
909 cairo_set_source (draw->cr, draw->bg_pattern);
910 cairo_fill (draw->cr);
911 }
912
913 void
_vte_draw_set_text_font(struct _vte_draw * draw,const PangoFontDescription * fontdesc,VteTerminalAntiAlias antialias)914 _vte_draw_set_text_font (struct _vte_draw *draw,
915 const PangoFontDescription *fontdesc,
916 VteTerminalAntiAlias antialias)
917 {
918 PangoFontDescription *bolddesc = NULL;
919
920 _vte_debug_print (VTE_DEBUG_DRAW, "draw_set_text_font (aa=%d)\n",
921 antialias);
922
923 if (draw->font_bold != draw->font)
924 font_info_destroy (draw->font_bold);
925 font_info_destroy (draw->font);
926 draw->font = font_info_create_for_widget (draw->widget, fontdesc, antialias);
927
928 /* calculate bold font desc */
929 bolddesc = pango_font_description_copy (fontdesc);
930 pango_font_description_set_weight (bolddesc, PANGO_WEIGHT_BOLD);
931
932 draw->font_bold = font_info_create_for_widget (draw->widget, bolddesc, antialias);
933 pango_font_description_free (bolddesc);
934
935 /* Decide if we should keep this bold font face, per bug 54926:
936 * - reject bold font if it is not within 10% of normal font width
937 */
938 if ( abs((draw->font_bold->width * 100 / draw->font->width) - 100) > 10 ) {
939 font_info_destroy (draw->font_bold);
940 draw->font_bold = draw->font;
941 }
942 }
943
944 void
_vte_draw_get_text_metrics(struct _vte_draw * draw,gint * width,gint * height,gint * ascent)945 _vte_draw_get_text_metrics(struct _vte_draw *draw,
946 gint *width, gint *height, gint *ascent)
947 {
948 g_return_if_fail (draw->font != NULL);
949
950 if (width)
951 *width = draw->font->width;
952 if (height)
953 *height = draw->font->height;
954 if (ascent)
955 *ascent = draw->font->ascent;
956 }
957
958
959 int
_vte_draw_get_char_width(struct _vte_draw * draw,vteunistr c,int columns,gboolean bold)960 _vte_draw_get_char_width (struct _vte_draw *draw, vteunistr c, int columns,
961 gboolean bold)
962 {
963 struct unistr_info *uinfo;
964
965 g_return_val_if_fail (draw->font != NULL, 0);
966
967 uinfo = font_info_get_unistr_info (bold ? draw->font_bold : draw->font, c);
968 return uinfo->width;
969 }
970
971 static gboolean
_vte_draw_has_bold(struct _vte_draw * draw)972 _vte_draw_has_bold (struct _vte_draw *draw)
973 {
974 return (draw->font != draw->font_bold);
975 }
976
977 static void
set_source_color_alpha(cairo_t * cr,const PangoColor * color,guchar alpha)978 set_source_color_alpha (cairo_t *cr,
979 const PangoColor *color,
980 guchar alpha)
981 {
982 cairo_set_source_rgba (cr,
983 color->red / 65535.,
984 color->green / 65535.,
985 color->blue / 65535.,
986 alpha / 255.);
987 }
988
989 static void
_vte_draw_text_internal(struct _vte_draw * draw,struct _vte_draw_text_request * requests,gsize n_requests,const PangoColor * color,guchar alpha,gboolean bold)990 _vte_draw_text_internal (struct _vte_draw *draw,
991 struct _vte_draw_text_request *requests, gsize n_requests,
992 const PangoColor *color, guchar alpha, gboolean bold)
993 {
994 gsize i;
995 cairo_scaled_font_t *last_scaled_font = NULL;
996 int n_cr_glyphs = 0;
997 cairo_glyph_t cr_glyphs[MAX_RUN_LENGTH];
998 struct font_info *font = bold ? draw->font_bold : draw->font;
999
1000 g_return_if_fail (font != NULL);
1001
1002 set_source_color_alpha (draw->cr, color, alpha);
1003 cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER);
1004
1005 for (i = 0; i < n_requests; i++) {
1006 vteunistr c = requests[i].c;
1007 int x = requests[i].x;
1008 int y = requests[i].y + font->ascent;
1009 struct unistr_info *uinfo = font_info_get_unistr_info (font, c);
1010 union unistr_font_info *ufi = &uinfo->ufi;
1011
1012 switch (uinfo->coverage) {
1013 default:
1014 case COVERAGE_UNKNOWN:
1015 g_assert_not_reached ();
1016 break;
1017 case COVERAGE_USE_PANGO_LAYOUT_LINE:
1018 cairo_move_to (draw->cr, x, y);
1019 pango_cairo_show_layout_line (draw->cr,
1020 ufi->using_pango_layout_line.line);
1021 break;
1022 case COVERAGE_USE_PANGO_GLYPH_STRING:
1023 cairo_move_to (draw->cr, x, y);
1024 pango_cairo_show_glyph_string (draw->cr,
1025 ufi->using_pango_glyph_string.font,
1026 ufi->using_pango_glyph_string.glyph_string);
1027 break;
1028 case COVERAGE_USE_CAIRO_GLYPH:
1029 if (last_scaled_font != ufi->using_cairo_glyph.scaled_font || n_cr_glyphs == MAX_RUN_LENGTH) {
1030 if (n_cr_glyphs) {
1031 cairo_set_scaled_font (draw->cr, last_scaled_font);
1032 cairo_show_glyphs (draw->cr,
1033 cr_glyphs,
1034 n_cr_glyphs);
1035 n_cr_glyphs = 0;
1036 }
1037 last_scaled_font = ufi->using_cairo_glyph.scaled_font;
1038 }
1039 cr_glyphs[n_cr_glyphs].index = ufi->using_cairo_glyph.glyph_index;
1040 cr_glyphs[n_cr_glyphs].x = x;
1041 cr_glyphs[n_cr_glyphs].y = y;
1042 n_cr_glyphs++;
1043 break;
1044 }
1045 }
1046 if (n_cr_glyphs) {
1047 cairo_set_scaled_font (draw->cr, last_scaled_font);
1048 cairo_show_glyphs (draw->cr,
1049 cr_glyphs,
1050 n_cr_glyphs);
1051 n_cr_glyphs = 0;
1052 }
1053 }
1054
1055 void
_vte_draw_text(struct _vte_draw * draw,struct _vte_draw_text_request * requests,gsize n_requests,const PangoColor * color,guchar alpha,gboolean bold)1056 _vte_draw_text (struct _vte_draw *draw,
1057 struct _vte_draw_text_request *requests, gsize n_requests,
1058 const PangoColor *color, guchar alpha, gboolean bold)
1059 {
1060 g_return_if_fail (draw->started);
1061
1062 if (_vte_debug_on (VTE_DEBUG_DRAW)) {
1063 GString *string = g_string_new ("");
1064 gchar *str;
1065 gsize n;
1066 for (n = 0; n < n_requests; n++) {
1067 g_string_append_unichar (string, requests[n].c);
1068 }
1069 str = g_string_free (string, FALSE);
1070 g_printerr ("draw_text (\"%s\", len=%"G_GSIZE_FORMAT", color=(%d,%d,%d,%d), %s)\n",
1071 str, n_requests, color->red, color->green, color->blue,
1072 alpha, bold ? "bold" : "normal");
1073 g_free (str);
1074 }
1075
1076 _vte_draw_text_internal (draw, requests, n_requests, color, alpha, bold);
1077
1078 /* handle fonts that lack a bold face by double-striking */
1079 if (bold && !_vte_draw_has_bold (draw)) {
1080 gsize i;
1081
1082 /* Take a step to the right. */
1083 for (i = 0; i < n_requests; i++) {
1084 requests[i].x++;
1085 }
1086 _vte_draw_text_internal (draw, requests,
1087 n_requests, color, alpha, FALSE);
1088 /* Now take a step back. */
1089 for (i = 0; i < n_requests; i++) {
1090 requests[i].x--;
1091 }
1092 }
1093 }
1094
1095 gboolean
_vte_draw_has_char(struct _vte_draw * draw,vteunistr c,gboolean bold)1096 _vte_draw_has_char (struct _vte_draw *draw, vteunistr c, gboolean bold)
1097 {
1098 struct unistr_info *uinfo;
1099
1100 _vte_debug_print (VTE_DEBUG_DRAW, "draw_has_char ('0x%04X', %s)\n", c,
1101 bold ? "bold" : "normal");
1102
1103 g_return_val_if_fail (draw->font != NULL, FALSE);
1104
1105 uinfo = font_info_get_unistr_info (bold ? draw->font_bold : draw->font, c);
1106 return !uinfo->has_unknown_chars;
1107 }
1108
1109 gboolean
_vte_draw_char(struct _vte_draw * draw,struct _vte_draw_text_request * request,const PangoColor * color,guchar alpha,gboolean bold)1110 _vte_draw_char (struct _vte_draw *draw,
1111 struct _vte_draw_text_request *request,
1112 const PangoColor *color, guchar alpha, gboolean bold)
1113 {
1114 gboolean has_char;
1115
1116 _vte_debug_print (VTE_DEBUG_DRAW,
1117 "draw_char ('%c', color=(%d,%d,%d,%d), %s)\n",
1118 request->c,
1119 color->red, color->green, color->blue,
1120 alpha, bold ? "bold" : "normal");
1121
1122 has_char =_vte_draw_has_char (draw, request->c, bold);
1123 if (has_char)
1124 _vte_draw_text (draw, request, 1, color, alpha, bold);
1125
1126 return has_char;
1127 }
1128
1129 void
_vte_draw_draw_rectangle(struct _vte_draw * draw,gint x,gint y,gint width,gint height,const PangoColor * color,guchar alpha)1130 _vte_draw_draw_rectangle (struct _vte_draw *draw,
1131 gint x, gint y, gint width, gint height,
1132 const PangoColor *color, guchar alpha)
1133 {
1134 g_return_if_fail (draw->started);
1135
1136 _vte_debug_print (VTE_DEBUG_DRAW,
1137 "draw_rectangle (%d, %d, %d, %d, color=(%d,%d,%d,%d))\n",
1138 x,y,width,height,
1139 color->red, color->green, color->blue,
1140 alpha);
1141
1142 cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER);
1143 cairo_rectangle (draw->cr, x+VTE_LINE_WIDTH/2., y+VTE_LINE_WIDTH/2., width-VTE_LINE_WIDTH, height-VTE_LINE_WIDTH);
1144 set_source_color_alpha (draw->cr, color, alpha);
1145 cairo_set_line_width (draw->cr, VTE_LINE_WIDTH);
1146 cairo_stroke (draw->cr);
1147 }
1148
1149 void
_vte_draw_fill_rectangle(struct _vte_draw * draw,gint x,gint y,gint width,gint height,const PangoColor * color,guchar alpha)1150 _vte_draw_fill_rectangle (struct _vte_draw *draw,
1151 gint x, gint y, gint width, gint height,
1152 const PangoColor *color, guchar alpha)
1153 {
1154 g_return_if_fail (draw->started);
1155
1156 _vte_debug_print (VTE_DEBUG_DRAW,
1157 "draw_fill_rectangle (%d, %d, %d, %d, color=(%d,%d,%d,%d))\n",
1158 x,y,width,height,
1159 color->red, color->green, color->blue,
1160 alpha);
1161
1162 cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER);
1163 cairo_rectangle (draw->cr, x, y, width, height);
1164 set_source_color_alpha (draw->cr, color, alpha);
1165 cairo_fill (draw->cr);
1166 }
1167