[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