Triangular Line Caps Patch
Lucian Hodor <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Message-ID | <CAEKStGuDiXZYQKW5JgmO5YTOEOwA4fP3NLXzK5+B4bG7+X_DzA@mail.gmail.com> |
Hello All, I finished writing a patch for adding Triangular Line caps. If someone could look it over and see if I did it correctly your input would be much appreciated. Thanks, Lucian Hodor -- cairo mailing list [email protected] https://lists.cairographics.org/mailman/listinfo/cairo
0001-Triangular-Line-Caps.patch
(application/octet-stream, 17.1 KB)
From bda9db4a7040fd0f5d1b23d57e4649ade4d5a1bf Mon Sep 17 00:00:00 2001 From: Hodor Hodor <[email protected]> Date: Wed, 9 Mar 2016 21:15:35 -0500 Subject: [PATCH] Triangular Line Caps Added the possibility of having triangular Line Caps: The triangular cap is constructed by adding a point one half-line width midway beyond the end points, and the join by adding a point one half-line width beyond the midpoint of the vector connecting the two outer edges. --- src/cairo-path-stroke-boxes.c | 1 + src/cairo-path-stroke-polygon.c | 21 +++++++++++++++++ src/cairo-path-stroke-traps.c | 24 +++++++++++++++++++ src/cairo-path-stroke-tristrip.c | 27 +++++++++++++++++++++ src/cairo-path-stroke.c | 40 ++++++++++++++++++++++++++++++++ src/cairo-pdf-operators.c | 4 ++++ src/cairo-quartz-surface.c | 4 ++++ src/cairo-script-surface.c | 7 ++++-- src/cairo-stroke-style.c | 16 +++++++++---- src/cairo-surface-observer-private.h | 2 +- src/cairo-surface-observer.c | 6 +++-- src/cairo-svg-surface.c | 6 ++++- src/cairo-vg-surface.c | 9 ++++--- src/cairo-xml-surface.c | 5 +++- src/cairo.c | 18 +++++++------- src/cairo.h | 4 +++- src/win32/cairo-win32-printing-surface.c | 4 ++++ 17 files changed, 174 insertions(+), 24 deletions(-) diff --git a/src/cairo-path-stroke-boxes.c b/src/cairo-path-stroke-boxes.c index 7f25bf7..e209028 100644 --- a/src/cairo-path-stroke-boxes.c +++ b/src/cairo-path-stroke-boxes.c @@ -367,6 +367,7 @@ _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *st /* Perform the adjustments of the endpoints. */ if (is_horizontal) { + if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->x <= b->x) { a->x -= half_line_x; diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c index e5082bb..7d4ea7d 100644 --- a/src/cairo-path-stroke-polygon.c +++ b/src/cairo-path-stroke-polygon.c @@ -749,6 +749,7 @@ add_cap (struct stroker *stroker, const cairo_stroke_face_t *f, struct stroke_contour *c) { + switch (stroker->style.line_cap) { case CAIRO_LINE_CAP_ROUND: { cairo_slope_t slope; @@ -780,6 +781,26 @@ add_cap (struct stroker *stroker, p.x = f->cw.x + fvector.dx; p.y = f->cw.y + fvector.dy; contour_add_point (stroker, c, &p); + break; + } + + case CAIRO_LINE_CAP_TRIANGULAR: { + cairo_slope_t fvector; + cairo_point_t p; + double dx, dy; + + dx = f->usr_vector.x; + dy = f->usr_vector.y; + dx *= stroker->half_line_width; + dy *= stroker->half_line_width; + cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); + fvector.dx = _cairo_fixed_from_double (dx); + fvector.dy = _cairo_fixed_from_double (dy); + + p.x = (f->ccw.x + f->cw.x) / 2 + fvector.dx; + p.y = (f->ccw.y + f->cw.y) / 2 + fvector.dy; + contour_add_point (stroker, c, &p); + break; } case CAIRO_LINE_CAP_BUTT: diff --git a/src/cairo-path-stroke-traps.c b/src/cairo-path-stroke-traps.c index da54e5a..4eaf1e5 100644 --- a/src/cairo-path-stroke-traps.c +++ b/src/cairo-path-stroke-traps.c @@ -474,6 +474,7 @@ join (struct stroker *stroker, static void add_cap (struct stroker *stroker, cairo_stroke_face_t *f) { + switch (stroker->style->line_cap) { case CAIRO_LINE_CAP_ROUND: { int start, stop; @@ -536,6 +537,29 @@ add_cap (struct stroker *stroker, cairo_stroke_face_t *f) break; } + case CAIRO_LINE_CAP_TRIANGULAR: { + double dx, dy; + cairo_slope_t fvector; + cairo_point_t quad[4]; + + dx = f->usr_vector.x; + dy = f->usr_vector.y; + dx *= stroker->half_line_width; + dy *= stroker->half_line_width; + cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); + fvector.dx = _cairo_fixed_from_double (dx); + fvector.dy = _cairo_fixed_from_double (dy); + + quad[0] = f->ccw; + quad[1] = quad[0]; + quad[2].x = (f->ccw.x + f->cw.x) / 2 + fvector.dx; + quad[2].y = (f->ccw.y + f->cw.y) / 2 + fvector.dy; + quad[3] = f->cw; + + _cairo_traps_tessellate_convex_quad (stroker->traps, quad); + break; + } + case CAIRO_LINE_CAP_BUTT: default: break; diff --git a/src/cairo-path-stroke-tristrip.c b/src/cairo-path-stroke-tristrip.c index 6ce4131..f1e8f2f 100644 --- a/src/cairo-path-stroke-tristrip.c +++ b/src/cairo-path-stroke-tristrip.c @@ -636,6 +636,33 @@ add_cap (struct stroker *stroker, //contour_add_point (stroker, c, &quad[1]); //contour_add_point (stroker, c, &quad[2]); + break; + } + + case CAIRO_LINE_CAP_TRIANGULAR: { + double dx, dy; + cairo_slope_t fvector; + cairo_point_t quad[4]; + + dx = f->usr_vector.x; + dy = f->usr_vector.y; + dx *= stroker->style.line_width / 2.0; + dy *= stroker->style.line_width / 2.0; + cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); + fvector.dx = _cairo_fixed_from_double (dx); + fvector.dy = _cairo_fixed_from_double (dy); + + /* can we remove point quad[2]? */ + quad[0] = f->ccw; + quad[1].x = (f->ccw.x + f->cw.x)/2 + fvector.dx; + quad[1].y = (f->ccw.y + f->cw.y)/2 + fvector.dy; + quad[2].x = quad[1].x; + quad[2].y = quad[1].y; + quad[3] = f->cw; + + //contour_add_point (stroker, c, &quad[1]); + //contour_add_point (stroker, c, &quad[2]); + break; } case CAIRO_LINE_CAP_BUTT: diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c index 4d4ede8..134cd20 100644 --- a/src/cairo-path-stroke.c +++ b/src/cairo-path-stroke.c @@ -629,6 +629,7 @@ static cairo_status_t _cairo_stroker_add_cap (cairo_stroker_t *stroker, const cairo_stroke_face_t *f) { + switch (stroker->style.line_cap) { case CAIRO_LINE_CAP_ROUND: { cairo_slope_t slope; @@ -688,6 +689,45 @@ _cairo_stroker_add_cap (cairo_stroker_t *stroker, } } + case CAIRO_LINE_CAP_TRIANGULAR: { + double dx, dy; + cairo_slope_t fvector; + cairo_point_t triangle[3]; + + dx = f->usr_vector.x; + dy = f->usr_vector.y; + dx *= stroker->half_line_width; + dy *= stroker->half_line_width; + cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); + fvector.dx = _cairo_fixed_from_double (dx); + fvector.dy = _cairo_fixed_from_double (dy); + + triangle[0] = f->ccw; + triangle[1].x = (f->ccw.x + f->cw.x) / 2 + fvector.dx; + triangle[1].y = (f->ccw.y + f->cw.y) / 2 + fvector.dy; + triangle[2] = f->cw; + + if (stroker->add_external_edge != NULL) { + printf(" stroker->add_external_edge != NULL\n"); + cairo_status_t status; + + status = stroker->add_external_edge (stroker->closure, + &triangle[0], &triangle[1]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &triangle[1], &triangle[2]); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } else { + printf(" stroker->add_external_edge == NULL\n"); + return stroker->add_triangle (stroker->closure, triangle); + } + } + case CAIRO_LINE_CAP_BUTT: default: if (stroker->add_external_edge != NULL) { diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c index dcee25f..968229a 100644 --- a/src/cairo-pdf-operators.c +++ b/src/cairo-pdf-operators.c @@ -588,6 +588,10 @@ _cairo_pdf_line_cap (cairo_line_cap_t cap) return 1; case CAIRO_LINE_CAP_SQUARE: return 2; + case CAIRO_LINE_CAP_TRIANGULAR: + /* PDF has no triangular caps. Use butt caps instead. */ + printf("cairo-pdf-operators.c clear\n"); + return 0; default: ASSERT_NOT_REACHED; return 0; diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index e047b22..c8aa8bd 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -585,6 +585,10 @@ _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap) case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; + + /* There is no triangular line cap */ + case CAIRO_LINE_CAP_TRIANGULAR: + return kCGLineCapButt; } } diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c index ea0117d..2f0e095 100644 --- a/src/cairo-script-surface.c +++ b/src/cairo-script-surface.c @@ -399,10 +399,13 @@ _antialias_to_string (cairo_antialias_t antialias) static const char * _line_cap_to_string (cairo_line_cap_t line_cap) { + static const char *names[] = { - "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ + + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ - "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + "LINE_CAP_SQUARE", /* CAIRO_LINE_CAP_SQUARE */ + "LINE_CAP_TRIANGULAR" /* CAIRO_LINE_CAP_TRIANGULAR */ }; assert (line_cap < ARRAY_LENGTH (names)); return names[line_cap]; diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c index 9c373c3..6829416 100644 --- a/src/cairo-stroke-style.c +++ b/src/cairo-stroke-style.c @@ -221,9 +221,10 @@ _cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style) switch (style->line_cap) { default: ASSERT_NOT_REACHED; - case CAIRO_LINE_CAP_BUTT: cap_scale = 0.0; break; - case CAIRO_LINE_CAP_ROUND: cap_scale = ROUND_MINSQ_APPROXIMATION; break; - case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break; + case CAIRO_LINE_CAP_BUTT: cap_scale = 0.0; break; + case CAIRO_LINE_CAP_ROUND: cap_scale = ROUND_MINSQ_APPROXIMATION; break; + case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break; + case CAIRO_LINE_CAP_TRIANGULAR: cap_scale = 0.5; break; } stroked = 0.0; @@ -322,12 +323,13 @@ _cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, * So when second > first, the second solution is the correct one (i.e. * the solution is always MAX (first, second). */ + switch (style->line_cap) { default: ASSERT_NOT_REACHED; dashes[0] = 0.0; break; - + case CAIRO_LINE_CAP_BUTT: /* Simplified formula (substituting 0 for cap_scale): */ dashes[0] = scale * coverage; @@ -338,6 +340,12 @@ _cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, scale * coverage - ROUND_MINSQ_APPROXIMATION * style->line_width); break; + case CAIRO_LINE_CAP_TRIANGULAR: + dashes[0] = MAX(scale * (coverage - 0.5) / (1.0 - 0.5), + scale * coverage - 0.5 * style->line_width); + break; + + case CAIRO_LINE_CAP_SQUARE: /* * Special attention is needed to handle the case cap_scale == 1 (since the first solution diff --git a/src/cairo-surface-observer-private.h b/src/cairo-surface-observer-private.h index 6ed0c18..0bd1464 100644 --- a/src/cairo-surface-observer-private.h +++ b/src/cairo-surface-observer-private.h @@ -51,7 +51,7 @@ struct stat { }; #define NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY+1) -#define NUM_CAPS (CAIRO_LINE_CAP_SQUARE+1) +#define NUM_CAPS (CAIRO_LINE_CAP_TRIANGULAR+1) #define NUM_JOINS (CAIRO_LINE_JOIN_BEVEL+1) #define NUM_ANTIALIAS (CAIRO_ANTIALIAS_BEST+1) #define NUM_FILL_RULE (CAIRO_FILL_RULE_EVEN_ODD+1) diff --git a/src/cairo-surface-observer.c b/src/cairo-surface-observer.c index 9d12fcd..4d399f6 100644 --- a/src/cairo-surface-observer.c +++ b/src/cairo-surface-observer.c @@ -1652,9 +1652,11 @@ print_fill_rule (cairo_output_stream_t *stream, unsigned int *array) } static const char *cap_names[] = { - "butt", /* CAIRO_LINE_CAP_BUTT */ + + "butt", /* CAIRO_LINE_CAP_BUTT */ "round", /* CAIRO_LINE_CAP_ROUND */ - "square" /* CAIRO_LINE_CAP_SQUARE */ + "square", /* CAIRO_LINE_CAP_SQUARE */ + "triangular"/* CAIRO_LINE_CAP_TRIANGULAR */ }; static void print_line_caps (cairo_output_stream_t *stream, unsigned int *array) diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 2e023b3..2ac2131 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -2096,7 +2096,8 @@ _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, unsigned int i; switch (stroke_style->line_cap) { - case CAIRO_LINE_CAP_BUTT: + + case CAIRO_LINE_CAP_BUTT: line_cap = "butt"; break; case CAIRO_LINE_CAP_ROUND: @@ -2105,6 +2106,9 @@ _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, case CAIRO_LINE_CAP_SQUARE: line_cap = "square"; break; + case CAIRO_LINE_CAP_TRIANGULAR: + line_cap = "triangular"; + break; default: ASSERT_NOT_REACHED; } diff --git a/src/cairo-vg-surface.c b/src/cairo-vg-surface.c index b94b7aa..0bf3219 100644 --- a/src/cairo-vg-surface.c +++ b/src/cairo-vg-surface.c @@ -679,9 +679,12 @@ static VGCapStyle _vg_line_cap_from_cairo (cairo_line_cap_t cap) { switch (cap) { - case CAIRO_LINE_CAP_BUTT: return VG_CAP_BUTT; - case CAIRO_LINE_CAP_ROUND: return VG_CAP_ROUND; - case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE; + + case CAIRO_LINE_CAP_BUTT: return VG_CAP_BUTT; + case CAIRO_LINE_CAP_ROUND: return VG_CAP_ROUND; + case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE; + /* OpenVG does not have a triangular cap style */ + case CAIRO_LINE_CAP_TRIANGULAR: return VG_CAP_BUTT; } ASSERT_NOT_REACHED; diff --git a/src/cairo-xml-surface.c b/src/cairo-xml-surface.c index b885fff..731882a 100644 --- a/src/cairo-xml-surface.c +++ b/src/cairo-xml-surface.c @@ -173,10 +173,13 @@ _antialias_to_string (cairo_antialias_t antialias) static const char * _line_cap_to_string (cairo_line_cap_t line_cap) { + static const char *names[] = { + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ - "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + "LINE_CAP_SQUARE", /* CAIRO_LINE_CAP_SQUARE */ + "LINE_CAP_TRIANGULAR" /* CAIRO_LINE_CAP_TRIANGULAR */ }; assert (line_cap < ARRAY_LENGTH (names)); return names[line_cap]; diff --git a/src/cairo.c b/src/cairo.c index e3acf4d..2ccae42 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -1045,10 +1045,10 @@ slim_hidden_def (cairo_set_line_join); * stroke. The @offset specifies an offset into the pattern at which * the stroke begins. * - * Each "on" segment will have caps applied as if the segment were a - * separate sub-path. In particular, it is valid to use an "on" length - * of 0.0 with %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE in order - * to distributed dots or squares along a path. + * Each "on" segment will have caps applied as if the segment were a separate + * sub-path. In particular, it is valid to use an "on" length of 0.0 with + * %CAIRO_LINE_CAP_ROUND, %CAIRO_LINE_CAP_SQUARE, or %CAIRO_LINE_CAP_TRIANGULAR + * in order to distribute dots, squares, or diamonds along a path. * * Note: The length values are in user-space units as evaluated at the * time of stroking. This is not necessarily the same as the user @@ -2120,11 +2120,11 @@ cairo_mask_surface (cairo_t *cr, * provide a useful result. These can result in two different * situations: * - * 1. Zero-length "on" segments set in cairo_set_dash(). If the cap - * style is %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE then these - * segments will be drawn as circular dots or squares respectively. In - * the case of %CAIRO_LINE_CAP_SQUARE, the orientation of the squares - * is determined by the direction of the underlying path. + * 1. Zero-length "on" segments set in cairo_set_dash(). If the cap style is + * %CAIRO_LINE_CAP_ROUND, %CAIRO_LINE_CAP_SQUARE, or %CAIRO_LINE_CAP_TRIANGULAR + * then these segments will be drawn as circular dots, squares, or diamonds + * respectively. In the case of %CAIRO_LINE_CAP_SQUARE, the orientation of the + * squares is determined by the direction of the underlying path. * * 2. A sub-path created by cairo_move_to() followed by either a * cairo_close_path() or one or more calls to cairo_line_to() to the diff --git a/src/cairo.h b/src/cairo.h index 3104d47..78e77b1 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -758,6 +758,7 @@ cairo_set_line_width (cairo_t *cr, double width); * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point (Since 1.0) * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point (Since 1.0) * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point (Since 1.0) + * @CAIRO_LINE_CAP_TRIANGULAR: use triangular ending, the center of the diamond is the end point (Since 1.16) * * Specifies how to render the endpoints of the path when stroking. * @@ -768,7 +769,8 @@ cairo_set_line_width (cairo_t *cr, double width); typedef enum _cairo_line_cap { CAIRO_LINE_CAP_BUTT, CAIRO_LINE_CAP_ROUND, - CAIRO_LINE_CAP_SQUARE + CAIRO_LINE_CAP_SQUARE, + CAIRO_LINE_CAP_TRIANGULAR } cairo_line_cap_t; cairo_public void diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c index afc0b11..377ce2b 100644 --- a/src/win32/cairo-win32-printing-surface.c +++ b/src/win32/cairo-win32-printing-surface.c @@ -1395,6 +1395,7 @@ _cairo_win32_printing_surface_paint (void *abstract_surface, static int _cairo_win32_line_cap (cairo_line_cap_t cap) { + switch (cap) { case CAIRO_LINE_CAP_BUTT: return PS_ENDCAP_FLAT; @@ -1402,6 +1403,9 @@ _cairo_win32_line_cap (cairo_line_cap_t cap) return PS_ENDCAP_ROUND; case CAIRO_LINE_CAP_SQUARE: return PS_ENDCAP_SQUARE; + /* There is no triangular cap; check out PS_USERSTYLE */ + case CAIRO_LINE_CAP_TRIANGULAR: + return PS_ENDCAP_FLAT; default: ASSERT_NOT_REACHED; return 0; -- 1.9.1