Add a rectangle contour

Add a contour that optimizes some things for
rectangles. Also add rectangle detection to the
path parser, and add tests similar to what we
have for the other special contours.
This commit is contained in:
Matthias Clasen 2023-08-27 11:17:24 -04:00
parent ddd4855bbc
commit 0a28a5d53a
6 changed files with 712 additions and 41 deletions

View File

@ -1457,6 +1457,544 @@ gsk_circle_contour_new (const graphene_point_t *center,
return (GskContour *) self; return (GskContour *) self;
} }
/* }}} */
/* {{{ Rectangle */
typedef struct _GskRectContour GskRectContour;
struct _GskRectContour
{
GskContour contour;
float x;
float y;
float width;
float height;
float length;
};
static void
gsk_rect_contour_copy (const GskContour *contour,
GskContour *dest)
{
const GskRectContour *self = (const GskRectContour *) contour;
GskRectContour *target = (GskRectContour *) dest;
*target = *self;
}
static GskPathFlags
gsk_rect_contour_get_flags (const GskContour *contour)
{
return GSK_PATH_FLAT | GSK_PATH_CLOSED;
}
static void
gsk_rect_contour_print (const GskContour *contour,
GString *string)
{
const GskRectContour *self = (const GskRectContour *) contour;
_g_string_append_point (string, "M ", &GRAPHENE_POINT_INIT (self->x, self->y));
_g_string_append_double (string, " h ", self->width);
_g_string_append_double (string, " v ", self->height);
_g_string_append_double (string, " h ", - self->width);
g_string_append (string, " z");
}
static gboolean
gsk_rect_contour_get_bounds (const GskContour *contour,
GskBoundingBox *bounds)
{
const GskRectContour *self = (const GskRectContour *) contour;
gsk_bounding_box_init (bounds,
&GRAPHENE_POINT_INIT (self->x, self->y),
&GRAPHENE_POINT_INIT (self->x + self->width, self->y + self->height));
return TRUE;
}
static gboolean
gsk_rect_contour_get_stroke_bounds (const GskContour *contour,
const GskStroke *stroke,
GskBoundingBox *bounds)
{
const GskRectContour *self = (const GskRectContour *) contour;
graphene_rect_t rect;
graphene_rect_init (&rect, self->x, self->y, self->width, self->height);
graphene_rect_inset (&rect, - stroke->line_width / 2, - stroke->line_width / 2);
gsk_bounding_box_init_from_rect (bounds, &rect);
return TRUE;
}
static gboolean
gsk_rect_contour_foreach (const GskContour *contour,
float tolerance,
GskPathForeachFunc func,
gpointer user_data)
{
const GskRectContour *self = (const GskRectContour *) contour;
graphene_point_t pts[] = {
GRAPHENE_POINT_INIT (self->x, self->y),
GRAPHENE_POINT_INIT (self->x + self->width, self->y),
GRAPHENE_POINT_INIT (self->x + self->width, self->y + self->height),
GRAPHENE_POINT_INIT (self->x, self->y + self->height),
GRAPHENE_POINT_INIT (self->x, self->y)
};
return func (GSK_PATH_MOVE, &pts[0], 1, 0.f, user_data) &&
func (GSK_PATH_LINE, &pts[0], 2, 0.f, user_data) &&
func (GSK_PATH_LINE, &pts[1], 2, 0.f, user_data) &&
func (GSK_PATH_LINE, &pts[2], 2, 0.f, user_data) &&
func (GSK_PATH_CLOSE, &pts[3], 2, 0.f, user_data);
}
static GskContour *
gsk_rect_contour_reverse (const GskContour *contour)
{
const GskRectContour *self = (const GskRectContour *) contour;
return gsk_rect_contour_new (&GRAPHENE_RECT_INIT (self->x + self->width,
self->y,
- self->width,
self->height));
}
static int
gsk_rect_contour_get_winding (const GskContour *contour,
const graphene_point_t *point)
{
const GskRectContour *self = (const GskRectContour *) contour;
graphene_rect_t rect;
graphene_rect_init (&rect, self->x, self->y, self->width, self->height);
if (graphene_rect_contains_point (&rect, point))
{
if ((self->width < 0) != (self->height < 0))
return -1;
else
return 1;
}
return 0;
}
static gsize
gsk_rect_contour_get_n_ops (const GskContour *contour)
{
return 2;
}
static gboolean
gsk_rect_contour_closest_point (const GskRectContour *self,
const graphene_point_t *point,
float threshold,
float *out_distance,
float *out_offset)
{
graphene_point_t t, p;
float distance;
/* offset coords to be relative to rectangle */
t.x = point->x - self->x;
t.y = point->y - self->y;
if (self->width)
{
/* do unit square math */
t.x /= self->width;
/* move point onto the square */
t.x = CLAMP (t.x, 0.f, 1.f);
}
else
t.x = 0.f;
if (self->height)
{
t.y /= self->height;
t.y = CLAMP (t.y, 0.f, 1.f);
}
else
t.y = 0.f;
if (t.x > 0 && t.x < 1 && t.y > 0 && t.y < 1)
{
float diff = MIN (t.x, 1.f - t.x) * fabsf (self->width) - MIN (t.y, 1.f - t.y) * fabsf (self->height);
if (diff < 0.f)
t.x = ceilf (t.x - 0.5f); /* round 0.5 down */
else if (diff > 0.f)
t.y = roundf (t.y); /* round 0.5 up */
else
{
/* at least 2 points match, return the first one in the stroke */
if (t.y <= 1.f - t.y)
t.y = 0.f;
else if (1.f - t.x <= t.x)
t.x = 1.f;
else
t.y = 1.f;
}
}
/* Don't let -0 confuse us */
t.x = fabsf (t.x);
t.y = fabsf (t.y);
p = GRAPHENE_POINT_INIT (self->x + t.x * self->width,
self->y + t.y * self->height);
distance = graphene_point_distance (point, &p, NULL, NULL);
if (distance > threshold)
return FALSE;
*out_distance = distance;
if (t.y == 0)
*out_offset = t.x * fabsf (self->width);
else if (t.y == 1)
*out_offset = (2 - t.x) * fabsf (self->width) + fabsf (self->height);
else if (t.x == 1)
*out_offset = fabsf (self->width) + t.y * fabsf (self->height);
else
*out_offset = 2 * fabsf (self->width) + (2 - t.y) * fabsf (self->height);
return TRUE;
}
static gboolean
gsk_rect_contour_get_closest_point (const GskContour *contour,
const graphene_point_t *point,
float threshold,
GskRealPathPoint *result,
float *out_dist)
{
const GskRectContour *self = (const GskRectContour *) contour;
float distance;
if (gsk_rect_contour_closest_point (self, point, threshold, out_dist, &distance))
{
result->idx = 1;
result->t = distance / self->length;
return TRUE;
}
return FALSE;
}
static void
set_tangent (float xs,
float ys,
float xe,
float ye,
GskPathDirection direction,
graphene_vec2_t *tangent)
{
if (direction == GSK_PATH_TO_START || direction == GSK_PATH_FROM_START)
graphene_vec2_init (tangent, xs, ys);
else
graphene_vec2_init (tangent, xe, ye);
if (direction == GSK_PATH_TO_START || direction == GSK_PATH_FROM_END)
graphene_vec2_negate (tangent, tangent);
}
static void
gsk_rect_contour_pos_tangent (const GskRectContour *self,
float distance,
GskPathDirection direction,
graphene_point_t *pos,
graphene_vec2_t *tangent)
{
if (distance == 0)
{
if (pos)
*pos = GRAPHENE_POINT_INIT (self->x, self->y);
if (tangent)
set_tangent (0.f, - copysignf (1.f, self->height),
copysignf (1.f, self->width), 0.f,
direction, tangent);
return;
}
if (distance < fabsf (self->width))
{
if (pos)
*pos = GRAPHENE_POINT_INIT (self->x + copysignf (distance, self->width), self->y);
if (tangent)
set_tangent (copysignf (1.f, self->width), 0.f,
copysignf (1.f, self->width), 0.f,
direction, tangent);
return;
}
distance -= fabsf (self->width);
if (distance == 0)
{
if (pos)
*pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y);
if (tangent)
set_tangent (copysignf (1.f, self->width), 0.f,
0.f, copysignf (1.f, self->height),
direction, tangent);
return;
}
if (distance < fabsf (self->height))
{
if (pos)
*pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y + copysignf (distance, self->height));
if (tangent)
set_tangent (0.f, copysignf (1.f, self->height),
0.f, copysignf (1.f, self->height),
direction, tangent);
return;
}
distance -= fabs (self->height);
if (distance == 0)
{
if (pos)
*pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y + self->height);
if (tangent)
set_tangent (0.f, copysignf (1.f, self->height),
- copysignf (1.f, self->width), 0.f,
direction, tangent);
return;
}
if (distance < fabsf (self->width))
{
if (pos)
*pos = GRAPHENE_POINT_INIT (self->x + self->width - copysignf (distance, self->width), self->y + self->height);
if (tangent)
set_tangent (- copysignf (1.f, self->width), 0.f,
- copysignf (1.f, self->width), 0.f,
direction, tangent);
return;
}
distance -= fabsf (self->width);
if (distance == 0)
{
if (pos)
*pos = GRAPHENE_POINT_INIT (self->x, self->y + self->height);
if (tangent)
set_tangent (- copysignf (1.f, self->width), 0.f,
0.f, - copysignf (1.f, self->height),
direction, tangent);
return;
}
if (distance < fabsf (self->height))
{
if (pos)
*pos = GRAPHENE_POINT_INIT (self->x, self->y + self->height - copysignf (distance, self->height));
if (tangent)
set_tangent (0.f, - copysignf (1.f, self->height),
0.f, - copysignf (1.f, self->height),
direction, tangent);
return;
}
if (pos)
*pos = GRAPHENE_POINT_INIT (self->x, self->y);
if (tangent)
set_tangent (0.f, - copysignf (1.f, self->height),
copysignf (1.f, self->width), 0.f,
direction, tangent);
}
static void
gsk_rect_contour_get_position (const GskContour *contour,
GskRealPathPoint *point,
graphene_point_t *position)
{
const GskRectContour *self = (const GskRectContour *) contour;
gsk_rect_contour_pos_tangent (self, point->t * self->length, GSK_PATH_TO_END, position, NULL);
}
static void
gsk_rect_contour_get_tangent (const GskContour *contour,
GskRealPathPoint *point,
GskPathDirection direction,
graphene_vec2_t *tangent)
{
const GskRectContour *self = (const GskRectContour *) contour;
gsk_rect_contour_pos_tangent (self, point->t * self->length, direction, NULL, tangent);
}
static float
gsk_rect_contour_get_curvature (const GskContour *contour,
GskRealPathPoint *point,
GskPathDirection direction,
graphene_point_t *center)
{
return 0;
}
static void
gsk_rect_contour_add_segment (const GskContour *contour,
GskPathBuilder *builder,
gboolean emit_move_to,
GskRealPathPoint *start_point,
GskRealPathPoint *end_point)
{
const GskRectContour *self = (const GskRectContour *) contour;
float w = fabsf (self->width);
float h = fabsf (self->height);
float start = start_point->t * self->length;
float end = end_point->t * self->length;
if (start < w)
{
if (emit_move_to)
gsk_path_builder_move_to (builder, self->x + start * (w / self->width), self->y);
if (end <= w)
{
gsk_path_builder_line_to (builder, self->x + end * (w / self->width), self->y);
return;
}
gsk_path_builder_line_to (builder, self->x + self->width, self->y);
}
start -= w;
end -= w;
if (start < h)
{
if (start >= 0 && emit_move_to)
gsk_path_builder_move_to (builder, self->x + self->width, self->y + start * (h / self->height));
if (end <= h)
{
gsk_path_builder_line_to (builder, self->x + self->width, self->y + end * (h / self->height));
return;
}
gsk_path_builder_line_to (builder, self->x + self->width, self->y + self->height);
}
start -= h;
end -= h;
if (start < w)
{
if (start >= 0 && emit_move_to)
gsk_path_builder_move_to (builder, self->x + (w - start) * (w / self->width), self->y + self->height);
if (end <= w)
{
gsk_path_builder_line_to (builder, self->x + (w - end) * (w / self->width), self->y + self->height);
return;
}
gsk_path_builder_line_to (builder, self->x, self->y + self->height);
}
start -= w;
end -= w;
if (start < h)
{
if (start >= 0 && emit_move_to)
gsk_path_builder_move_to (builder, self->x, self->y + (h - start) * (h / self->height));
if (end <= h)
{
gsk_path_builder_line_to (builder, self->x, self->y + (h - end) * (h / self->height));
return;
}
gsk_path_builder_line_to (builder, self->x, self->y);
}
}
static gpointer
gsk_rect_contour_init_measure (const GskContour *contour,
float tolerance,
float *out_length)
{
const GskRectContour *self = (const GskRectContour *) contour;
*out_length = self->length;
return NULL;
}
static void
gsk_rect_contour_free_measure (const GskContour *contour,
gpointer data)
{
}
static void
gsk_rect_contour_get_point (const GskContour *contour,
gpointer measure_data,
float distance,
GskRealPathPoint *result)
{
const GskRectContour *self = (const GskRectContour *) contour;
result->idx = 1;
result->t = CLAMP (distance / self->length, 0, 1);
}
static float
gsk_rect_contour_get_distance (const GskContour *contour,
GskRealPathPoint *point,
gpointer measure_data)
{
const GskRectContour *self = (const GskRectContour *) contour;
return point->t * self->length;
}
static const GskContourClass GSK_RECT_CONTOUR_CLASS =
{
sizeof (GskRectContour),
"GskRectContour",
gsk_rect_contour_copy,
gsk_contour_get_size_default,
gsk_rect_contour_get_flags,
gsk_rect_contour_print,
gsk_rect_contour_get_bounds,
gsk_rect_contour_get_stroke_bounds,
gsk_rect_contour_foreach,
gsk_rect_contour_reverse,
gsk_rect_contour_get_winding,
gsk_rect_contour_get_n_ops,
gsk_rect_contour_get_closest_point,
gsk_rect_contour_get_position,
gsk_rect_contour_get_tangent,
gsk_rect_contour_get_curvature,
gsk_rect_contour_add_segment,
gsk_rect_contour_init_measure,
gsk_rect_contour_free_measure,
gsk_rect_contour_get_point,
gsk_rect_contour_get_distance,
};
GskContour *
gsk_rect_contour_new (const graphene_rect_t *rect)
{
GskRectContour *self;
self = g_new0 (GskRectContour, 1);
self->contour.klass = &GSK_RECT_CONTOUR_CLASS;
self->x = rect->origin.x;
self->y = rect->origin.y;
self->width = rect->size.width;
self->height = rect->size.height;
self->length = 2 * (fabsf (self->width) + fabsf (self->height));
return (GskContour *) self;
}
/* }}} */ /* }}} */
/* {{{ Rounded Rectangle */ /* {{{ Rounded Rectangle */

View File

@ -36,6 +36,7 @@ GskContour * gsk_standard_contour_new (GskPathFlags
GskContour * gsk_circle_contour_new (const graphene_point_t *center, GskContour * gsk_circle_contour_new (const graphene_point_t *center,
float radius); float radius);
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
GskContour * gsk_rounded_rect_contour_new (const GskRoundedRect *rounded_rect); GskContour * gsk_rounded_rect_contour_new (const GskRoundedRect *rounded_rect);
const char * gsk_contour_get_type_name (const GskContour *self); const char * gsk_contour_get_type_name (const GskContour *self);

View File

@ -995,6 +995,35 @@ is_line (double x0, double y0,
NEAR (x0, x1) && NEAR (x0, x2) && NEAR (x0, x3); NEAR (x0, x1) && NEAR (x0, x2) && NEAR (x0, x3);
} }
static gboolean
parse_rectangle (const char **p,
double *x,
double *y,
double *w,
double *h)
{
const char *o = *p;
double w2;
if (parse_coordinate_pair (p, x, y) &&
parse_string (p, "h") &&
parse_coordinate (p, w) &&
parse_string (p, "v") &&
parse_coordinate (p, h) &&
parse_string (p, "h") &&
parse_coordinate (p, &w2) &&
parse_string (p, "z") &&
NEAR (w2, -*w))
{
skip_whitespace (p);
return TRUE;
}
*p = o;
return FALSE;
}
static gboolean static gboolean
parse_circle (const char **p, parse_circle (const char **p,
double *cx, double *cx,
@ -1101,6 +1130,9 @@ parse_rounded_rect (const char **p,
rr->corner[GSK_CORNER_TOP_RIGHT] = GRAPHENE_SIZE_INIT (x2 - x1, y3 - y2); rr->corner[GSK_CORNER_TOP_RIGHT] = GRAPHENE_SIZE_INIT (x2 - x1, y3 - y2);
rr->corner[GSK_CORNER_BOTTOM_RIGHT] = GRAPHENE_SIZE_INIT (x5 - x6, y5 - y4); rr->corner[GSK_CORNER_BOTTOM_RIGHT] = GRAPHENE_SIZE_INIT (x5 - x6, y5 - y4);
rr->corner[GSK_CORNER_BOTTOM_LEFT] = GRAPHENE_SIZE_INIT (x7 - x8, y8 - y7); rr->corner[GSK_CORNER_BOTTOM_LEFT] = GRAPHENE_SIZE_INIT (x7 - x8, y8 - y7);
skip_whitespace (p);
return TRUE; return TRUE;
} }
} }
@ -1195,35 +1227,47 @@ gsk_path_parse (const char *string)
case 'M': case 'M':
case 'm': case 'm':
{ {
double x1, y1, r; double x1, y1, w, h, r;
GskRoundedRect rr; GskRoundedRect rr;
/* Look for special contours */ /* Look for special contours */
if (parse_circle (&p, &x1, &y1, &r)) if (parse_rectangle (&p, &x1, &y1, &w, &h))
{
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (x1, y1, w, h));
if (_strchr ("zZX", prev_cmd))
{
path_x = x1;
path_y = y1;
}
x = x1;
y = y1;
}
else if (parse_circle (&p, &x1, &y1, &r))
{ {
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (x1, y1), r); gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (x1, y1), r);
x = x1 + r;
y = y1;
if (_strchr ("zZX", prev_cmd)) if (_strchr ("zZX", prev_cmd))
{ {
path_x = x; path_x = x1 + r;
path_y = y; path_y = y1;
} }
x = x1 + r;
y = y1;
} }
else if (parse_rounded_rect (&p, &rr)) else if (parse_rounded_rect (&p, &rr))
{ {
gsk_path_builder_add_rounded_rect (builder, &rr); gsk_path_builder_add_rounded_rect (builder, &rr);
x = rr.bounds.origin.x + rr.corner[GSK_CORNER_TOP_LEFT].width;
y = rr.bounds.origin.y;
if (_strchr ("zZX", prev_cmd)) if (_strchr ("zZX", prev_cmd))
{ {
path_x = x; path_x = rr.bounds.origin.x + rr.corner[GSK_CORNER_TOP_LEFT].width;
path_y = y; path_y = rr.bounds.origin.y;
} }
x = rr.bounds.origin.x + rr.corner[GSK_CORNER_TOP_LEFT].width;
y = rr.bounds.origin.y;
} }
else if (parse_coordinate_pair (&p, &x1, &y1)) else if (parse_coordinate_pair (&p, &x1, &y1))
{ {
@ -1232,6 +1276,7 @@ gsk_path_parse (const char *string)
x1 += x; x1 += x;
y1 += y; y1 += y;
} }
if (repeat) if (repeat)
gsk_path_builder_line_to (builder, x1, y1); gsk_path_builder_line_to (builder, x1, y1);
else else
@ -1243,6 +1288,7 @@ gsk_path_parse (const char *string)
path_y = y1; path_y = y1;
} }
} }
x = x1; x = x1;
y = y1; y = y1;
} }

View File

@ -460,20 +460,10 @@ void
gsk_path_builder_add_rect (GskPathBuilder *self, gsk_path_builder_add_rect (GskPathBuilder *self,
const graphene_rect_t *rect) const graphene_rect_t *rect)
{ {
graphene_point_t current;
g_return_if_fail (self != NULL); g_return_if_fail (self != NULL);
g_return_if_fail (rect != NULL);
current = self->current_point; gsk_path_builder_add_contour (self, gsk_rect_contour_new (rect));
gsk_path_builder_move_to (self, rect->origin.x, rect->origin.y);
gsk_path_builder_rel_line_to (self, rect->size.width, 0);
gsk_path_builder_rel_line_to (self, 0, rect->size.height);
gsk_path_builder_rel_line_to (self, - rect->size.width, 0);
gsk_path_builder_close (self);
self->current_point = current;
} }
/** /**

View File

@ -116,18 +116,14 @@ test_circle_winding (void)
path1 = convert_to_standard_contour (path); path1 = convert_to_standard_contour (path);
contour1 = gsk_path_get_contour (path1, 0); contour1 = gsk_path_get_contour (path1, 0);
g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (100, 100))
==
gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (100, 100)));
builder = gsk_path_builder_new (); builder = gsk_path_builder_new ();
gsk_path_builder_add_reverse_path (builder, path); gsk_path_builder_add_reverse_path (builder, path);
path2 = gsk_path_builder_free_to_path (builder); path2 = gsk_path_builder_free_to_path (builder);
contour2 = gsk_path_get_contour (path2, 0); contour2 = gsk_path_get_contour (path2, 0);
g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (100, 100)) g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (100, 100)) == 1);
== g_assert_true (gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (100, 100)) == 1);
- gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (100, 100))); g_assert_true (gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (100, 100)) == -1);
gsk_path_unref (path2); gsk_path_unref (path2);
gsk_path_unref (path1); gsk_path_unref (path1);
@ -189,22 +185,79 @@ test_rounded_rect_winding (void)
path1 = convert_to_standard_contour (path); path1 = convert_to_standard_contour (path);
contour1 = gsk_path_get_contour (path1, 0); contour1 = gsk_path_get_contour (path1, 0);
g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (150, 150)) builder = gsk_path_builder_new ();
== gsk_path_builder_add_reverse_path (builder, path);
gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (150, 150))); path2 = gsk_path_builder_free_to_path (builder);
contour2 = gsk_path_get_contour (path2, 0);
g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (150, 150)) == 1);
g_assert_true (gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (150, 150)) == 1);
g_assert_true (gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (150, 150)) == -1);
gsk_path_unref (path2);
gsk_path_unref (path1);
}
static void
test_rect_roundtrip (void)
{
graphene_rect_t rect;
GskPathBuilder *builder;
GskPath *path, *path2;
const GskContour *contour;
char *s;
rect = GRAPHENE_RECT_INIT (100, 100, 200, 150);
builder = gsk_path_builder_new ();
gsk_path_builder_add_rect (builder, &rect);
path = gsk_path_builder_free_to_path (builder);
contour = gsk_path_get_contour (path, 0);
g_assert_cmpstr (gsk_contour_get_type_name (contour), ==, "GskRectContour");
s = gsk_path_to_string (path);
path2 = gsk_path_parse (s);
contour = gsk_path_get_contour (path2, 0);
g_assert_cmpstr (gsk_contour_get_type_name (contour), ==, "GskRectContour");
g_free (s);
gsk_path_unref (path2);
gsk_path_unref (path);
}
static void
test_rect_winding (void)
{
GskPathBuilder *builder;
GskPath *path, *path1, *path2, *path3;
const GskContour *contour, *contour1, *contour2, *contour3;
builder = gsk_path_builder_new ();
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (100, 100, 200, 150));
path = gsk_path_builder_free_to_path (builder);
contour = gsk_path_get_contour (path, 0);
path1 = convert_to_standard_contour (path);
contour1 = gsk_path_get_contour (path1, 0);
builder = gsk_path_builder_new (); builder = gsk_path_builder_new ();
gsk_path_builder_add_reverse_path (builder, path); gsk_path_builder_add_reverse_path (builder, path);
path2 = gsk_path_builder_free_to_path (builder); path2 = gsk_path_builder_free_to_path (builder);
contour2 = gsk_path_get_contour (path2, 0); contour2 = gsk_path_get_contour (path2, 0);
g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (150, 150)) path3 = convert_to_standard_contour (path2);
== contour3 = gsk_path_get_contour (path3, 0);
- gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (150, 150)));
g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (150, 150)) == 1);
g_assert_true (gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (150, 150)) == 1);
g_assert_true (gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (150, 150)) == -1);
g_assert_true (gsk_contour_get_winding (contour3, &GRAPHENE_POINT_INIT (150, 150)) == -1);
gsk_path_unref (path3);
gsk_path_unref (path2); gsk_path_unref (path2);
gsk_path_unref (path1); gsk_path_unref (path1);
gsk_path_unref (path);
} }
int int
@ -216,6 +269,8 @@ main (int argc, char *argv[])
g_test_add_func ("/path/circle/winding", test_circle_winding); g_test_add_func ("/path/circle/winding", test_circle_winding);
g_test_add_func ("/path/rounded-rect/roundtrip", test_rounded_rect_roundtrip); g_test_add_func ("/path/rounded-rect/roundtrip", test_rounded_rect_roundtrip);
g_test_add_func ("/path/rounded-rect/winding", test_rounded_rect_winding); g_test_add_func ("/path/rounded-rect/winding", test_rounded_rect_winding);
g_test_add_func ("/path/rect/roundtrip", test_rect_roundtrip);
g_test_add_func ("/path/rect/winding", test_rect_winding);
return g_test_run (); return g_test_run ();
} }

View File

@ -323,7 +323,7 @@ test_rect_path (void)
g_assert_true (gsk_path_is_closed (path)); g_assert_true (gsk_path_is_closed (path));
s = gsk_path_to_string (path); s = gsk_path_to_string (path);
g_assert_cmpstr (s, ==, "M 0 0 L 200 0 L 200 100 L 0 100 Z"); g_assert_cmpstr (s, ==, "M 0 0 h 200 v 100 h -200 z");
g_free (s); g_free (s);
g_assert_true (gsk_path_get_bounds (path, &bounds)); g_assert_true (gsk_path_get_bounds (path, &bounds));
@ -819,6 +819,42 @@ test_rounded_rect (void)
gsk_path_unref (path); gsk_path_unref (path);
} }
static void
test_rect (void)
{
graphene_rect_t rect;
GskPathBuilder *builder;
GskPath *path;
GskPathPoint point;
graphene_point_t p;
rect = GRAPHENE_RECT_INIT (10, 10, 100, 50);
builder = gsk_path_builder_new ();
gsk_path_builder_add_rect (builder, &rect);
path = gsk_path_builder_free_to_path (builder);
for (int i = 0; i < 100; i++)
{
p = GRAPHENE_POINT_INIT (g_test_rand_double_range (0, 200),
g_test_rand_double_range (0, 200));
g_assert_true (graphene_rect_contains_point (&rect, &p) == gsk_path_in_fill (path, &p, GSK_FILL_RULE_WINDING));
}
gsk_path_get_start_point (path, &point);
gsk_path_point_get_position (&point, path, &p);
g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (10, 10)));
gsk_path_get_end_point (path, &point);
gsk_path_point_get_position (&point, path, &p);
g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (10, 10)));
gsk_path_unref (path);
}
static void static void
test_circle (void) test_circle (void)
{ {
@ -883,7 +919,11 @@ test_circle (void)
builder = gsk_path_builder_new (); builder = gsk_path_builder_new ();
gsk_path_builder_add_path (builder, path); gsk_path_builder_add_path (builder, path);
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (-2, -2, 4, 4)); gsk_path_builder_move_to (builder, -2, -2);
gsk_path_builder_line_to (builder, 2, 0);
gsk_path_builder_line_to (builder, 2, 2);
gsk_path_builder_line_to (builder, -2, 2);
gsk_path_builder_close (builder);
path4 = gsk_path_builder_free_to_path (builder); path4 = gsk_path_builder_free_to_path (builder);
g_assert_true (gsk_path_in_fill (path4, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING)); g_assert_true (gsk_path_in_fill (path4, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING));
@ -1002,7 +1042,7 @@ main (int argc, char *argv[])
g_test_add_func ("/path/rsvg-parse", test_rsvg_parse); g_test_add_func ("/path/rsvg-parse", test_rsvg_parse);
g_test_add_func ("/path/empty", test_empty_path); g_test_add_func ("/path/empty", test_empty_path);
g_test_add_func ("/path/rect", test_rect_path); g_test_add_func ("/path/rect-path", test_rect_path);
g_test_add_func ("/path/foreach", test_foreach); g_test_add_func ("/path/foreach", test_foreach);
g_test_add_func ("/path/point", test_path_point); g_test_add_func ("/path/point", test_path_point);
g_test_add_func ("/path/segments", test_path_segments); g_test_add_func ("/path/segments", test_path_segments);
@ -1011,6 +1051,7 @@ main (int argc, char *argv[])
g_test_add_func ("/path/builder/add", test_path_builder_add); g_test_add_func ("/path/builder/add", test_path_builder_add);
g_test_add_func ("/path/rotated-arc", test_rotated_arc); g_test_add_func ("/path/rotated-arc", test_rotated_arc);
g_test_add_func ("/path/rounded-rect", test_rounded_rect); g_test_add_func ("/path/rounded-rect", test_rounded_rect);
g_test_add_func ("/path/rect", test_rect);
g_test_add_func ("/path/circle", test_circle); g_test_add_func ("/path/circle", test_circle);
g_test_add_func ("/path/length", test_length); g_test_add_func ("/path/length", test_length);
g_test_add_func ("/path/rect/segment", test_rect_segment); g_test_add_func ("/path/rect/segment", test_rect_segment);