[PATCH 09/12] d2d1: Add cubic bezier fill side detection.

Connor McAdams conmanx360 at gmail.com
Mon Feb 24 20:32:20 CST 2020


Add ability to detect correct fill side for each cubic bezier.

Signed-off-by: Connor McAdams <conmanx360 at gmail.com>
---
 dlls/d2d1/geometry.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c
index a231bed986..c78664488d 100644
--- a/dlls/d2d1/geometry.c
+++ b/dlls/d2d1/geometry.c
@@ -2913,6 +2913,7 @@ struct d2d_cubic_triangles
     unsigned int tri_count;
 
     D2D1_POINT_2F p[4];
+    unsigned int inside;
 };
 
 enum figure_orientation
@@ -3159,6 +3160,28 @@ static HRESULT d2d_geometry_triangulate_beziers(struct d2d_geometry *geometry,
     return S_OK;
 }
 
+static void d2d_geometry_bezier_normal(D2D1_POINT_2F *out, const D2D1_POINT_2F *p0,
+        const D2D1_POINT_2F *p1, const D2D1_POINT_2F *p2, const D2D1_POINT_2F *p3, float t)
+{
+    D2D1_POINT_2F derivative, probe, p1_p0, p2_p1, p3_p2;
+    float t_c = 1.0f - t;
+    float t_c_sq = t_c * t_c;
+
+    d2d_point_subtract(&p1_p0, p1, p0);
+    d2d_point_subtract(&p2_p1, p2, p1);
+    d2d_point_subtract(&p3_p2, p3, p2);
+
+    d2d_point_calculate_bezier(&probe, p0, p1, p2, p3, t);
+    derivative.x = 3.0f * t_c_sq * p1_p0.x + 6.0f * t_c * t * p2_p1.x + (3.0f * (t * t)) * p3_p2.x;
+    derivative.y = 3.0f * t_c_sq * p1_p0.y + 6.0f * t_c * t * p2_p1.y + (3.0f * (t * t)) * p3_p2.y;
+    d2d_point_normalise(&derivative);
+    /* Scale the normal down so it doesn't go too far from the curve. */
+    d2d_point_scale(&derivative, 0.01f);
+
+    out->x = -derivative.y + probe.x;
+    out->y = derivative.x + probe.y;
+}
+
 static float d2d_geometry_point_orientation(const D2D1_POINT_2F *a, const D2D1_POINT_2F *b)
 {
     return (b->x - a->x) * (b->y + a->y);
@@ -3167,15 +3190,27 @@ static float d2d_geometry_point_orientation(const D2D1_POINT_2F *a, const D2D1_P
 static void d2d_geometry_get_figure_orientation(struct d2d_geometry *geometry,
         struct d2d_cubic_triangulation *triangles)
 {
+    struct d2d_geometry *geometry_flat;
+    ID2D1PathGeometry *flatten_geometry;
+    ID2D1GeometrySink *sink;
     const struct d2d_figure *figure;
     struct d2d_segment_idx idx;
     struct d2d_cubic_triangulation *fig_tri;
+    struct d2d_cubic_triangles *tri;
     enum d2d_vertex_type type;
     const D2D1_POINT_2F *p[4];
+    D2D1_POINT_2F probe;
     float orientation;
     size_t next;
     unsigned int i;
 
+    ID2D1Factory_CreatePathGeometry((ID2D1Factory *)geometry->factory, &flatten_geometry);
+    ID2D1PathGeometry_Open(flatten_geometry, &sink);
+    ID2D1PathGeometry_Simplify((ID2D1PathGeometry *)&geometry->ID2D1Geometry_iface, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+        NULL, 0.05f, (ID2D1SimplifiedGeometrySink *)sink);
+    geometry_flat = impl_from_ID2D1GeometrySink(sink);
+    ID2D1GeometrySink_Close(sink);
+
     for (idx.figure_idx = 0; idx.figure_idx < geometry->u.path.figure_count; ++idx.figure_idx)
     {
         figure = &geometry->u.path.figures[idx.figure_idx];
@@ -3184,6 +3219,7 @@ static void d2d_geometry_get_figure_orientation(struct d2d_geometry *geometry,
 
         for (idx.vertex_idx = 0, idx.control_idx = 0; idx.vertex_idx < figure->vertex_count; ++idx.vertex_idx)
         {
+            tri = &fig_tri->cubic_tri[idx.control_idx];
             type = figure->vertex_types[idx.vertex_idx];
             next = idx.vertex_idx + 1;
             if (next == figure->vertex_count)
@@ -3204,6 +3240,18 @@ static void d2d_geometry_get_figure_orientation(struct d2d_geometry *geometry,
                     p[2] = &figure->bezier_controls[idx.control_idx++].c1;
                     p[3] = &figure->vertices[next];
 
+                    /* Check the fill side for each bezier individually by
+                     * casting a normal at the center of the bezier, and
+                     * seeing if the point is within the interior of the
+                     * flattened geometry. If this isn't done, it leads to
+                     * issues with multiple overlapping figures. The geometry
+                     * needs to be flattened because otherwise interior
+                     * detection is done between the start and end vertices of
+                     * the bezier, which leads to issues where one control
+                     * point is inside and the other outside. */
+                    d2d_geometry_bezier_normal(&probe, p[0], p[1], p[2], p[3], 0.5f);
+                    tri->inside = d2d_path_geometry_point_inside(geometry_flat, &probe, FALSE);
+
                     for (i = 1; i < 4; i++)
                         orientation += d2d_geometry_point_orientation(p[i - 1], p[i]);
                     break;
@@ -3220,6 +3268,9 @@ static void d2d_geometry_get_figure_orientation(struct d2d_geometry *geometry,
         else
             fig_tri->orientation = ORIENTATION_CCW;
     }
+
+    ID2D1PathGeometry_Release(flatten_geometry);
+    ID2D1Factory_Release((ID2D1Factory *)geometry->factory);
 }
 
 static BOOL d2d_geometry_check_bezier_overlap(struct d2d_geometry *geometry,
-- 
2.20.1




More information about the wine-devel mailing list