[PATCH 1/4] d2d1: Store original Bézier curves as cubics.

Henri Verbeet hverbeet at codeweavers.com
Wed Dec 8 13:14:19 CST 2021


Signed-off-by: Henri Verbeet <hverbeet at codeweavers.com>
---
 dlls/d2d1/geometry.c | 127 +++++++++++++++++++++++++++++--------------
 1 file changed, 85 insertions(+), 42 deletions(-)

diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c
index c7d0d94f1ad..49e4393ffec 100644
--- a/dlls/d2d1/geometry.c
+++ b/dlls/d2d1/geometry.c
@@ -72,6 +72,7 @@ struct d2d_figure
     size_t bezier_control_count;
 
     D2D1_POINT_2F *original_bezier_controls;
+    size_t original_bezier_controls_size;
     size_t original_bezier_control_count;
 
     D2D1_RECT_F bounds;
@@ -656,6 +657,22 @@ static BOOL d2d_figure_add_bezier_controls(struct d2d_figure *figure, size_t cou
     return TRUE;
 }
 
+static BOOL d2d_figure_add_original_bezier_controls(struct d2d_figure *figure, size_t count, const D2D1_POINT_2F *p)
+{
+    if (!d2d_array_reserve((void **)&figure->original_bezier_controls, &figure->original_bezier_controls_size,
+            figure->original_bezier_control_count + count, sizeof(*figure->original_bezier_controls)))
+    {
+        ERR("Failed to grow cubic Bézier controls array.\n");
+        return FALSE;
+    }
+
+    memcpy(&figure->original_bezier_controls[figure->original_bezier_control_count],
+            p, count * sizeof(*figure->original_bezier_controls));
+    figure->original_bezier_control_count += count;
+
+    return TRUE;
+}
+
 static void d2d_cdt_edge_rot(struct d2d_cdt_edge_ref *dst, const struct d2d_cdt_edge_ref *src)
 {
     dst->idx = src->idx;
@@ -2595,7 +2612,15 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddBeziers(ID2D1GeometrySink *if
     {
         D2D1_RECT_F bezier_bounds;
 
-        /* FIXME: This tries to approximate a cubic bezier with a quadratic one. */
+        if (!d2d_figure_add_original_bezier_controls(figure, 1, &beziers[i].point1)
+                || !d2d_figure_add_original_bezier_controls(figure, 1, &beziers[i].point2))
+        {
+            ERR("Failed to add cubic Bézier controls.\n");
+            geometry->u.path.state = D2D_GEOMETRY_STATE_ERROR;
+            return;
+        }
+
+        /* FIXME: This tries to approximate a cubic Bézier with a quadratic one. */
         p.x = (beziers[i].point1.x + beziers[i].point2.x) * 0.75f;
         p.y = (beziers[i].point1.y + beziers[i].point2.y) * 0.75f;
         p.x -= (figure->vertices[figure->vertex_count - 1].x + beziers[i].point3.x) * 0.25f;
@@ -2667,8 +2692,8 @@ static void d2d_path_geometry_free_figures(struct d2d_geometry *geometry)
 
     for (i = 0; i < geometry->u.path.figure_count; ++i)
     {
-        heap_free(geometry->u.path.figures[i].bezier_controls);
         heap_free(geometry->u.path.figures[i].original_bezier_controls);
+        heap_free(geometry->u.path.figures[i].bezier_controls);
         heap_free(geometry->u.path.figures[i].vertices);
     }
     heap_free(geometry->u.path.figures);
@@ -2937,7 +2962,6 @@ static HRESULT STDMETHODCALLTYPE d2d_geometry_sink_Close(ID2D1GeometrySink *ifac
 {
     struct d2d_geometry *geometry = impl_from_ID2D1GeometrySink(iface);
     HRESULT hr = E_FAIL;
-    size_t i;
 
     TRACE("iface %p.\n", iface);
 
@@ -2949,15 +2973,6 @@ static HRESULT STDMETHODCALLTYPE d2d_geometry_sink_Close(ID2D1GeometrySink *ifac
     }
     geometry->u.path.state = D2D_GEOMETRY_STATE_CLOSED;
 
-    for (i = 0; i < geometry->u.path.figure_count; ++i)
-    {
-        struct d2d_figure *figure = &geometry->u.path.figures[i];
-        size_t size = figure->bezier_control_count * sizeof(*figure->original_bezier_controls);
-        if (!(figure->original_bezier_controls = heap_alloc(size)))
-            goto done;
-        memcpy(figure->original_bezier_controls, figure->bezier_controls, size);
-    }
-
     if (!d2d_geometry_intersect_self(geometry))
         goto done;
     if (FAILED(hr = d2d_geometry_resolve_beziers(geometry)))
@@ -3016,6 +3031,17 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddQuadraticBeziers(ID2D1Geometr
     for (i = 0; i < bezier_count; ++i)
     {
         D2D1_RECT_F bezier_bounds;
+        D2D1_POINT_2F p[2];
+
+        /* Construct a cubic curve. */
+        d2d_point_lerp(&p[0], &figure->vertices[figure->vertex_count - 1], &beziers[i].point1, 2.0f / 3.0f);
+        d2d_point_lerp(&p[1], &beziers[i].point2, &beziers[i].point1, 2.0f / 3.0f);
+        if (!d2d_figure_add_original_bezier_controls(figure, 2, p))
+        {
+            ERR("Failed to add cubic Bézier controls.\n");
+            geometry->u.path.state = D2D_GEOMETRY_STATE_ERROR;
+            return;
+        }
 
         d2d_rect_get_bezier_bounds(&bezier_bounds, &figure->vertices[figure->vertex_count - 1],
                 &beziers[i].point1, &beziers[i].point2);
@@ -3228,10 +3254,19 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_GetBounds(ID2D1PathGeometry *
                     break;
 
                 case D2D_VERTEX_TYPE_BEZIER:
+                    /* FIXME: This attempts to approximate a cubic Bézier with
+                     * a quadratic one. */
                     p1 = figure->original_bezier_controls[bezier_idx++];
                     d2d_point_transform(&p1, transform, p1.x, p1.y);
+                    p2 = figure->original_bezier_controls[bezier_idx++];
+                    d2d_point_transform(&p2, transform, p2.x, p2.y);
+                    p1.x = (p1.x + p2.x) * 0.75f;
+                    p1.y = (p1.y + p2.y) * 0.75f;
                     p2 = figure->vertices[j];
                     d2d_point_transform(&p2, transform, p2.x, p2.y);
+                    p1.x -= (p.x + p2.x) * 0.25f;
+                    p1.y -= (p.y + p2.y) * 0.25f;
+
                     d2d_rect_get_bezier_bounds(&bezier_bounds, &p, &p1, &p2);
                     d2d_rect_union(bounds, &bezier_bounds);
                     p = p2;
@@ -3250,10 +3285,19 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_GetBounds(ID2D1PathGeometry *
 
         if (d2d_vertex_type_is_bezier(type))
         {
+            /* FIXME: This attempts to approximate a cubic Bézier with
+             * a quadratic one. */
             p1 = figure->original_bezier_controls[bezier_idx++];
             d2d_point_transform(&p1, transform, p1.x, p1.y);
+            p2 = figure->original_bezier_controls[bezier_idx++];
+            d2d_point_transform(&p2, transform, p2.x, p2.y);
+            p1.x = (p1.x + p2.x) * 0.75f;
+            p1.y = (p1.y + p2.y) * 0.75f;
             p2 = figure->vertices[0];
             d2d_point_transform(&p2, transform, p2.x, p2.y);
+            p1.x -= (p.x + p2.x) * 0.25f;
+            p1.y -= (p.y + p2.y) * 0.25f;
+
             d2d_rect_get_bezier_bounds(&bezier_bounds, &p, &p1, &p2);
             d2d_rect_union(bounds, &bezier_bounds);
         }
@@ -3367,22 +3411,6 @@ static void d2d_geometry_flatten_cubic(ID2D1SimplifiedGeometrySink *sink, const
     ID2D1SimplifiedGeometrySink_SetSegmentFlags(sink, D2D1_PATH_SEGMENT_NONE);
 }
 
-static void d2d_geometry_simplify_quadratic(ID2D1SimplifiedGeometrySink *sink,
-        D2D1_GEOMETRY_SIMPLIFICATION_OPTION option, const D2D1_POINT_2F *p0,
-        const D2D1_POINT_2F *p1, const D2D1_POINT_2F *p2, float tolerance)
-{
-    D2D1_BEZIER_SEGMENT b;
-
-    d2d_point_lerp(&b.point1, p0, p1, 2.0f / 3.0f);
-    d2d_point_lerp(&b.point2, p2, p1, 2.0f / 3.0f);
-    b.point3 = *p2;
-
-    if (option == D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES)
-        d2d_geometry_flatten_cubic(sink, p0, &b, tolerance);
-    else
-        ID2D1SimplifiedGeometrySink_AddBeziers(sink, &b, 1);
-}
-
 static HRESULT STDMETHODCALLTYPE d2d_path_geometry_Simplify(ID2D1PathGeometry *iface,
         D2D1_GEOMETRY_SIMPLIFICATION_OPTION option, const D2D1_MATRIX_3X2_F *transform, float tolerance,
         ID2D1SimplifiedGeometrySink *sink)
@@ -3391,8 +3419,9 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_Simplify(ID2D1PathGeometry *i
     enum d2d_vertex_type type = D2D_VERTEX_TYPE_NONE;
     unsigned int i, j, bezier_idx;
     D2D1_FIGURE_BEGIN begin;
-    D2D1_POINT_2F p, p1, p2;
+    D2D1_BEZIER_SEGMENT b;
     D2D1_FIGURE_END end;
+    D2D1_POINT_2F p;
 
     TRACE("iface %p, option %#x, transform %p, tolerance %.8e, sink %p.\n",
             iface, option, transform, tolerance, sink);
@@ -3432,14 +3461,21 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_Simplify(ID2D1PathGeometry *i
                     break;
 
                 case D2D_VERTEX_TYPE_BEZIER:
-                    p1 = figure->original_bezier_controls[bezier_idx++];
+                    b.point1 = figure->original_bezier_controls[bezier_idx++];
+                    b.point2 = figure->original_bezier_controls[bezier_idx++];
+                    b.point3 = figure->vertices[j];
                     if (transform)
-                        d2d_point_transform(&p1, transform, p1.x, p1.y);
-                    p2 = figure->vertices[j];
-                    if (transform)
-                        d2d_point_transform(&p2, transform, p2.x, p2.y);
-                    d2d_geometry_simplify_quadratic(sink, option, &p, &p1, &p2, tolerance);
-                    p = p2;
+                    {
+                        d2d_point_transform(&b.point1, transform, b.point1.x, b.point1.y);
+                        d2d_point_transform(&b.point2, transform, b.point2.x, b.point2.y);
+                        d2d_point_transform(&b.point3, transform, b.point3.x, b.point3.y);
+                    }
+
+                    if (option == D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES)
+                        d2d_geometry_flatten_cubic(sink, &p, &b, tolerance);
+                    else
+                        ID2D1SimplifiedGeometrySink_AddBeziers(sink, &b, 1);
+                    p = b.point3;
                     break;
 
                 default:
@@ -3456,13 +3492,20 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_Simplify(ID2D1PathGeometry *i
 
         if (d2d_vertex_type_is_bezier(type))
         {
-            p1 = figure->original_bezier_controls[bezier_idx++];
+            b.point1 = figure->original_bezier_controls[bezier_idx++];
+            b.point2 = figure->original_bezier_controls[bezier_idx++];
+            b.point3 = figure->vertices[0];
             if (transform)
-                d2d_point_transform(&p1, transform, p1.x, p1.y);
-            p2 = figure->vertices[0];
-            if (transform)
-                d2d_point_transform(&p2, transform, p2.x, p2.y);
-            d2d_geometry_simplify_quadratic(sink, option, &p, &p1, &p2, tolerance);
+            {
+                d2d_point_transform(&b.point1, transform, b.point1.x, b.point1.y);
+                d2d_point_transform(&b.point2, transform, b.point2.x, b.point2.y);
+                d2d_point_transform(&b.point3, transform, b.point3.x, b.point3.y);
+            }
+
+            if (option == D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES)
+                d2d_geometry_flatten_cubic(sink, &p, &b, tolerance);
+            else
+                ID2D1SimplifiedGeometrySink_AddBeziers(sink, &b, 1);
         }
 
         end = figure->flags & D2D_FIGURE_FLAG_CLOSED ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN;
-- 
2.30.2




More information about the wine-devel mailing list