diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index bcbed9153a..330183af04 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -1583,6 +1583,9 @@ gsk_standard_contour_add_segment (const GskContour *contour, } } +/* Returns twice the winding number, so we can record the + * 'point on ray' case by returning an odd number. + */ static inline int line_get_crossing (const graphene_point_t *p, const graphene_point_t *p1, @@ -1590,6 +1593,7 @@ line_get_crossing (const graphene_point_t *p, gboolean *on_edge) { int dir = 1; + float r; if (p1->x >= p->x && p2->x >= p->x) return 0; @@ -1603,21 +1607,32 @@ line_get_crossing (const graphene_point_t *p, dir = -1; } - if ((p1->x >= p->x && p1->y == p->y) || - (p2->x >= p->x && p2->y == p->y)) + if ((p1->x <= p->x && p1->y == p->y) || + (p2->x <= p->x && p2->y == p->y)) { - *on_edge = TRUE; - return 0; + if (p1->y == p2->y) + { + if (p1->x >= p->x || p2->x >= p->x) + *on_edge = TRUE; + + return 0; + } + + return dir; } if (p2->y <= p->y || p1->y > p->y) return 0; if (p1->x <= p->x && p2->x <= p->x) - return dir; + return 2 * dir; - if (p->x > p1->x + (p->y - p1->y) * (p2->x - p1->x) / (p2->y - p1->y)) - return dir; + r = p1->x + (p->y - p1->y) * (p2->x - p1->x) / (p2->y - p1->y); + if (p->x > r) + return 2 * dir; + + if (p->x == r) + *on_edge = TRUE; return 0; } @@ -1653,6 +1668,8 @@ gsk_standard_contour_get_winding (const GskContour *contour, winding += line_get_crossing (point, &last_point, &self->points[0], on_edge); + winding /= 2; + return winding; } diff --git a/testsuite/gsk/path-special-cases.c b/testsuite/gsk/path-special-cases.c index 04a575ac1f..79389b5c1a 100644 --- a/testsuite/gsk/path-special-cases.c +++ b/testsuite/gsk/path-special-cases.c @@ -306,6 +306,39 @@ test_serialize_custom_contours (void) gsk_path_unref (path1); } +static void +test_path_winding (void) +{ + struct { + const char *path; + graphene_point_t point; + gboolean result; + } tests[] = { + { "M 150 103 L 250 100 L 300 103 L 250 180 Z", GRAPHENE_POINT_INIT (175, 100), FALSE }, + { "M 100 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (250, 100), TRUE }, + { "M 100 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (400, 100), FALSE}, + { "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (50, 100), FALSE }, + { "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (150, 100), TRUE }, + { "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (250, 100), TRUE }, + { "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (400, 100), FALSE }, + }; + GskPath *path; + GskPathMeasure *measure; + gboolean res; + + for (int i = 0; i < G_N_ELEMENTS (tests); i++) + { + path = gsk_path_parse (tests[i].path); + measure = gsk_path_measure_new (path); + + res = gsk_path_measure_in_fill (measure, &tests[i].point, GSK_FILL_RULE_WINDING); + g_assert_true (res == tests[i].result); + + gsk_path_measure_unref (measure); + gsk_path_unref (path); + } +} + int main (int argc, char *argv[]) @@ -314,6 +347,7 @@ main (int argc, g_test_add_func ("/path/rsvg-parse", test_rsvg_parse); g_test_add_func ("/path/serialize-custom-contours", test_serialize_custom_contours); + g_test_add_func ("/path/winding", test_path_winding); return g_test_run (); }