[PATCH 11/12] d2d1: Add cubic bezier texture coordinate functions.
Connor McAdams
conmanx360 at gmail.com
Mon Feb 24 20:32:22 CST 2020
Add functions for getting cubic bezier texture coordinates.
Signed-off-by: Connor McAdams <conmanx360 at 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)
{
--
2.20.1
More information about the wine-devel
mailing list