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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.