[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