mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-12 13:30:19 +00:00
contour: Fix some corner cases
Adding segments of rects or rounded rects was not working right in cases where some of the curves are trivial. Fix that.
This commit is contained in:
parent
061637f4eb
commit
de724b2a57
507
gsk/gskcontour.c
507
gsk/gskcontour.c
@ -278,6 +278,106 @@ contour_get_closest_point (const GskContour *contour,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
add_curve (GskCurve *curve,
|
||||
GskPathBuilder *builder,
|
||||
gboolean *emit_move_to)
|
||||
{
|
||||
if (*emit_move_to)
|
||||
{
|
||||
const graphene_point_t *s;
|
||||
|
||||
s = gsk_curve_get_start_point (curve);
|
||||
gsk_path_builder_move_to (builder, s->x, s->y);
|
||||
*emit_move_to = FALSE;
|
||||
}
|
||||
gsk_curve_builder_to (curve, builder);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
gboolean emit_move_to;
|
||||
GskPathPoint start;
|
||||
GskPathPoint end;
|
||||
gsize idx;
|
||||
} AddSegmentData;
|
||||
|
||||
static gboolean
|
||||
add_segment_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
AddSegmentData *sd = data;
|
||||
GskCurve c, c1, c2;
|
||||
|
||||
if (op == GSK_PATH_MOVE)
|
||||
return TRUE;
|
||||
|
||||
sd->idx++;
|
||||
|
||||
if (sd->start.idx > sd->idx)
|
||||
return TRUE;
|
||||
|
||||
if (sd->end.idx < sd->idx)
|
||||
return FALSE;
|
||||
|
||||
if (op == GSK_PATH_CLOSE)
|
||||
op = GSK_PATH_LINE;
|
||||
|
||||
gsk_curve_init_foreach (&c, op, pts, n_pts, weight);
|
||||
|
||||
if (sd->start.idx == sd->idx)
|
||||
{
|
||||
if (sd->end.idx == sd->idx)
|
||||
{
|
||||
gsk_curve_segment (&c, sd->start.t, sd->end.t, &c1);
|
||||
add_curve (&c1, sd->builder, &sd->emit_move_to);
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_curve_split (&c, sd->start.t, &c1, &c2);
|
||||
add_curve (&c2, sd->builder, &sd->emit_move_to);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sd->end.idx == sd->idx)
|
||||
{
|
||||
gsk_curve_split (&c, sd->end.t, &c1, &c2);
|
||||
add_curve (&c1, sd->builder, &sd->emit_move_to);
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
add_curve (&c, sd->builder, &sd->emit_move_to);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
contour_add_segment (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
gboolean emit_move_to,
|
||||
const GskPathPoint *start,
|
||||
const GskPathPoint *end)
|
||||
{
|
||||
AddSegmentData sd;
|
||||
|
||||
sd.builder = builder;
|
||||
sd.emit_move_to = emit_move_to;
|
||||
sd.start = *start;
|
||||
sd.end = *end;
|
||||
sd.idx = 0;
|
||||
|
||||
gsk_contour_foreach (contour, add_segment_cb, &sd);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Default implementations */
|
||||
|
||||
@ -683,7 +783,6 @@ gsk_standard_contour_get_tangent (const GskContour *contour,
|
||||
|
||||
gsk_curve_init (&curve, self->ops[idx]);
|
||||
gsk_curve_get_tangent (&curve, t, tangent);
|
||||
|
||||
if (direction == GSK_PATH_TO_START || direction == GSK_PATH_FROM_END)
|
||||
graphene_vec2_negate (tangent, tangent);
|
||||
}
|
||||
@ -722,22 +821,6 @@ gsk_standard_contour_get_curvature (const GskContour *contour,
|
||||
return gsk_curve_get_curvature (&curve, t, center);
|
||||
}
|
||||
|
||||
static void
|
||||
add_curve (GskCurve *curve,
|
||||
GskPathBuilder *builder,
|
||||
gboolean *emit_move_to)
|
||||
{
|
||||
if (*emit_move_to)
|
||||
{
|
||||
const graphene_point_t *s;
|
||||
|
||||
s = gsk_curve_get_start_point (curve);
|
||||
gsk_path_builder_move_to (builder, s->x, s->y);
|
||||
*emit_move_to = FALSE;
|
||||
}
|
||||
gsk_curve_builder_to (curve, builder);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
@ -749,6 +832,7 @@ gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
GskCurve c, c1, c2;
|
||||
|
||||
g_assert (start->idx < self->n_ops);
|
||||
g_assert (end->idx < self->n_ops);
|
||||
|
||||
gsk_curve_init (&c, self->ops[start->idx]);
|
||||
|
||||
@ -1567,7 +1651,6 @@ struct _GskRectContour
|
||||
float y;
|
||||
float width;
|
||||
float height;
|
||||
float length;
|
||||
};
|
||||
|
||||
static void
|
||||
@ -1682,84 +1765,7 @@ gsk_rect_contour_get_winding (const GskContour *contour,
|
||||
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;
|
||||
return 5;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -1769,149 +1775,7 @@ gsk_rect_contour_get_closest_point (const GskContour *contour,
|
||||
GskPathPoint *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;
|
||||
if (self->length == 0)
|
||||
result->t = 0;
|
||||
else
|
||||
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);
|
||||
return contour_get_closest_point (contour, point, threshold, result, out_dist);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1919,9 +1783,10 @@ gsk_rect_contour_get_position (const GskContour *contour,
|
||||
const GskPathPoint *point,
|
||||
graphene_point_t *position)
|
||||
{
|
||||
const GskRectContour *self = (const GskRectContour *) contour;
|
||||
GskCurve curve;
|
||||
|
||||
gsk_rect_contour_pos_tangent (self, point->t * self->length, GSK_PATH_TO_END, position, NULL);
|
||||
contour_init_curve (contour, point->idx, &curve);
|
||||
gsk_curve_get_point (&curve, point->t, position);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1930,9 +1795,33 @@ gsk_rect_contour_get_tangent (const GskContour *contour,
|
||||
GskPathDirection direction,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskRectContour *self = (const GskRectContour *) contour;
|
||||
gsize idx = point->idx;
|
||||
float t = point->t;
|
||||
GskCurve curve;
|
||||
|
||||
gsk_rect_contour_pos_tangent (self, point->t * self->length, direction, NULL, tangent);
|
||||
if (t == 0 &&
|
||||
(direction == GSK_PATH_FROM_START || direction == GSK_PATH_TO_START))
|
||||
{
|
||||
if (idx == 1)
|
||||
idx = 4;
|
||||
else
|
||||
idx--;
|
||||
t = 1;
|
||||
}
|
||||
else if (t == 1 &&
|
||||
(direction == GSK_PATH_FROM_END || direction == GSK_PATH_TO_END))
|
||||
{
|
||||
if (idx == 4)
|
||||
idx = 1;
|
||||
else
|
||||
idx++;
|
||||
t = 0;
|
||||
}
|
||||
|
||||
contour_init_curve (contour, idx, &curve);
|
||||
gsk_curve_get_tangent (&curve, point->t, tangent);
|
||||
if (direction == GSK_PATH_TO_START || direction == GSK_PATH_FROM_END)
|
||||
graphene_vec2_negate (tangent, tangent);
|
||||
}
|
||||
|
||||
static float
|
||||
@ -1948,68 +1837,10 @@ static void
|
||||
gsk_rect_contour_add_segment (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
gboolean emit_move_to,
|
||||
const GskPathPoint *start_point,
|
||||
const GskPathPoint *end_point)
|
||||
const GskPathPoint *start,
|
||||
const GskPathPoint *end)
|
||||
{
|
||||
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);
|
||||
}
|
||||
contour_add_segment (contour, builder, emit_move_to, start, end);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
@ -2019,7 +1850,7 @@ gsk_rect_contour_init_measure (const GskContour *contour,
|
||||
{
|
||||
const GskRectContour *self = (const GskRectContour *) contour;
|
||||
|
||||
*out_length = self->length;
|
||||
*out_length = 2 * (fabsf (self->width) + fabsf (self->height));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -2038,12 +1869,35 @@ gsk_rect_contour_get_point (const GskContour *contour,
|
||||
{
|
||||
const GskRectContour *self = (const GskRectContour *) contour;
|
||||
|
||||
result->idx = 1;
|
||||
if (self->length == 0)
|
||||
result->t = 0;
|
||||
else
|
||||
result->t = CLAMP (distance / self->length, 0, 1);
|
||||
g_assert (0 <= result->t && result->t <= 1);
|
||||
if (distance <= fabsf (self->width))
|
||||
{
|
||||
result->idx = 1;
|
||||
result->t = distance / fabsf (self->width);
|
||||
return;
|
||||
}
|
||||
|
||||
distance -= fabsf (self->width);
|
||||
|
||||
if (distance <= fabs (self->height))
|
||||
{
|
||||
result->idx = 2;
|
||||
result->t = distance / fabsf (self->height);
|
||||
return;
|
||||
}
|
||||
|
||||
distance -= fabs (self->height);
|
||||
|
||||
if (distance <= fabsf (self->width))
|
||||
{
|
||||
result->idx = 3;
|
||||
result->t = distance / fabsf (self->width);
|
||||
return;
|
||||
}
|
||||
|
||||
distance -= fabsf (self->width);
|
||||
|
||||
result->idx = 4;
|
||||
result->t = CLAMP (distance / fabsf (self->height), 0, 1);
|
||||
}
|
||||
|
||||
static float
|
||||
@ -2053,7 +1907,23 @@ gsk_rect_contour_get_distance (const GskContour *contour,
|
||||
{
|
||||
const GskRectContour *self = (const GskRectContour *) contour;
|
||||
|
||||
return point->t * self->length;
|
||||
switch (point->idx)
|
||||
{
|
||||
case 1:
|
||||
return point->t * fabsf (self->width);
|
||||
|
||||
case 2:
|
||||
return fabsf (self->width) + point->t * fabsf (self->height);
|
||||
|
||||
case 3:
|
||||
return (1 + point->t) * fabsf (self->width) + fabsf (self->height);
|
||||
|
||||
case 4:
|
||||
return 2 * fabsf (self->width) + (1 + point->t) * fabsf (self->height);
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static const GskContourClass GSK_RECT_CONTOUR_CLASS =
|
||||
@ -2094,7 +1964,6 @@ gsk_rect_contour_new (const graphene_rect_t *rect)
|
||||
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;
|
||||
}
|
||||
@ -2284,6 +2153,8 @@ gsk_rounded_rect_contour_get_tangent (const GskContour *contour,
|
||||
|
||||
contour_init_curve (contour, point->idx, &curve);
|
||||
gsk_curve_get_tangent (&curve, point->t, tangent);
|
||||
if (direction == GSK_PATH_TO_START || direction == GSK_PATH_FROM_END)
|
||||
graphene_vec2_negate (tangent, tangent);
|
||||
}
|
||||
|
||||
static float
|
||||
@ -2305,15 +2176,7 @@ gsk_rounded_rect_contour_add_segment (const GskContour *contour,
|
||||
const GskPathPoint *start,
|
||||
const GskPathPoint *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);
|
||||
contour_add_segment (contour, builder, emit_move_to, start, end);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
|
Loading…
Reference in New Issue
Block a user