Merge branch 'rounded-rect-contour' into 'main'

path: Add a rounded rect contour

See merge request GNOME/gtk!6347
This commit is contained in:
Matthias Clasen
2023-08-27 13:36:53 +00:00
5 changed files with 530 additions and 72 deletions

View File

@@ -1244,13 +1244,10 @@ gsk_circle_contour_get_winding (const GskContour *contour,
{
const GskCircleContour *self = (const GskCircleContour *) contour;
if (graphene_point_distance (point, &self->center, NULL, NULL) >= self->radius)
return 0;
if (graphene_point_distance (point, &self->center, NULL, NULL) <= self->radius)
return self->ccw ? -1 : 1;
if (self->ccw)
return -1;
else
return 1;
return 0;
}
static gsize
@@ -1500,6 +1497,382 @@ gsk_circle_contour_new (const graphene_point_t *center,
return (GskContour *) self;
}
/* }}} */
/* {{{ Rounded Rectangle */
typedef struct _GskRoundedRectContour GskRoundedRectContour;
struct _GskRoundedRectContour
{
GskContour contour;
GskRoundedRect rect;
gboolean ccw;
};
static void
gsk_rounded_rect_contour_copy (const GskContour *contour,
GskContour *dest)
{
const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour;
GskRoundedRectContour *target = (GskRoundedRectContour *) dest;
*target = *self;
}
static GskPathFlags
gsk_rounded_rect_contour_get_flags (const GskContour *contour)
{
return GSK_PATH_CLOSED;
}
static gboolean
gsk_rounded_rect_contour_get_bounds (const GskContour *contour,
GskBoundingBox *bounds)
{
const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour;
gsk_bounding_box_init_from_rect (bounds, &self->rect.bounds);
return TRUE;
}
static gboolean
gsk_rounded_rect_contour_get_stroke_bounds (const GskContour *contour,
const GskStroke *stroke,
GskBoundingBox *bounds)
{
const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour;
GskBoundingBox b;
gsk_bounding_box_init_from_rect (&b, &self->rect.bounds);
gsk_bounding_box_init (bounds,
&GRAPHENE_POINT_INIT (b.min.x - stroke->line_width / 2,
b.min.y - stroke->line_width / 2),
&GRAPHENE_POINT_INIT (b.max.x + stroke->line_width / 2,
b.max.y + stroke->line_width / 2));
return TRUE;
}
static void
gsk_rounded_rect_contour_get_start_end (const GskContour *contour,
graphene_point_t *start,
graphene_point_t *end)
{
const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour;
if (start)
*start = GRAPHENE_POINT_INIT (self->rect.bounds.origin.x + self->rect.corner[GSK_CORNER_TOP_LEFT].width,
self->rect.bounds.origin.y);
if (end)
*end = GRAPHENE_POINT_INIT (self->rect.bounds.origin.x + self->rect.corner[GSK_CORNER_TOP_LEFT].width,
self->rect.bounds.origin.y);
}
static void
get_rounded_rect_points (const GskRoundedRect *rect,
graphene_point_t *pts)
{
pts[0] = GRAPHENE_POINT_INIT (rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width, rect->bounds.origin.y);
pts[1] = GRAPHENE_POINT_INIT (rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_TOP_RIGHT].width, rect->bounds.origin.y);
pts[2] = GRAPHENE_POINT_INIT (rect->bounds.origin.x + rect->bounds.size.width, rect->bounds.origin.y);
pts[3] = GRAPHENE_POINT_INIT (rect->bounds.origin.x + rect->bounds.size.width, rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_RIGHT].height);
pts[4] = GRAPHENE_POINT_INIT (rect->bounds.origin.x + rect->bounds.size.width, rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_RIGHT].height);
pts[5] = GRAPHENE_POINT_INIT (rect->bounds.origin.x + rect->bounds.size.width, rect->bounds.origin.y + rect->bounds.size.height);
pts[6] = GRAPHENE_POINT_INIT (rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_BOTTOM_RIGHT].width, rect->bounds.origin.y + rect->bounds.size.height);
pts[7] = GRAPHENE_POINT_INIT (rect->bounds.origin.x + rect->corner[GSK_CORNER_BOTTOM_LEFT].width, rect->bounds.origin.y + rect->bounds.size.height);
pts[8] = GRAPHENE_POINT_INIT (rect->bounds.origin.x, rect->bounds.origin.y + rect->bounds.size.height);
pts[9] = GRAPHENE_POINT_INIT (rect->bounds.origin.x, rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_LEFT].height);
pts[10] = GRAPHENE_POINT_INIT (rect->bounds.origin.x, rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_LEFT].height);
pts[11] = GRAPHENE_POINT_INIT (rect->bounds.origin.x, rect->bounds.origin.y);
pts[12] = GRAPHENE_POINT_INIT (rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width, rect->bounds.origin.y);
}
static gboolean
gsk_rounded_rect_contour_foreach (const GskContour *contour,
float tolerance,
GskPathForeachFunc func,
gpointer user_data)
{
const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour;
graphene_point_t pts[13];
get_rounded_rect_points (&self->rect, pts);
if (self->ccw)
{
graphene_point_t p;
#define SWAP(a,b,c) a = b; b = c; c = a;
SWAP (p, pts[1], pts[11]);
SWAP (p, pts[2], pts[10]);
SWAP (p, pts[3], pts[9]);
SWAP (p, pts[4], pts[8]);
SWAP (p, pts[5], pts[7]);
#undef SWAP
return func (GSK_PATH_MOVE, &pts[0], 1, 0.f, user_data) &&
func (GSK_PATH_CONIC, &pts[0], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_LINE, &pts[2], 2, 0.f, user_data) &&
func (GSK_PATH_CONIC, &pts[3], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_LINE, &pts[5], 2, 0.f, user_data) &&
func (GSK_PATH_CONIC, &pts[6], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_LINE, &pts[8], 2, 0.f, user_data) &&
func (GSK_PATH_CONIC, &pts[9], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_LINE, &pts[11], 2, 0.f, user_data) &&
func (GSK_PATH_CLOSE, &pts[12], 2, 0.f, user_data);
}
else
{
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_CONIC, &pts[1], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_LINE, &pts[3], 2, 0.f, user_data) &&
func (GSK_PATH_CONIC, &pts[4], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_LINE, &pts[6], 2, 0.f, user_data) &&
func (GSK_PATH_CONIC, &pts[7], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_LINE, &pts[9], 2, 0.f, user_data) &&
func (GSK_PATH_CONIC, &pts[10], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_CLOSE, &pts[12], 2, 0.f, user_data);
}
}
static GskContour *
gsk_rounded_rect_contour_reverse (const GskContour *contour)
{
const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour;
GskRoundedRectContour *copy;
copy = g_new0 (GskRoundedRectContour, 1);
gsk_rounded_rect_contour_copy (contour, (GskContour *)copy);
copy->ccw = !self->ccw;
return (GskContour *)copy;
}
static int
gsk_rounded_rect_contour_get_winding (const GskContour *contour,
const graphene_point_t *point)
{
const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour;
if (gsk_rounded_rect_contains_point (&self->rect, point))
return self->ccw ? -1 : 1;
return 0;
}
static gsize
gsk_rounded_rect_contour_get_n_ops (const GskContour *contour)
{
return 9;
}
static gboolean
gsk_rounded_rect_contour_get_closest_point (const GskContour *contour,
const graphene_point_t *point,
float threshold,
GskRealPathPoint *result,
float *out_dist)
{
GskPath *path;
const GskContour *std;
gboolean ret;
path = convert_to_standard_contour (contour);
std = gsk_path_get_contour (path, 0);
ret = gsk_standard_contour_get_closest_point (std, point, threshold, result, out_dist);
gsk_path_unref (path);
return ret;
}
typedef struct
{
GskCurve *curve;
unsigned int idx;
unsigned int count;
} InitCurveData;
static gboolean
init_curve_cb (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
InitCurveData *data = user_data;
if (data->idx == data->count)
{
gsk_curve_init_foreach (data->curve, op, pts, n_pts, weight);
return FALSE;
}
data->count++;
return TRUE;
}
static void
gsk_rounded_rect_contour_init_curve (const GskContour *contour,
unsigned int idx,
GskCurve *curve)
{
InitCurveData data;
data.curve = curve;
data.idx = idx;
data.count = 0;
gsk_contour_foreach (contour, 0.5, init_curve_cb, &data);
}
static void
gsk_rounded_rect_contour_get_position (const GskContour *contour,
GskRealPathPoint *point,
graphene_point_t *position)
{
GskCurve curve;
gsk_rounded_rect_contour_init_curve (contour, point->idx, &curve);
gsk_curve_get_point (&curve, point->t, position);
}
static void
gsk_rounded_rect_contour_get_tangent (const GskContour *contour,
GskRealPathPoint *point,
GskPathDirection direction,
graphene_vec2_t *tangent)
{
GskCurve curve;
gsk_rounded_rect_contour_init_curve (contour, point->idx, &curve);
gsk_curve_get_tangent (&curve, point->t, tangent);
}
static float
gsk_rounded_rect_contour_get_curvature (const GskContour *contour,
GskRealPathPoint *point,
GskPathDirection direction,
graphene_point_t *center)
{
GskCurve curve;
gsk_rounded_rect_contour_init_curve (contour, point->idx, &curve);
return gsk_curve_get_curvature (&curve, point->t, center);
}
static void
gsk_rounded_rect_contour_add_segment (const GskContour *contour,
GskPathBuilder *builder,
gboolean emit_move_to,
GskRealPathPoint *start,
GskRealPathPoint *end)
{
GskPath *path;
const GskContour *std;
path = convert_to_standard_contour (contour);
std = gsk_path_get_contour (path, 0);
gsk_standard_contour_add_segment (std, builder, emit_move_to, start, end);
gsk_path_unref (path);
}
typedef struct
{
const GskContour *contour;
gpointer measure_data;
} RoundedRectMeasureData;
static gpointer
gsk_rounded_rect_contour_init_measure (const GskContour *contour,
float tolerance,
float *out_length)
{
RoundedRectMeasureData *data;
GskPath *path;
path = convert_to_standard_contour (contour);
data = g_new (RoundedRectMeasureData, 1);
data->contour = gsk_contour_dup (gsk_path_get_contour (path, 0));
data->measure_data = gsk_standard_contour_init_measure (data->contour, tolerance, out_length);
gsk_path_unref (path);
return data;
}
static void
gsk_rounded_rect_contour_free_measure (const GskContour *contour,
gpointer measure_data)
{
RoundedRectMeasureData *data = measure_data;
gsk_standard_contour_free_measure (data->contour, data->measure_data);
g_free (data);
}
static void
gsk_rounded_rect_contour_get_point (const GskContour *contour,
gpointer measure_data,
float distance,
GskRealPathPoint *result)
{
RoundedRectMeasureData *data = measure_data;
gsk_standard_contour_get_point (data->contour, data->measure_data, distance, result);
}
static float
gsk_rounded_rect_contour_get_distance (const GskContour *contour,
GskRealPathPoint *point,
gpointer measure_data)
{
RoundedRectMeasureData *data = measure_data;
return gsk_standard_contour_get_distance (data->contour, point, data->measure_data);
}
static const GskContourClass GSK_ROUNDED_RECT_CONTOUR_CLASS =
{
sizeof (GskRoundedRectContour),
"GskRoundedRectContour",
gsk_rounded_rect_contour_copy,
gsk_contour_get_size_default,
gsk_rounded_rect_contour_get_flags,
gsk_contour_print_default,
gsk_rounded_rect_contour_get_bounds,
gsk_rounded_rect_contour_get_stroke_bounds,
gsk_rounded_rect_contour_get_start_end,
gsk_rounded_rect_contour_foreach,
gsk_rounded_rect_contour_reverse,
gsk_rounded_rect_contour_get_winding,
gsk_rounded_rect_contour_get_n_ops,
gsk_rounded_rect_contour_get_closest_point,
gsk_rounded_rect_contour_get_position,
gsk_rounded_rect_contour_get_tangent,
gsk_rounded_rect_contour_get_curvature,
gsk_rounded_rect_contour_add_segment,
gsk_rounded_rect_contour_init_measure,
gsk_rounded_rect_contour_free_measure,
gsk_rounded_rect_contour_get_point,
gsk_rounded_rect_contour_get_distance,
};
GskContour *
gsk_rounded_rect_contour_new (const GskRoundedRect *rect)
{
GskRoundedRectContour *self;
self = g_new0 (GskRoundedRectContour, 1);
self->contour.klass = &GSK_ROUNDED_RECT_CONTOUR_CLASS;
self->rect = *rect;
return (GskContour *) self;
}
/* }}} */
/* {{{ API */

View File

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

View File

@@ -964,6 +964,33 @@ parse_string (const char **p,
return TRUE;
}
#define NEAR(x, y) (fabs ((x) - (y)) < 0.001)
static gboolean
is_rect (double x0, double y0,
double x1, double y1,
double x2, double y2,
double x3, double y3)
{
return NEAR (x0, x3) && NEAR (x1, x2) &&
NEAR (y0, y1) && NEAR (y2, y3) &&
x0 < x1 && y1 < y2;
}
static gboolean
is_line (double x0, double y0,
double x1, double y1,
double x2, double y2,
double x3, double y3)
{
if (NEAR (y0, y3))
return x0 <= x1 && x1 <= x2 && x2 <= x3 &&
NEAR (y0, y1) && NEAR (y0, y2) && NEAR (y0, y3);
else
return y0 <= y1 && y1 <= y2 && y2 <= y3 &&
NEAR (x0, x1) && NEAR (x0, x2) && NEAR (x0, x3);
}
static gboolean
parse_circle (const char **p,
double *cx,
@@ -998,16 +1025,12 @@ parse_circle (const char **p,
xx = (x0 + x4) / 2;
yy = (y2 + y6) / 2;
#define NEAR(x, y) (fabs ((x) - (y)) < 0.001)
if (NEAR (x0, x1) && NEAR (x0, x8) && NEAR (x0, x7) &&
NEAR (x2, x6) && NEAR (x3, x4) && NEAR (x4, x5) &&
NEAR (y5, y6) && NEAR (y6, y7) && NEAR (y4, y8) &&
NEAR (y8, y0) && NEAR (y3, y2) && NEAR (y2, y1) &&
NEAR (x2, xx) && NEAR (yy, y4) &&
if (NEAR (x0, x8) && NEAR (y0, y8) &&
is_rect (x5, y5, x7, y7, x1, y1, x3, y3) &&
is_rect (x5, y5, x6, y6, xx, yy, x4, y4) &&
is_rect (xx, yy, x0, y0, x1, y1, x2, y2) &&
NEAR (w0, M_SQRT1_2) && NEAR (w1, M_SQRT1_2) &&
NEAR (w2, M_SQRT1_2) && NEAR (w3, M_SQRT1_2) &&
x1 > x2 && x2 > x3 && y3 > y4 && y4 > y5)
NEAR (w2, M_SQRT1_2) && NEAR (w3, M_SQRT1_2))
{
*cx = xx;
*cy = yy;
@@ -1017,14 +1040,73 @@ parse_circle (const char **p,
return TRUE;
}
#undef NEAR
}
*p = o;
return FALSE;
}
static gboolean
parse_rounded_rect (const char **p,
GskRoundedRect *rr)
{
const char *o = *p;
double x0, y0, x1, y1, x2, y2, x3, y3;
double x4, y4, x5, y5, x6, y6, x7, y7;
double x8, y8, x9, y9, x10, y10, x11, y11;
double x12, y12, w0, w1, w2, w3;
if (parse_coordinate_pair (p, &x0, &y0) &&
parse_string (p, "L") &&
parse_coordinate_pair (p, &x1, &y1) &&
parse_string (p, "O") &&
parse_coordinate_pair (p, &x2, &y2) &&
parse_coordinate_pair (p, &x3, &y3) &&
parse_nonnegative_number (p, &w0) &&
parse_string (p, "L") &&
parse_coordinate_pair (p, &x4, &y4) &&
parse_string (p, "O") &&
parse_coordinate_pair (p, &x5, &y5) &&
parse_coordinate_pair (p, &x6, &y6) &&
parse_nonnegative_number (p, &w1) &&
parse_string (p, "L") &&
parse_coordinate_pair (p, &x7, &y7) &&
parse_string (p, "O") &&
parse_coordinate_pair (p, &x8, &y8) &&
parse_coordinate_pair (p, &x9, &y9) &&
parse_nonnegative_number (p, &w2) &&
parse_string (p, "L") &&
parse_coordinate_pair (p, &x10, &y10) &&
parse_string (p, "O") &&
parse_coordinate_pair (p, &x11, &y11) &&
parse_coordinate_pair (p, &x12, &y12) &&
parse_nonnegative_number (p, &w3) &&
parse_string (p, "Z"))
{
if (NEAR (x0, x12) && NEAR (y0, y12) &&
is_rect (x11, y11, x2, y2, x5, y5, x8, y8) &&
is_line (x11, y11, x0, y0, x1, y1, x2, y2) &&
is_line (x2, y2, x3, y3, x4, y4, x5, y5) &&
is_line (x8, y8, x7, y7, x6, y6, x5, y5) &&
is_line (x11, y11, x10, y10, x9, y9, x8, y8) &&
NEAR (w0, M_SQRT1_2) && NEAR (w1, M_SQRT1_2) &&
NEAR (w2, M_SQRT1_2) && NEAR (w3, M_SQRT1_2))
{
rr->bounds = GRAPHENE_RECT_INIT (x11, y11, x5 - x11, y5 - y11);
rr->corner[GSK_CORNER_TOP_LEFT] = GRAPHENE_SIZE_INIT (x12 - x11, y10 - y11);
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_LEFT] = GRAPHENE_SIZE_INIT (x7 - x8, y8 - y7);
return TRUE;
}
}
*p = o;
return FALSE;
}
#undef NEAR
/**
* gsk_path_parse:
* @string: a string
@@ -1110,17 +1192,34 @@ gsk_path_parse (const char *string)
case 'm':
{
double x1, y1, r;
GskRoundedRect rr;
/* Look for special contours */
if (parse_circle (&p, &x1, &y1, &r))
{
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (x1, y1), r);
if (_strchr ("zZX", prev_cmd))
{
path_x = x1 + r;
path_y = y1;
}
x = x1 + r;
y = y1;
if (_strchr ("zZX", prev_cmd))
{
path_x = x;
path_y = y;
}
}
else if (parse_rounded_rect (&p, &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))
{
path_x = x;
path_y = y;
}
}
else if (parse_coordinate_pair (&p, &x1, &y1))
{

View File

@@ -488,59 +488,10 @@ void
gsk_path_builder_add_rounded_rect (GskPathBuilder *self,
const GskRoundedRect *rect)
{
graphene_point_t current;
g_return_if_fail (self != NULL);
g_return_if_fail (rect != NULL);
current = self->current_point;
gsk_path_builder_move_to (self,
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->bounds.origin.y);
/* top */
gsk_path_builder_line_to (self,
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_TOP_RIGHT].width,
rect->bounds.origin.y);
/* topright corner */
gsk_path_builder_arc_to (self,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_RIGHT].height);
/* right */
gsk_path_builder_line_to (self,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_RIGHT].height);
/* bottomright corner */
gsk_path_builder_arc_to (self,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->bounds.size.height,
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_BOTTOM_RIGHT].width,
rect->bounds.origin.y + rect->bounds.size.height);
/* bottom */
gsk_path_builder_line_to (self,
rect->bounds.origin.x + rect->corner[GSK_CORNER_BOTTOM_LEFT].width,
rect->bounds.origin.y + rect->bounds.size.height);
/* bottomleft corner */
gsk_path_builder_arc_to (self,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->bounds.size.height,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_LEFT].height);
/* left */
gsk_path_builder_line_to (self,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_LEFT].height);
/* topleft corner */
gsk_path_builder_arc_to (self,
rect->bounds.origin.x,
rect->bounds.origin.y,
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->bounds.origin.y);
/* done */
gsk_path_builder_close (self);
self->current_point = current;
gsk_path_builder_add_contour (self, gsk_rounded_rect_contour_new (rect));
}
/**

View File

@@ -47,12 +47,46 @@ test_roundtrip_circle (void)
gsk_path_unref (path);
}
static void
test_roundtrip_rounded_rect (void)
{
GskRoundedRect rr;
GskPathBuilder *builder;
GskPath *path, *path2;
const GskContour *contour;
char *s;
rr.bounds = GRAPHENE_RECT_INIT (100, 100, 200, 150);
rr.corner[GSK_CORNER_TOP_LEFT] = GRAPHENE_SIZE_INIT (10, 10);
rr.corner[GSK_CORNER_TOP_RIGHT] = GRAPHENE_SIZE_INIT (20, 10);
rr.corner[GSK_CORNER_BOTTOM_RIGHT] = GRAPHENE_SIZE_INIT (20, 0);
rr.corner[GSK_CORNER_BOTTOM_LEFT] = GRAPHENE_SIZE_INIT (0, 0);
builder = gsk_path_builder_new ();
gsk_path_builder_add_rounded_rect (builder, &rr);
path = gsk_path_builder_free_to_path (builder);
contour = gsk_path_get_contour (path, 0);
g_assert_cmpstr (gsk_contour_get_type_name (contour), ==, "GskRoundedRectContour");
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), ==, "GskRoundedRectContour");
g_free (s);
gsk_path_unref (path2);
gsk_path_unref (path);
}
int
main (int argc, char *argv[])
{
gtk_test_init (&argc, &argv, NULL);
g_test_add_func ("/path/roundtrip/circle", test_roundtrip_circle);
g_test_add_func ("/path/roundtrip/rounded-rect", test_roundtrip_rounded_rect);
return g_test_run ();
}