summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEgmont Koblinger <egmont@gmail.com>2017-12-19 22:22:22 +0100
committerEgmont Koblinger <egmont@gmail.com>2017-12-19 22:22:22 +0100
commit68944c293d1004208846b9e9b18e536be3291365 (patch)
tree7acd8ef084f66eca176b17e11ce3b809ba361b73
parent96dfd02eeb3c63b321204fcba01c3b8417c12d19 (diff)
downloadvte-68944c293d1004208846b9e9b18e536be3291365.tar.gz
widget: Speed up the drawing of curly underline by caching its look
https://bugzilla.gnome.org/show_bug.cgi?id=721761
-rw-r--r--src/vte.cc16
-rw-r--r--src/vtedraw.cc79
-rw-r--r--src/vtedraw.hh7
-rw-r--r--src/vteinternal.hh2
4 files changed, 76 insertions, 28 deletions
diff --git a/src/vte.cc b/src/vte.cc
index 82661faa..ccf00dc3 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -8121,7 +8121,7 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
m_double_underline_position = 1;
m_double_underline_thickness = 1;
m_undercurl_position = 1.;
- m_undercurl_thickness = 1;
+ m_undercurl_thickness = 1.;
m_strikethrough_position = 1;
m_strikethrough_thickness = 1;
m_overline_position = 1;
@@ -9023,14 +9023,12 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
&dc, VTE_DRAW_OPAQUE);
break;
case 3:
- for (int j = 0; j < columns; j++) {
- _vte_draw_draw_undercurl(m_draw,
- x + j * column_width,
- y + m_undercurl_position,
- column_width,
- m_undercurl_thickness,
- &dc, VTE_DRAW_OPAQUE);
- }
+ _vte_draw_draw_undercurl(m_draw,
+ x,
+ y + m_undercurl_position,
+ m_undercurl_thickness,
+ columns,
+ &dc, VTE_DRAW_OPAQUE);
break;
}
if (strikethrough) {
diff --git a/src/vtedraw.cc b/src/vtedraw.cc
index 9e47bfa8..28251461 100644
--- a/src/vtedraw.cc
+++ b/src/vtedraw.cc
@@ -758,6 +758,9 @@ struct _vte_draw {
GtkBorder char_spacing;
cairo_t *cr;
+
+ /* Cache the undercurl's rendered look. */
+ cairo_surface_t *undercurl_surface;
};
struct _vte_draw *
@@ -788,6 +791,11 @@ _vte_draw_free (struct _vte_draw *draw)
}
}
+ if (draw->undercurl_surface != NULL) {
+ cairo_surface_destroy (draw->undercurl_surface);
+ draw->undercurl_surface = NULL;
+ }
+
g_slice_free (struct _vte_draw, draw);
}
@@ -907,6 +915,12 @@ _vte_draw_set_text_font (struct _vte_draw *draw,
draw->cell_height = draw->fonts[VTE_DRAW_NORMAL]->height * cell_height_scale;
draw->char_spacing.top = (draw->cell_height - draw->fonts[VTE_DRAW_NORMAL]->height + 1) / 2;
draw->char_spacing.bottom = (draw->cell_height - draw->fonts[VTE_DRAW_NORMAL]->height) / 2;
+
+ /* Drop the undercurl's cached look. Will recache on demand. */
+ if (draw->undercurl_surface != NULL) {
+ cairo_surface_destroy (draw->undercurl_surface);
+ draw->undercurl_surface = NULL;
+ }
}
void
@@ -1670,36 +1684,71 @@ _vte_draw_get_undercurl_arc_height(gint width)
}
double
-_vte_draw_get_undercurl_height(gint width, int line_width)
+_vte_draw_get_undercurl_height(gint width, double line_width)
{
return 2. * _vte_draw_get_undercurl_arc_height(width) + line_width;
}
void
_vte_draw_draw_undercurl(struct _vte_draw *draw,
- gint x, double y, gint width,
- int line_width,
+ gint x, double y,
+ double line_width,
+ gint count,
vte::color::rgb const *color, double alpha)
{
- double rad = _vte_draw_get_undercurl_rad(width);
- double yc = y + _vte_draw_get_undercurl_height(width, line_width) / 2.;
+ /* The end of the curly line slightly overflows to the next cell, so the canvas
+ * caching the rendered look has to be wider not to chop this off. */
+ gint x_padding = line_width + 1; /* ceil, kind of */
+
+ gint surface_top = y; /* floor */
g_assert(draw->cr);
_vte_debug_print (VTE_DEBUG_DRAW,
- "draw_undercurl (%d, %f, %d, color=(%d,%d,%d,%.3f))\n",
- x,y,width,
+ "draw_undercurl (x=%d, y=%f, count=%d, color=(%d,%d,%d,%.3f))\n",
+ x, y, count,
color->red, color->green, color->blue,
alpha);
+ if (G_UNLIKELY (draw->undercurl_surface == NULL)) {
+ /* Cache the undercurl's look. The design assumes that until the cached look is
+ * invalidated (the font is changed), this method is always called with the "y"
+ * parameter having the same fractional part, and the same "line_width" parameter.
+ * For caching, only the fractional part of "y" is used. */
+ cairo_t *undercurl_cr;
+
+ double rad = _vte_draw_get_undercurl_rad(draw->cell_width);
+ double y_bottom = y + _vte_draw_get_undercurl_height(draw->cell_width, line_width);
+ double y_center = (y + y_bottom) / 2.;
+ gint surface_bottom = y_bottom + 1; /* ceil, kind of */
+
+ _vte_debug_print (VTE_DEBUG_DRAW,
+ "caching undercurl shape\n");
+
+ /* Add a line_width of margin horizontally on both sides, for nice antialias overflowing. */
+ draw->undercurl_surface = cairo_surface_create_similar (cairo_get_target (draw->cr),
+ CAIRO_CONTENT_ALPHA,
+ draw->cell_width + 2 * x_padding,
+ surface_bottom - surface_top);
+ undercurl_cr = cairo_create (draw->undercurl_surface);
+ cairo_set_operator (undercurl_cr, CAIRO_OPERATOR_OVER);
+ /* First quarter circle, similar to the left half of the tilde symbol. */
+ cairo_arc (undercurl_cr, x_padding + draw->cell_width / 4., y_center - surface_top + draw->cell_width / 4., rad, M_PI * 5 / 4, M_PI * 7 / 4);
+ /* Second quarter circle, similar to the right half of the tilde symbol. */
+ cairo_arc_negative (undercurl_cr, x_padding + draw->cell_width * 3 / 4., y_center - surface_top - draw->cell_width / 4., rad, M_PI * 3 / 4, M_PI / 4);
+ cairo_set_line_width (undercurl_cr, line_width);
+ cairo_stroke (undercurl_cr);
+ cairo_destroy (undercurl_cr);
+ }
+
+ /* Paint the cached look of the undercurl using the desired look.
+ * The cached look takes the fractional part of "y" into account,
+ * here we only offset by its integer part. */
+ cairo_save (draw->cr);
cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER);
- cairo_new_sub_path(draw->cr);
- /* First quarter circle, similar to the left half of the tilde symbol. */
- cairo_arc (draw->cr, x + width / 4., yc + width / 4., rad, M_PI * 5 / 4, M_PI * 7 / 4);
- /* Second quarter circle, similar to the right half of the tilde symbol. */
- cairo_arc_negative (draw->cr, x + width * 3 / 4., yc - width / 4., rad, M_PI * 3 / 4, M_PI / 4);
_vte_draw_set_source_color_alpha (draw, color, alpha);
- cairo_set_line_width (draw->cr, line_width);
- /* FIXME: This is quite slow. The rendered bitmap should be cached and reused. */
- cairo_stroke (draw->cr);
+ for (int i = 0; i < count; i++) {
+ cairo_mask_surface (draw->cr, draw->undercurl_surface, x - x_padding + i * draw->cell_width, surface_top);
+ }
+ cairo_restore (draw->cr);
}
diff --git a/src/vtedraw.hh b/src/vtedraw.hh
index 745b8413..b5c3d36c 100644
--- a/src/vtedraw.hh
+++ b/src/vtedraw.hh
@@ -94,12 +94,13 @@ void _vte_draw_draw_line(struct _vte_draw *draw,
vte::color::rgb const *color, double alpha);
double
-_vte_draw_get_undercurl_height(gint width, int line_width);
+_vte_draw_get_undercurl_height(gint width, double line_width);
void
_vte_draw_draw_undercurl(struct _vte_draw *draw,
- gint x, double y, gint width,
- int line_width,
+ gint x, double y,
+ double line_width,
+ gint count,
vte::color::rgb const *color, double alpha);
G_END_DECLS
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 2b9e7ba2..446204c3 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -655,7 +655,7 @@ public:
long m_double_underline_position;
long m_double_underline_thickness;
double m_undercurl_position;
- long m_undercurl_thickness;
+ double m_undercurl_thickness;
long m_strikethrough_position;
long m_strikethrough_thickness;
long m_overline_position;