From 2864eb56aab14140c7129bca42165d3109686d6d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 7 Feb 2022 07:43:08 -0500 Subject: Add a line api to PangoRenderer This replaces and generalizes error underlines and lets us draw squiggly strikethroughs and overlines. --- pango/pango-renderer.c | 274 +++++++++++++++++++++------------------------- pango/pango-renderer.h | 21 ++-- pango/pangocairo-render.c | 170 +++++++++++++++------------- pango/pangocairo.h | 26 +++-- 4 files changed, 245 insertions(+), 246 deletions(-) diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c index 6f6f2c73..1a50eaa0 100644 --- a/pango/pango-renderer.c +++ b/pango/pango-renderer.c @@ -82,7 +82,9 @@ static void pango_renderer_default_draw_rectangle (PangoRenderer *rende int y, int width, int height); -static void pango_renderer_default_draw_error_underline (PangoRenderer *renderer, +static void pango_renderer_default_draw_line (PangoRenderer *renderer, + PangoRenderPart part, + PangoLineStyle style, int x, int y, int width, @@ -122,7 +124,7 @@ pango_renderer_class_init (PangoRendererClass *klass) klass->draw_glyphs = pango_renderer_default_draw_glyphs; klass->draw_glyph_item = pango_renderer_default_draw_glyph_item; klass->draw_rectangle = pango_renderer_default_draw_rectangle; - klass->draw_error_underline = pango_renderer_default_draw_error_underline; + klass->draw_line = pango_renderer_default_draw_line; klass->prepare_run = pango_renderer_default_prepare_run; gobject_class->finalize = pango_renderer_finalize; @@ -220,38 +222,17 @@ draw_underline (PangoRenderer *renderer, state->underline = PANGO_LINE_STYLE_NONE; state->underline_position = PANGO_UNDERLINE_POSITION_NORMAL; - switch (underline) - { - case PANGO_LINE_STYLE_NONE: - break; - case PANGO_LINE_STYLE_DOUBLE: - pango_renderer_draw_rectangle (renderer, - PANGO_RENDER_PART_UNDERLINE, - rect->x, - rect->y + 2 * rect->height, - rect->width, - rect->height); - G_GNUC_FALLTHROUGH; - case PANGO_LINE_STYLE_SINGLE: - case PANGO_LINE_STYLE_DOTTED: - case PANGO_LINE_STYLE_DASHED: - pango_renderer_draw_rectangle (renderer, - PANGO_RENDER_PART_UNDERLINE, - rect->x, - rect->y, - rect->width, - rect->height); - break; - case PANGO_LINE_STYLE_WAVY: - pango_renderer_draw_error_underline (renderer, - rect->x, - rect->y, - rect->width, - 3 * rect->height); - break; - default: - break; - } + if (underline == PANGO_LINE_STYLE_DOUBLE || + underline == PANGO_LINE_STYLE_WAVY) + rect->height *= 3; + + pango_renderer_draw_line (renderer, + PANGO_RENDER_PART_UNDERLINE, + underline, + rect->x, + rect->y, + rect->width, + rect->height); } static void @@ -263,32 +244,20 @@ draw_overline (PangoRenderer *renderer, state->overline = PANGO_LINE_STYLE_NONE; - switch (overline) + if (overline == PANGO_LINE_STYLE_DOUBLE || + overline == PANGO_LINE_STYLE_WAVY) { - case PANGO_LINE_STYLE_NONE: - break; - case PANGO_LINE_STYLE_DOUBLE: - pango_renderer_draw_rectangle (renderer, - PANGO_RENDER_PART_OVERLINE, - rect->x, - rect->y - 2 * rect->height, - rect->width, - rect->height); - G_GNUC_FALLTHROUGH; - case PANGO_LINE_STYLE_SINGLE: - case PANGO_LINE_STYLE_DOTTED: - case PANGO_LINE_STYLE_DASHED: - case PANGO_LINE_STYLE_WAVY: - pango_renderer_draw_rectangle (renderer, - PANGO_RENDER_PART_OVERLINE, - rect->x, - rect->y, - rect->width, - rect->height); - break; - default: - break; + rect->y -= 2 * rect->height; + rect->height *= 3; } + + pango_renderer_draw_line (renderer, + PANGO_RENDER_PART_OVERLINE, + overline, + rect->x, + rect->y, + rect->width, + rect->height); } static void @@ -302,33 +271,20 @@ draw_strikethrough (PangoRenderer *renderer, rect->y /= state->strikethrough_glyphs; rect->height /= state->strikethrough_glyphs; - switch (state->strikethrough) + if (state->strikethrough == PANGO_LINE_STYLE_DOUBLE || + state->strikethrough == PANGO_LINE_STYLE_WAVY) { - case PANGO_LINE_STYLE_NONE: - break; - case PANGO_LINE_STYLE_DOUBLE: - pango_renderer_draw_rectangle (renderer, - PANGO_RENDER_PART_STRIKETHROUGH, - rect->x, - rect->y - rect->height, - rect->width, - rect->height); - rect->y += rect->height; - G_GNUC_FALLTHROUGH; - case PANGO_LINE_STYLE_SINGLE: - case PANGO_LINE_STYLE_DOTTED: - case PANGO_LINE_STYLE_DASHED: - case PANGO_LINE_STYLE_WAVY: - pango_renderer_draw_rectangle (renderer, - PANGO_RENDER_PART_STRIKETHROUGH, - rect->x, - rect->y, - rect->width, - rect->height); - break; - default: - break; + rect->y -= rect->height; + rect->height *= 3; } + + pango_renderer_draw_line (renderer, + PANGO_RENDER_PART_STRIKETHROUGH, + state->strikethrough, + rect->x, + rect->y, + rect->width, + rect->height); } state->strikethrough = PANGO_LINE_STYLE_NONE; @@ -403,33 +359,18 @@ add_underline (PangoRenderer *renderer, new_rect.height = underline_thickness; new_rect.y = base_y; - switch (renderer->underline) + if (renderer->underline_position == PANGO_UNDERLINE_POSITION_UNDER) + new_rect.y += ink_rect->y + ink_rect->height + underline_thickness; + else + new_rect.y -= underline_position; + + if (renderer->underline == state->underline && + renderer->underline_position == state->underline_position) { - case PANGO_LINE_STYLE_NONE: - g_assert_not_reached (); - break; - case PANGO_LINE_STYLE_SINGLE: - case PANGO_LINE_STYLE_DASHED: - case PANGO_LINE_STYLE_DOTTED: - if (renderer->underline_position == PANGO_UNDERLINE_POSITION_UNDER) - { - new_rect.y += ink_rect->y + ink_rect->height + underline_thickness; - break; - } - G_GNUC_FALLTHROUGH; - case PANGO_LINE_STYLE_DOUBLE: - case PANGO_LINE_STYLE_WAVY: - new_rect.y -= underline_position; - if (state->underline == renderer->underline) - { - new_rect.y = MAX (current_rect->y, new_rect.y); - new_rect.height = MAX (current_rect->height, new_rect.height); - current_rect->y = new_rect.y; - current_rect->height = new_rect.height; - } - break; - default: - break; + new_rect.y = MAX (current_rect->y, new_rect.y); + new_rect.height = MAX (current_rect->height, new_rect.height); + current_rect->y = new_rect.y; + current_rect->height = new_rect.height; } if (renderer->underline == state->underline && @@ -984,39 +925,6 @@ pango_renderer_default_draw_rectangle (PangoRenderer *renderer, draw_rectangle (renderer, renderer->matrix, part, x, y, width, height); } -/** - * pango_renderer_draw_error_underline: - * @renderer: a `PangoRenderer` - * @x: X coordinate of underline, in Pango units in user coordinate system - * @y: Y coordinate of underline, in Pango units in user coordinate system - * @width: width of underline, in Pango units in user coordinate system - * @height: height of underline, in Pango units in user coordinate system - * - * Draw a squiggly line that approximately covers the given rectangle - * in the style of an underline used to indicate a spelling error. - * - * The width of the underline is rounded to an integer number - * of up/down segments and the resulting rectangle is centered - * in the original rectangle. - * - * This should be called while @renderer is already active. - * Use [method@Pango.Renderer.activate] to activate a renderer. - * - * Since: 1.8 - */ -void -pango_renderer_draw_error_underline (PangoRenderer *renderer, - int x, - int y, - int width, - int height) -{ - g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer)); - g_return_if_fail (renderer->active_count > 0); - - PANGO_RENDERER_GET_CLASS (renderer)->draw_error_underline (renderer, x, y, width, height); -} - /* We are drawing an error underline that looks like one of: * * /\ /\ /\ /\ /\ - @@ -1084,11 +992,12 @@ get_total_matrix (PangoMatrix *total, } static void -pango_renderer_default_draw_error_underline (PangoRenderer *renderer, - int x, - int y, - int width, - int height) +draw_wavy_line (PangoRenderer *renderer, + PangoRenderPart part, + int x, + int y, + int width, + int height) { int square; int unit_width; @@ -1121,7 +1030,7 @@ pango_renderer_default_draw_error_underline (PangoRenderer *renderer, i = (width_units - 1) / 2; while (TRUE) { - draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* A */ + draw_rectangle (renderer, &total, part, /* A */ 0, 0, HEIGHT_SQUARES * 2 - 1, 1); @@ -1129,7 +1038,7 @@ pango_renderer_default_draw_error_underline (PangoRenderer *renderer, break; i--; - draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* B */ + draw_rectangle (renderer, &total, part, /* B */ HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 3), 1, HEIGHT_SQUARES * 2 - 3); @@ -1138,12 +1047,77 @@ pango_renderer_default_draw_error_underline (PangoRenderer *renderer, } if (width_units % 2 == 0) { - draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* C */ + draw_rectangle (renderer, &total, part, /* C */ HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 2), 1, HEIGHT_SQUARES * 2 - 2); } } +/** + * pango_renderer_draw_line: + * @renderer: a `PangoRenderer` + * @part: type of object this rectangle is part of + * @style: the line style + * @x: X position at which to draw rectangle, in user space coordinates + * in Pango units + * @y: Y position at which to draw rectangle, in user space coordinates + * in Pango units + * @width: width of rectangle in Pango units + * @height: height of rectangle in Pango units + * + * Draw a line that approximately covers the given rectangle + * in the given style. + + * Since: 1.52 + */ +void +pango_renderer_draw_line (PangoRenderer *renderer, + PangoRenderPart part, + PangoLineStyle style, + int x, + int y, + int width, + int height) +{ + g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer)); + g_return_if_fail (renderer->active_count > 0); + + PANGO_RENDERER_GET_CLASS (renderer)->draw_line (renderer, part, style, x, y, width, height); +} + +static void +pango_renderer_default_draw_line (PangoRenderer *renderer, + PangoRenderPart part, + PangoLineStyle style, + int x, + int y, + int width, + int height) +{ + switch (style) + { + /* FIXME implement these: */ + case PANGO_LINE_STYLE_DASHED: + case PANGO_LINE_STYLE_DOTTED: + case PANGO_LINE_STYLE_SINGLE: + pango_renderer_draw_rectangle (renderer, part, x, y, width, height); + break; + + case PANGO_LINE_STYLE_DOUBLE: + pango_renderer_draw_rectangle (renderer, part, x, y, width, height / 3); + pango_renderer_draw_rectangle (renderer, part, x, y + height * 2 / 3, width, height / 3); + break; + + case PANGO_LINE_STYLE_WAVY: + draw_wavy_line (renderer, part, x, y, width, height); + break; + + case PANGO_LINE_STYLE_NONE: + default: + break; + } +} + /** * pango_renderer_draw_trapezoid: * @renderer: a `PangoRenderer` diff --git a/pango/pango-renderer.h b/pango/pango-renderer.h index 2151a243..ae0b23ed 100644 --- a/pango/pango-renderer.h +++ b/pango/pango-renderer.h @@ -97,7 +97,6 @@ struct _PangoRenderer * PangoRendererClass: * @draw_glyphs: draws a `PangoGlyphString` * @draw_rectangle: draws a rectangle - * @draw_error_underline: draws a squiggly line that approximately * covers the given rectangle in the style of an underline used to * indicate a spelling error. * @draw_trapezoid: draws a trapezoidal filled area @@ -115,7 +114,7 @@ struct _PangoRenderer * and have default implementations: * - draw_glyphs * - draw_rectangle - * - draw_error_underline + * - draw_line * - draw_glyph_item * * The following vfuncs take device space coordinates as doubles @@ -144,7 +143,9 @@ struct _PangoRendererClass int y, int width, int height); - void (*draw_error_underline) (PangoRenderer *renderer, + void (*draw_line) (PangoRenderer *renderer, + PangoRenderPart part, + PangoLineStyle style, int x, int y, int width, @@ -219,12 +220,6 @@ void pango_renderer_draw_rectangle (PangoRenderer *renderer, int width, int height); PANGO_AVAILABLE_IN_1_8 -void pango_renderer_draw_error_underline (PangoRenderer *renderer, - int x, - int y, - int width, - int height); -PANGO_AVAILABLE_IN_1_8 void pango_renderer_draw_trapezoid (PangoRenderer *renderer, PangoRenderPart part, double y1_, @@ -239,6 +234,14 @@ void pango_renderer_draw_glyph (PangoRenderer *renderer, PangoGlyph glyph, double x, double y); +PANGO_AVAILABLE_IN_1_52 +void pango_renderer_draw_line (PangoRenderer *renderer, + PangoRenderPart part, + PangoLineStyle style, + int x, + int y, + int width, + int height); PANGO_AVAILABLE_IN_1_8 void pango_renderer_activate (PangoRenderer *renderer); diff --git a/pango/pangocairo-render.c b/pango/pangocairo-render.c index 688137dd..de38489a 100644 --- a/pango/pangocairo-render.c +++ b/pango/pangocairo-render.c @@ -613,7 +613,6 @@ pango_cairo_renderer_draw_rectangle (PangoRenderer *renderer, if (!crenderer->do_path) { cairo_save (crenderer->cr); - set_color (crenderer, part); } @@ -625,7 +624,6 @@ pango_cairo_renderer_draw_rectangle (PangoRenderer *renderer, if (!crenderer->do_path) { cairo_fill (crenderer->cr); - cairo_restore (crenderer->cr); } } @@ -743,35 +741,88 @@ draw_error_underline (cairo_t *cr, } static void -pango_cairo_renderer_draw_error_underline (PangoRenderer *renderer, - int x, - int y, - int width, - int height) +pango_cairo_do_line (cairo_t *cr, + PangoLineStyle style, + double x, + double y, + double width, + double height, + gboolean do_path) { - PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer); - cairo_t *cr = crenderer->cr; + if (!do_path) + cairo_new_path (cr); - if (!crenderer->do_path) + switch (style) { + case PANGO_LINE_STYLE_DOTTED: + case PANGO_LINE_STYLE_DASHED: + /* FIXME: make this work for paths */ cairo_save (cr); + cairo_set_line_width (cr, height); + if (style == PANGO_LINE_STYLE_DOTTED) + { + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_dash (cr, (const double []){ 0., 1.5 * height}, 2, 0.); + } + else + { + cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT); + cairo_set_dash (cr, (const double []){ 3. * height, 2 * height}, 2, 0.); + } + cairo_move_to (cr, x, y + height / 2); + cairo_rel_line_to (cr, width, 0.); + cairo_stroke (cr); + cairo_restore (cr); + break; + + case PANGO_LINE_STYLE_SINGLE: + cairo_rectangle (cr, x, y, width, height); + break; + + case PANGO_LINE_STYLE_DOUBLE: + cairo_rectangle (cr, x, y, width, height / 3); + cairo_rectangle (cr, x, y + height * 2 / 3, width, height / 3); + break; - set_color (crenderer, PANGO_RENDER_PART_UNDERLINE); + case PANGO_LINE_STYLE_WAVY: + draw_error_underline (cr, x, y, width, height); + break; - cairo_new_path (cr); + case PANGO_LINE_STYLE_NONE: + default: + break; } - draw_error_underline (cr, - crenderer->x_offset + (double)x / PANGO_SCALE, - crenderer->y_offset + (double)y / PANGO_SCALE, - (double)width / PANGO_SCALE, (double)height / PANGO_SCALE); + if (!do_path) + cairo_fill (cr); +} + +static void +pango_cairo_renderer_draw_line (PangoRenderer *renderer, + PangoRenderPart part, + PangoLineStyle style, + int x, + int y, + int width, + int height) +{ + PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer); + cairo_t *cr = crenderer->cr; if (!crenderer->do_path) { - cairo_fill (cr); - - cairo_restore (cr); + cairo_save (cr); + set_color (crenderer, part); } + + pango_cairo_do_line (cr, style, + crenderer->x_offset + (double)x / PANGO_SCALE, + crenderer->y_offset + (double)y / PANGO_SCALE, + (double)width / PANGO_SCALE, (double)height / PANGO_SCALE, + crenderer->do_path); + + if (!crenderer->do_path) + cairo_restore (cr); } static void @@ -788,7 +839,7 @@ pango_cairo_renderer_class_init (PangoCairoRendererClass *klass) renderer_class->draw_glyph_item = pango_cairo_renderer_draw_glyph_item; renderer_class->draw_rectangle = pango_cairo_renderer_draw_rectangle; renderer_class->draw_trapezoid = pango_cairo_renderer_draw_trapezoid; - renderer_class->draw_error_underline = pango_cairo_renderer_draw_error_underline; + renderer_class->draw_line = pango_cairo_renderer_draw_line; } static PangoCairoRenderer *cached_renderer = NULL; /* MT-safe */ @@ -976,30 +1027,6 @@ _pango_cairo_do_layout (cairo_t *cr, release_renderer (crenderer); } -static void -_pango_cairo_do_error_underline (cairo_t *cr, - double x, - double y, - double width, - double height, - gboolean do_path) -{ - /* We don't use a renderer here, for a simple reason: - * the only renderer we can get is the default renderer, that - * is all implemented here, so we shortcircuit and make our - * life way easier. - */ - - if (!do_path) - cairo_new_path (cr); - - draw_error_underline (cr, x, y, width, height); - - if (!do_path) - cairo_fill (cr); -} - - /* public wrapper of above to show or append path */ @@ -1105,34 +1132,31 @@ pango_cairo_show_layout (cairo_t *cr, } /** - * pango_cairo_show_error_underline: + * pango_cairo_show_line: * @cr: a Cairo context + * @style: the line style * @x: The X coordinate of one corner of the rectangle * @y: The Y coordinate of one corner of the rectangle * @width: Non-negative width of the rectangle * @height: Non-negative height of the rectangle * - * Draw a squiggly line in the specified cairo context that approximately - * covers the given rectangle in the style of an underline used to indicate a - * spelling error. - * - * The width of the underline is rounded to an integer - * number of up/down segments and the resulting rectangle is centered in the - * original rectangle. + * Draw a line in the specified cairo context that approximately + * covers the given rectangle in the given style. * - * Since: 1.14 + * Since: 1.52 */ void -pango_cairo_show_error_underline (cairo_t *cr, - double x, - double y, - double width, - double height) +pango_cairo_show_line (cairo_t *cr, + PangoLineStyle style, + double x, + double y, + double width, + double height) { g_return_if_fail (cr != NULL); g_return_if_fail ((width >= 0) && (height >= 0)); - _pango_cairo_do_error_underline (cr, x, y, width, height, FALSE); + pango_cairo_do_line (cr, style, x, y, width, height, FALSE); } /** @@ -1171,7 +1195,7 @@ pango_cairo_glyph_string_path (cairo_t *cr, * The origin of the glyphs (the left edge of the line) will be * at the current point of the cairo context. * - * Since: 1.10 + * Since: 1.52 */ void pango_cairo_layout_line_path (cairo_t *cr, @@ -1207,31 +1231,27 @@ pango_cairo_layout_path (cairo_t *cr, } /** - * pango_cairo_error_underline_path: + * pango_cairo_line_path: * @cr: a Cairo context + * @style: the line style * @x: The X coordinate of one corner of the rectangle * @y: The Y coordinate of one corner of the rectangle * @width: Non-negative width of the rectangle * @height: Non-negative height of the rectangle * - * Add a squiggly line to the current path in the specified cairo context that - * approximately covers the given rectangle in the style of an underline used - * to indicate a spelling error. - * - * The width of the underline is rounded to an integer number of up/down - * segments and the resulting rectangle is centered in the original rectangle. - * - * Since: 1.14 + * Add a line to the current path in the specified cairo context that + * approximately covers the given rectangle in the given style. */ void -pango_cairo_error_underline_path (cairo_t *cr, - double x, - double y, - double width, - double height) +pango_cairo_line_path (cairo_t *cr, + PangoLineStyle style, + double x, + double y, + double width, + double height) { g_return_if_fail (cr != NULL); g_return_if_fail ((width >= 0) && (height >= 0)); - _pango_cairo_do_error_underline (cr, x, y, width, height, TRUE); + pango_cairo_do_line (cr, style, x, y, width, height, TRUE); } diff --git a/pango/pangocairo.h b/pango/pangocairo.h index f96ae1f4..ac43d680 100644 --- a/pango/pangocairo.h +++ b/pango/pangocairo.h @@ -159,12 +159,13 @@ PANGO_AVAILABLE_IN_1_10 void pango_cairo_show_layout (cairo_t *cr, PangoLayout *layout); -PANGO_AVAILABLE_IN_1_14 -void pango_cairo_show_error_underline (cairo_t *cr, - double x, - double y, - double width, - double height); +PANGO_AVAILABLE_IN_1_52 +void pango_cairo_show_line (cairo_t *cr, + PangoLineStyle style, + double x, + double y, + double width, + double height); /* * Rendering to a path @@ -180,12 +181,13 @@ PANGO_AVAILABLE_IN_1_10 void pango_cairo_layout_path (cairo_t *cr, PangoLayout *layout); -PANGO_AVAILABLE_IN_1_14 -void pango_cairo_error_underline_path (cairo_t *cr, - double x, - double y, - double width, - double height); +PANGO_AVAILABLE_IN_1_52 +void pango_cairo_line_path (cairo_t *cr, + PangoLineStyle style, + double x, + double y, + double width, + double height); G_END_DECLS -- cgit v1.2.1