Commit 00fafb27 authored by Henri Verbeet's avatar Henri Verbeet Committed by Alexandre Julliard

d2d1: Implement bezier/bezier intersections.

parent a4f23ea1
......@@ -503,6 +503,62 @@ static float d2d_point_ccw(const D2D1_POINT_2F *a, const D2D1_POINT_2F *b, const
return det_d[det_d_len - 1];
}
static BOOL d2d_rect_check_overlap(const D2D_RECT_F *p, const D2D_RECT_F *q)
{
return p->left < q->right && p->top < q->bottom && p->right > q->left && p->bottom > q->top;
}
static void d2d_rect_get_bezier_bounds(D2D_RECT_F *bounds, const D2D1_POINT_2F *p0,
const D2D1_POINT_2F *p1, const D2D1_POINT_2F *p2)
{
D2D1_POINT_2F p;
float root;
bounds->left = p0->x;
bounds->top = p0->y;
bounds->right = p0->x;
bounds->bottom = p0->y;
d2d_rect_expand(bounds, p2);
/* f(t) = (1 - t)²P₀ + 2(1 - t)tP₁ + t²P₂
* f'(t) = 2(1 - t)(P₁ - P₀) + 2t(P₂ - P₁)
* = 2(P₂ - 2P₁ + P₀)t + 2(P₁ - P₀)
*
* f'(t) = 0
* t = (P₀ - P₁) / (P₂ - 2P₁ + P₀) */
root = (p0->x - p1->x) / (p2->x - 2.0f * p1->x + p0->x);
if (root > 0.0f && root < 1.0f)
{
d2d_point_calculate_bezier(&p, p0, p1, p2, root);
d2d_rect_expand(bounds, &p);
}
root = (p0->y - p1->y) / (p2->y - 2.0f * p1->y + p0->y);
if (root > 0.0f && root < 1.0f)
{
d2d_point_calculate_bezier(&p, p0, p1, p2, root);
d2d_rect_expand(bounds, &p);
}
}
static void d2d_rect_get_bezier_segment_bounds(D2D_RECT_F *bounds, const D2D1_POINT_2F *p0,
const D2D1_POINT_2F *p1, const D2D1_POINT_2F *p2, float start, float end)
{
D2D1_POINT_2F q[3], r[2];
d2d_point_lerp(&r[0], p0, p1, start);
d2d_point_lerp(&r[1], p1, p2, start);
d2d_point_lerp(&q[0], &r[0], &r[1], start);
end = (end - start) / (1.0f - start);
d2d_point_lerp(&q[1], &q[0], &r[1], end);
d2d_point_lerp(&r[0], &r[1], p2, end);
d2d_point_lerp(&q[2], &q[1], &r[0], end);
d2d_rect_get_bezier_bounds(bounds, &q[0], &q[1], &q[2]);
}
static BOOL d2d_array_reserve(void **elements, size_t *capacity, size_t element_count, size_t element_size)
{
size_t new_capacity, max_capacity;
......@@ -1766,6 +1822,71 @@ static BOOL d2d_geometry_intersect_bezier_line(struct d2d_geometry *geometry,
return TRUE;
}
static BOOL d2d_geometry_intersect_bezier_bezier(struct d2d_geometry *geometry,
struct d2d_geometry_intersections *intersections,
const struct d2d_segment_idx *idx_p, float start_p, float end_p,
const struct d2d_segment_idx *idx_q, float start_q, float end_q)
{
const D2D1_POINT_2F *p[3], *q[3];
const struct d2d_figure *figure;
D2D_RECT_F p_bounds, q_bounds;
D2D1_POINT_2F intersection;
float centre_p, centre_q;
size_t next;
figure = &geometry->u.path.figures[idx_p->figure_idx];
p[0] = &figure->vertices[idx_p->vertex_idx];
p[1] = &figure->bezier_controls[idx_p->control_idx];
next = idx_p->vertex_idx + 1;
if (next == figure->vertex_count)
next = 0;
p[2] = &figure->vertices[next];
figure = &geometry->u.path.figures[idx_q->figure_idx];
q[0] = &figure->vertices[idx_q->vertex_idx];
q[1] = &figure->bezier_controls[idx_q->control_idx];
next = idx_q->vertex_idx + 1;
if (next == figure->vertex_count)
next = 0;
q[2] = &figure->vertices[next];
d2d_rect_get_bezier_segment_bounds(&p_bounds, p[0], p[1], p[2], start_p, end_p);
d2d_rect_get_bezier_segment_bounds(&q_bounds, q[0], q[1], q[2], start_q, end_q);
if (!d2d_rect_check_overlap(&p_bounds, &q_bounds))
return TRUE;
centre_p = (start_p + end_p) / 2.0f;
centre_q = (start_q + end_q) / 2.0f;
if (end_p - start_p < 1e-3f)
{
d2d_point_calculate_bezier(&intersection, p[0], p[1], p[2], centre_p);
if (start_p > 0.0f && end_p < 1.0f && !d2d_geometry_intersections_add(intersections,
idx_p, centre_p, intersection))
return FALSE;
if (start_q > 0.0f && end_q < 1.0f && !d2d_geometry_intersections_add(intersections,
idx_q, centre_q, intersection))
return FALSE;
return TRUE;
}
if (!d2d_geometry_intersect_bezier_bezier(geometry, intersections,
idx_p, start_p, centre_p, idx_q, start_q, centre_q))
return FALSE;
if (!d2d_geometry_intersect_bezier_bezier(geometry, intersections,
idx_p, start_p, centre_p, idx_q, centre_q, end_q))
return FALSE;
if (!d2d_geometry_intersect_bezier_bezier(geometry, intersections,
idx_p, centre_p, end_p, idx_q, start_q, centre_q))
return FALSE;
if (!d2d_geometry_intersect_bezier_bezier(geometry, intersections,
idx_p, centre_p, end_p, idx_q, centre_q, end_q))
return FALSE;
return TRUE;
}
static BOOL d2d_geometry_apply_intersections(struct d2d_geometry *geometry,
struct d2d_geometry_intersections *intersections)
{
......@@ -1834,7 +1955,6 @@ static BOOL d2d_geometry_apply_intersections(struct d2d_geometry *geometry,
/* Intersect the geometry's segments with themselves. This uses the
* straightforward approach of testing everything against everything, but
* there certainly exist more scalable algorithms for this. */
/* FIXME: Beziers can't currently self-intersect. */
static BOOL d2d_geometry_intersect_self(struct d2d_geometry *geometry)
{
struct d2d_geometry_intersections intersections = {0};
......@@ -1859,10 +1979,7 @@ static BOOL d2d_geometry_intersect_self(struct d2d_geometry *geometry)
figure_q = &geometry->u.path.figures[idx_q.figure_idx];
if (idx_q.figure_idx != idx_p.figure_idx)
{
if (figure_p->bounds.left > figure_q->bounds.right
|| figure_q->bounds.left > figure_p->bounds.right
|| figure_p->bounds.top > figure_q->bounds.bottom
|| figure_q->bounds.top > figure_p->bounds.bottom)
if (!d2d_rect_check_overlap(&figure_p->bounds, &figure_q->bounds))
continue;
max_q = figure_q->vertex_count;
}
......@@ -1877,9 +1994,17 @@ static BOOL d2d_geometry_intersect_self(struct d2d_geometry *geometry)
type_q = figure_q->vertex_types[idx_q.vertex_idx];
if (type_q == D2D_VERTEX_TYPE_BEZIER)
{
if (type_p != D2D_VERTEX_TYPE_BEZIER
&& !d2d_geometry_intersect_bezier_line(geometry, &intersections, &idx_q, &idx_p))
goto done;
if (type_p == D2D_VERTEX_TYPE_BEZIER)
{
if (!d2d_geometry_intersect_bezier_bezier(geometry, &intersections,
&idx_p, 0.0f, 1.0f, &idx_q, 0.0f, 1.0f))
goto done;
}
else
{
if (!d2d_geometry_intersect_bezier_line(geometry, &intersections, &idx_q, &idx_p))
goto done;
}
++idx_q.control_idx;
}
else
......
......@@ -2364,7 +2364,7 @@ static void test_path_geometry(void)
"EyYTVBQjFFYUIhRWFSAVVxUeFVgWHBZZFhoWWhcYF1sWGBZcFxYWXhcUF18WFBZhFhIWYxUSFWUV"
"EBVnFBAUaRQOFGsTDhJvEgwSchAMEHYPCg96DQoMggEICgiLAQQIBJQBCJgBCJkBBpoBBpoBBpoB"
"BpsBBJwBBJwBBJwBBJwBBJ0BAp4BAp4BAp4BAp4BAp4BAp4BAp4BAgAA");
todo_wine ok(match, "Figure does not match.\n");
ok(match, "Figure does not match.\n");
match = compare_figure(surface, 0, 226, 160, 160, 0xff652e89, 64,
"7xoCngECngECngECngECngECngECngECnQEEnAEEnAEEnAEEnAEEmwEGmgEGmgEGmgEGmQEImAEI"
"lAEECASLAQgKCIEBDQoMew8KD3YQDBByEgwSbhMOEmwUDhRpFBAUZxUQFWUVEhVjFhIWYRYUFl8X"
......@@ -2374,7 +2374,7 @@ static void test_path_geometry(void)
"EyYTVBQjFFYUIhRWFSAVVxUeFVgWHBZZFhoWWhcYF1sWGBZcFxYWXhcUF18WFBZhFhIWYxUSFWUV"
"EBVnFBAUaRQOFGsTDhJvEgwSchAMEHYPCg96DQoMggEICgiLAQQIBJQBCJgBCJkBBpoBBpoBBpoB"
"BpsBBJwBBJwBBJwBBJwBBJ0BAp4BAp4BAp4BAp4BAp4BAp4BAp4BAgAA");
todo_wine ok(match, "Figure does not match.\n");
ok(match, "Figure does not match.\n");
match = compare_figure(surface, 160, 0, 320, 160, 0xff652e89, 64,
"gVQBwAIBWgHlAQFYAecBAVYB6QEBVAHrAQEjDCMB7AECHhQeAu0BAxoYGgPvAQMWHhYD8QEDFCAU"
"A/MBBBAkEAT0AQUOJw0F9QEGCioKBvcBBggsCAb4AQgFLgUI+QEJATIBCfsBCAIwAgj8AQcFLAUH"
......@@ -2386,7 +2386,7 @@ static void test_path_geometry(void)
"AgMNIA0D/wEFCSYJBf4BBgYqBgf8AQgDLgMI+wFG+gEIAzADCPkBBwYuBgf3AQYKKgoG9gEFDCgM"
"BfUBBBAlDwTzAQQSIhIE8QEDFh4WA/ABAhkaGQLvAQIcFhwC7QECIBAgAusBASgEKAHpAQFWAecB"
"AVgB5QEBWgHAAgHhUgAA");
todo_wine ok(match, "Figure does not match.\n");
ok(match, "Figure does not match.\n");
match = compare_figure(surface, 160, 160, 320, 160, 0xff652e89, 64,
"/VUB5QEBWAHnAQFWAekBAVQB6wECIQ8hAe0BAh0VHQLuAQIZGhkD7wEDFh4WA/EBBBMhEwPzAQQQ"
"JQ8F9AEFDCgNBfUBBgoqCgb3AQcHLQcG+QEIBC8ECPkBPAEJ+wEIAy8CCP0BBgYrBQf9AQUJJgkF"
......@@ -2398,7 +2398,7 @@ static void test_path_geometry(void)
"IA0E/gEFCSYJBf4BBgYrBQf8AQgDLwII+wE8AQn6AQgELwQI+AEHBy0HBvcBBgoqCgb2AQUNJw0F"
"9AEEECQQBfIBBBMhEwPxAQMWHhYD8AECGRoZA+4BAh0VHQLsAQIhDiIB6wEBVAHpAQFWAecBAVgB"
"wAIBwlYA");
todo_wine ok(match, "Figure does not match.\n");
ok(match, "Figure does not match.\n");
ID2D1TransformedGeometry_Release(transformed_geometry);
ID2D1PathGeometry_Release(geometry);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment