Add functions for getting cubic bezier texture coordinates.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- dlls/d2d1/geometry.c | 331 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+)
diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c index c740f232cd..28c62c58f0 100644 --- a/dlls/d2d1/geometry.c +++ b/dlls/d2d1/geometry.c @@ -2907,6 +2907,26 @@ static BOOL d2d_geometry_get_next_bezier_segment_idx(struct d2d_geometry *geomet return d2d_geometry_get_bezier_segment_idx(geometry, idx, TRUE); }
+enum bezier_fill_side +{ + FILL_LEFT, + FILL_RIGHT, +}; + +enum bezier_type +{ + BEZIER_TYPE_SERP, + BEZIER_TYPE_LOOP, + BEZIER_TYPE_CUSP, + BEZIER_TYPE_QUAD, + BEZIER_TYPE_LINE, +}; + +struct d2d_vec3 +{ + float x, y, z; +}; + struct d2d_cubic_triangles { unsigned int triangles[3][3]; @@ -2914,6 +2934,11 @@ struct d2d_cubic_triangles
D2D1_POINT_2F p[4]; unsigned int inside; + + struct d2d_vec3 klm[4]; + unsigned int type; + unsigned int fill_side; + unsigned int reverse; };
enum figure_orientation @@ -3487,6 +3512,304 @@ static BOOL d2d_geometry_split_bezier(struct d2d_geometry *geometry, const struc return TRUE; }
+static void d2d_vec3_cross_product(struct d2d_vec3 *res, struct d2d_vec3 *a, struct d2d_vec3 *b) +{ + res->x = (a->y * b->z) - (a->z * b->y); + res->y = -(a->x * b->z - a->z * b->x); + res->z = a->x * b->y - a->y * b->x; +} + +static float d2d_vec3_dot_product(struct d2d_vec3 *a, struct d2d_vec3 *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->z; +} + +static void d2d_vec3_scale(struct d2d_vec3 *vec, float scale) +{ + vec->x = vec->x * scale; + vec->y = vec->y * scale; + vec->z = vec->z * scale; +} + +static void d2d_vec3_normalize(struct d2d_vec3 *vec) +{ + float l; + + if ((l = sqrt(d2d_vec3_dot_product(vec, vec))) != 0.0f) + d2d_vec3_scale(vec, 1.0f / l); +} + +static void d2d_vec3_round_to_zero(struct d2d_vec3 *vec) +{ + vec->x = d2d_cubic_bezier_round_to_zero(vec->x); + vec->y = d2d_cubic_bezier_round_to_zero(vec->y); + vec->z = d2d_cubic_bezier_round_to_zero(vec->z); +} + +static void d2d_point_to_vec3(struct d2d_vec3 *vec, const D2D1_POINT_2F *p) +{ + vec->x = p->x; + vec->y = p->y; + vec->z = 1.0f; +} + +static void d2d_geometry_reverse_bezier_coords(struct d2d_cubic_triangles *tri) +{ + unsigned int i; + + for (i = 0; i < 4; i++) + { + tri->klm[i].x = -tri->klm[i].x; + tri->klm[i].y = -tri->klm[i].y; + } +} + +static void d2d_geometry_get_bezier_serp_coords(struct d2d_vec3 *d, struct d2d_cubic_triangles *tri) +{ + float l_s, l_t, m_s, m_t; + + l_s = 3.0f * d->y - sqrtf(9.0f * d->y * d->y - 12.0f * d->x * d->z); + l_t = 6.0f * d->x; + + m_s = 3.0f * d->y + sqrtf(9.0f * d->y * d->y - 12.0f * d->x * d->z); + m_t = 6.0f * d->x; + + tri->klm[0].x = l_s * m_s; + tri->klm[0].y = l_s * l_s * l_s; + tri->klm[0].z = m_s * m_s * m_s; + + tri->klm[1].x = (1.0f / 3.0f) * (3.0f * l_s * m_s - l_s * m_t - l_t * m_s); + tri->klm[1].y = l_s * l_s * (l_s - l_t); + tri->klm[1].z = m_s * m_s * (m_s - m_t); + + tri->klm[2].x = (1.0f / 3.0f) * (l_t * (m_t - 2.0f * m_s) + l_s * (3.0f * m_s - 2.0f * m_t)); + tri->klm[2].y = (l_t - l_s) * (l_t - l_s) * l_s; + tri->klm[2].z = (m_t - m_s) * (m_t - m_s) * m_s; + + tri->klm[3].x = (l_t - l_s) * (m_t - m_s); + tri->klm[3].y = -((l_t - l_s) * (l_t - l_s) * (l_t - l_s)); + tri->klm[3].z = -((m_t - m_s) * (m_t - m_s) * (m_t - m_s)); + + if (d->x < 0.0f) + tri->reverse = 1; + else + tri->reverse = 0; +} + +static BOOL d2d_geometry_get_bezier_loop_coords(struct d2d_vec3 *d, struct d2d_cubic_triangles *tri) +{ + float l_s, l_t, m_s, m_t, split_check; + + l_s = d->y - sqrtf(4.0f * d->x * d->z - 3.0f * d->y * d->y); + l_t = 2.0f * d->x; + split_check = l_s / l_t; + if (split_check > 0.0f && split_check < 1.0f) + return FALSE; + + m_s = d->y + sqrtf(4 * d->x * d->z - 3.0f * d->y * d->y); + m_t = 2.0f * d->x; + split_check = m_s / m_t; + if (split_check > 0.0f && split_check < 1.0f) + return FALSE; + + tri->klm[0].x = l_s * m_s; + tri->klm[0].y = l_s * l_s * m_s; + tri->klm[0].z = l_s * m_s * m_s; + + tri->klm[1].x = (1.0f / 3.0f) * (-l_s * m_t - l_t * m_s + 3.0f * l_s * m_s); + tri->klm[1].y = -(1.0f / 3.0f) * l_s * (l_s * (m_t - 3.0f * m_s) + 2.0f * l_t * m_s); + tri->klm[1].z = -(1.0f / 3.0f) * m_s * (l_s * (2.0f * m_t - 3.0f * m_s) + l_t * m_s); + + tri->klm[2].x = (1.0f / 3.0f) * (l_t * (m_t - 2.0f * m_s) + l_s * (3.0f * m_s - 2.0f * m_t)); + tri->klm[2].y = (1.0f / 3.0f) * (l_t - l_s) * (l_s * (2.0f * m_t - 3.0f * m_s) + l_t * m_s); + tri->klm[2].z = (1.0f / 3.0f) * (m_t - m_s) * (l_s * (m_t - 3.0f * m_s) + 2.0f * l_t * m_s); + + tri->klm[3].x = (l_t - l_s) * (m_t - m_s); + tri->klm[3].y = -((l_t - l_s) * (l_t - l_s)) * (m_t - m_s); + tri->klm[3].z = -(l_t - l_s) * ((m_t - m_s) * (m_t - m_s)); + + if ((d->x > 0.0f && tri->klm[0].x < 0.0f) || (d->x < 0.0f && tri->klm[0].x > 0.0f)) + tri->reverse = 1; + else + tri->reverse = 0; + + return TRUE; +} + +static void d2d_geometry_get_bezier_cusp_coords(struct d2d_vec3 *d, struct d2d_cubic_triangles *tri) +{ + float l_s, l_t; + + l_s = d->z; + l_t = 3.0f * d->y; + + tri->klm[0].x = l_s; + tri->klm[0].y = l_s * l_s * l_s; + tri->klm[0].z = 1.0f; + + tri->klm[1].x = l_s - (1.0f / 3.0f) * l_t; + tri->klm[1].y = (l_s - l_t) * (l_s - l_t) * l_s; + tri->klm[1].z = 1.0f; + + tri->klm[2].x = l_s - (2.0f / 3.0f) * l_t; + tri->klm[2].y = (l_s - l_t) * (l_s - l_t) * l_s; + tri->klm[2].z = 1.0f; + + tri->klm[3].x = l_s - l_t; + tri->klm[3].y = (l_s - l_t) * (l_s - l_t) * (l_s - l_t); + tri->klm[3].z = 1.0f; + + tri->reverse = 0; +} + +static void d2d_geometry_get_bezier_quad_coords(struct d2d_vec3 *d, struct d2d_cubic_triangles *tri) +{ + tri->klm[0].x = 0.0f; + tri->klm[0].y = 0.0f; + tri->klm[0].z = 0.0f; + + tri->klm[1].x = 1.0f / 3.0f; + tri->klm[1].y = 0.0f; + tri->klm[1].z = 1.0f / 3.0f; + + tri->klm[2].x = 2.0f / 3.0f; + tri->klm[2].y = 1.0f / 3.0f; + tri->klm[2].z = 2.0f / 3.0f; + + tri->klm[3].x = 1.0f; + tri->klm[3].y = 1.0f; + tri->klm[3].z = 1.0f; + + if (d->z < 0.0f) + tri->reverse = 1; + else + tri->reverse = 0; +} + +/* From GPU Gems 3, Chapter 25: Rendering Vector Art on the GPU. */ +static void d2d_geometry_classify_bezier(struct d2d_geometry *geometry, struct d2d_cubic_triangulation *triangles, + const struct d2d_segment_idx *idx) +{ + struct d2d_figure *figure; + struct d2d_cubic_triangles *tri; + struct d2d_vec3 p_v[4], tmp, d; + const D2D1_POINT_2F *p[4]; + float a[3], discriminant, cusp_check; + unsigned int i; + + tri = &triangles[idx->figure_idx].cubic_tri[idx->control_idx]; + figure = &geometry->u.path.figures[idx->figure_idx]; + p[0] = &figure->vertices[idx->vertex_idx]; + p[1] = &figure->bezier_controls[idx->control_idx].c0; + p[2] = &figure->bezier_controls[idx->control_idx].c1; + if (idx->vertex_idx == figure->vertex_count - 1) + p[3] = &figure->vertices[0]; + else + p[3] = &figure->vertices[idx->vertex_idx + 1]; + + /* Before doing any math, lets make sure that what we have isn't just a + * point or a line. If it is, it will be ignored. */ + if (d2d_point_approximately_equal(p[0], p[1]) && d2d_point_approximately_equal(p[0], p[2]) + && d2d_point_approximately_equal(p[0], p[3])) + { + tri->type = BEZIER_TYPE_LINE; + return; + } + + for (i = 0; i < 4; i++) + d2d_point_to_vec3(&p_v[i], p[i]); + + d2d_vec3_cross_product(&tmp, &p_v[3], &p_v[2]); + a[0] = d2d_vec3_dot_product(&p_v[0], &tmp); + + d2d_vec3_cross_product(&tmp, &p_v[0], &p_v[3]); + a[1] = d2d_vec3_dot_product(&p_v[1], &tmp); + + d2d_vec3_cross_product(&tmp, &p_v[1], &p_v[0]); + a[2] = d2d_vec3_dot_product(&p_v[2], &tmp); + + d.x = a[0] - 2.0f * a[1] + 3.0f * a[2]; + d.y = -a[1] + 3.0f * a[2]; + d.z = 3.0f * a[2]; + + /* Not mentioned in the GPU Gems article, but the results of these + * computations need to be normalized, otherwise floating point overflows + * start to occur. */ + d2d_vec3_normalize(&d); + + /* Mentioned in 'Resolution Independent Curve Rendering using Programmable + * Graphics Hardware', the value cusp_check is used when getting the k, l, + * and m texture coordinates for the cusp case. If the cusp has an + * inflection point at infinity, cusp_check should be 0. */ + cusp_check = 3.0f * d.y * d.y - 4.0f * d.x * d.z; + discriminant = d.x * d.x * cusp_check; + + /* Need to round these values to 0, because there's issues with + * classification otherwise. */ + d2d_vec3_round_to_zero(&d); + discriminant = d2d_cubic_bezier_round_to_zero(discriminant); + + if (discriminant > 0.0f) + { + tri->type = BEZIER_TYPE_SERP; + d2d_geometry_get_bezier_serp_coords(&d, tri); + } + else if (discriminant < 0.0f) + { + tri->type = BEZIER_TYPE_LOOP; + /* Check to see if it's the type of loop that has an overlap within + * the t = 0 through t = 1 range. If it is, we'll need to split it + * into two separate beziers to avoid rendering artifacts. */ + if (!d2d_geometry_get_bezier_loop_coords(&d, tri)) + { + d2d_geometry_split_bezier(geometry, idx, triangles); + d2d_geometry_classify_bezier(geometry, triangles, idx); + return; + } + } + else + { + if (d.x == 0.0f && d.y == 0.0f) + { + if (d.z == 0.0f) + { + tri->type = BEZIER_TYPE_LINE; + return; + } + else + { + tri->type = BEZIER_TYPE_QUAD; + d2d_geometry_get_bezier_quad_coords(&d, tri); + } + } + else + { + if (d.x == 0.0f) + { + tri->type = BEZIER_TYPE_CUSP; + d2d_geometry_get_bezier_cusp_coords(&d, tri); + } + else if (cusp_check < 0.0f) + { + tri->type = BEZIER_TYPE_LOOP; + d2d_geometry_get_bezier_loop_coords(&d, tri); + } + else + { + tri->type = BEZIER_TYPE_SERP; + d2d_geometry_get_bezier_serp_coords(&d, tri); + } + } + } + + tri->fill_side = triangles->orientation ^ tri->inside ? FILL_LEFT : FILL_RIGHT; + + if (tri->fill_side == FILL_RIGHT) + tri->reverse = !tri->reverse; + if (tri->reverse) + d2d_geometry_reverse_bezier_coords(tri); +} + static HRESULT d2d_geometry_resolve_beziers(struct d2d_geometry *geometry) { struct d2d_cubic_triangulation *triangles; @@ -3538,6 +3861,14 @@ static HRESULT d2d_geometry_resolve_beziers(struct d2d_geometry *geometry) }
d2d_geometry_get_figure_orientation(geometry, triangles); + d2d_geometry_get_first_bezier_segment_idx(geometry, &idx_p); + for (;;) + { + d2d_geometry_classify_bezier(geometry, triangles, &idx_p); + + if (!d2d_geometry_get_next_bezier_segment_idx(geometry, &idx_p)) + break; + }
for (i = 0; i < geometry->u.path.figure_count; ++i) {