[v2 PATCH] d2d1: Partially implement StrokeContainsPoint() for rectangles

Nikolay Sivov nsivov at codeweavers.com
Wed Sep 6 05:35:36 CDT 2017


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/d2d1/geometry.c   | 126 +++++++++++++++++++++++++++++++++++++++++++++++--
 dlls/d2d1/tests/d2d1.c |  69 +++++++++++++++++++++++++++
 2 files changed, 191 insertions(+), 4 deletions(-)

diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c
index 8c6688176f..6034299d6f 100644
--- a/dlls/d2d1/geometry.c
+++ b/dlls/d2d1/geometry.c
@@ -411,12 +411,14 @@ static float d2d_point_dot(const D2D1_POINT_2F *p0, const D2D1_POINT_2F *p1)
     return p0->x * p1->x + p0->y * p1->y;
 }
 
-static void d2d_point_normalise(D2D1_POINT_2F *p)
+static float d2d_point_normalise(D2D1_POINT_2F *p)
 {
     float l;
 
     if ((l = sqrtf(d2d_point_dot(p, p))) != 0.0f)
         d2d_point_scale(p, 1.0f / l);
+
+    return l;
 }
 
 /* This implementation is based on the paper "Adaptive Precision
@@ -3519,15 +3521,131 @@ static HRESULT STDMETHODCALLTYPE d2d_rectangle_geometry_GetWidenedBounds(ID2D1Re
     return E_NOTIMPL;
 }
 
+static BOOL d2d_rect_point_on(const D2D1_POINT_2F *point, float tolerance, const D2D1_POINT_2F *points)
+{
+    unsigned int i;
+    float dot;
+
+    for (i = 0; i < 4; i++)
+    {
+        float edge_length, distance;
+        D2D1_POINT_2F v, edge;
+
+        v.x = point->x - points[i].x;
+        v.y = point->y - points[i].y;
+
+        /* Point to vertex distance. */
+        if (d2d_point_dot(&v, &v) < tolerance * tolerance)
+            return TRUE;
+
+        /* Point to edge distance. */
+        edge.x = points[(i + 1) % 4].x - points[i].x;
+        edge.y = points[(i + 1) % 4].y - points[i].y;
+
+        if ((edge_length = sqrtf(d2d_point_dot(&edge, &edge))) == 0.0f)
+            continue;
+
+        distance = fabsf(edge.x * v.y - edge.y * v.x) / edge_length;
+
+        if (distance >= tolerance)
+            continue;
+
+        d2d_point_normalise(&edge);
+        dot = d2d_point_dot(&edge, &v);
+        if (dot >= 0.0f && dot <= edge_length)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static BOOL d2d_rect_point_inside(const D2D1_POINT_2F *point, const D2D1_POINT_2F *points)
+{
+    D2D1_POINT_2F vec[2];
+    unsigned int i;
+    float dot;
+
+    for (i = 0; i < 2; i++)
+    {
+        float l;
+
+        vec[0].x = points[i + 1].x - points[i].x;
+        vec[0].y = points[i + 1].y - points[i].y;
+        vec[1].x = point->x - points[i].x;
+        vec[1].y = point->y - points[i].y;
+
+        if ((l = d2d_point_normalise(&vec[0])) == 0.0f)
+            return FALSE;
+
+        if ((dot = d2d_point_dot(&vec[0], &vec[1])) < 0.0f || dot > l)
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
 static HRESULT STDMETHODCALLTYPE d2d_rectangle_geometry_StrokeContainsPoint(ID2D1RectangleGeometry *iface,
         D2D1_POINT_2F point, float stroke_width, ID2D1StrokeStyle *stroke_style, const D2D1_MATRIX_3X2_F *transform,
         float tolerance, BOOL *contains)
 {
-    FIXME("iface %p, point {%.8e, %.8e}, stroke_width %.8e, stroke_style %p, "
-            "transform %p, tolerance %.8e, contains %p stub!\n",
+    struct d2d_geometry *geometry = impl_from_ID2D1RectangleGeometry(iface);
+    D2D1_RECT_F *rect = &geometry->u.rectangle.rect;
+
+    TRACE("iface %p, point {%.8e, %.8e}, stroke_width %.8e, stroke_style %p, "
+            "transform %p, tolerance %.8e, contains %p.\n",
             iface, point.x, point.y, stroke_width, stroke_style, transform, tolerance, contains);
 
-    return E_NOTIMPL;
+    if (stroke_style)
+        FIXME("Stroke style ignored.\n");
+
+    if (tolerance <= 0.0f)
+        tolerance = D2D1_DEFAULT_FLATTENING_TOLERANCE;
+
+    if (transform)
+    {
+        D2D1_POINT_2F outer[4], inner[4];
+
+        stroke_width *= 0.5f;
+
+        d2d_point_transform(&inner[0], transform, rect->left + stroke_width, rect->top + stroke_width);
+        d2d_point_transform(&inner[1], transform, rect->left + stroke_width, rect->bottom - stroke_width);
+        d2d_point_transform(&inner[2], transform, rect->right - stroke_width, rect->bottom - stroke_width);
+        d2d_point_transform(&inner[3], transform, rect->right - stroke_width, rect->top + stroke_width);
+
+        d2d_point_transform(&outer[0], transform, rect->left - stroke_width, rect->top - stroke_width);
+        d2d_point_transform(&outer[1], transform, rect->left - stroke_width, rect->bottom + stroke_width);
+        d2d_point_transform(&outer[2], transform, rect->right + stroke_width, rect->bottom + stroke_width);
+        d2d_point_transform(&outer[3], transform, rect->right + stroke_width, rect->top - stroke_width);
+
+        if (d2d_rect_point_inside(&point, outer) && !d2d_rect_point_inside(&point, inner))
+        {
+            *contains = TRUE;
+        }
+        else
+            *contains = d2d_rect_point_on(&point, tolerance, outer) || d2d_rect_point_on(&point, tolerance, inner);
+    }
+    else
+    {
+        D2D1_POINT_2F d, s;
+
+        s.x = rect->right - rect->left;
+        s.y = rect->bottom - rect->top;
+        d.x = fabsf((rect->right + rect->left) * 0.5f - point.x);
+        d.y = fabsf((rect->bottom + rect->top) * 0.5f - point.y);
+
+        if (d.x < (s.x - stroke_width) * 0.5f - tolerance && d.y < (s.y - stroke_width) * 0.5f - tolerance)
+        {
+            *contains = FALSE;
+            return S_OK;
+        }
+
+        d.x = max(d.x - (s.x + stroke_width) * 0.5f, 0.0f);
+        d.y = max(d.y - (s.y + stroke_width) * 0.5f, 0.0f);
+
+        *contains = (d.x * d.x + d.y * d.y) < tolerance * tolerance;
+    }
+
+    return S_OK;
 }
 
 static HRESULT STDMETHODCALLTYPE d2d_rectangle_geometry_FillContainsPoint(ID2D1RectangleGeometry *iface,
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c
index f6261295b6..647018879a 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -2766,6 +2766,75 @@ static void test_rectangle_geometry(void)
     ok(SUCCEEDED(hr), "FillContainsPoint() failed, hr %#x.\n", hr);
     ok(!!contains, "Got wrong hit test result %d.\n", contains);
 
+    /* Stroked area hittesting. Edge. */
+    contains = FALSE;
+    set_point(&point, 0.0f, 0.0f);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 1.0f, NULL, NULL, 0.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!!contains, "Got wrong hit test result %d.\n", contains);
+
+    /* Negative tolerance. */
+    contains = FALSE;
+    set_point(&point, 0.6f, 0.0f);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 1.0f, NULL, NULL, -1.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!!contains, "Got wrong hit test result %d.\n", contains);
+
+    /* Within tolerance limit around corner. */
+    contains = TRUE;
+    set_point(&point, -D2D1_DEFAULT_FLATTENING_TOLERANCE - 1.0f, 0.0f);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 2.0f, NULL, NULL, 0.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!contains, "Got wrong hit test result %d.\n", contains);
+
+    contains = TRUE;
+    set_point(&point, -D2D1_DEFAULT_FLATTENING_TOLERANCE - 1.01f, 0.0f);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 2.0f, NULL, NULL, 0.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!contains, "Got wrong hit test result %d.\n", contains);
+
+    contains = FALSE;
+    set_point(&point, -D2D1_DEFAULT_FLATTENING_TOLERANCE, -D2D1_DEFAULT_FLATTENING_TOLERANCE);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 2.0f, NULL, NULL, 0.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!!contains, "Got wrong hit test result %d.\n", contains);
+
+    contains = TRUE;
+    set_point(&point, -D2D1_DEFAULT_FLATTENING_TOLERANCE, -D2D1_DEFAULT_FLATTENING_TOLERANCE - 1.01f);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 2.0f, NULL, NULL, 0.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!contains, "Got wrong hit test result %d.\n", contains);
+
+    /* Center point. */
+    contains = TRUE;
+    set_point(&point, 5.0f, 10.0f);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 1.0f, NULL, NULL, 0.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!contains, "Got wrong hit test result %d.\n", contains);
+
+    /* Center point, large stroke width. */
+    contains = FALSE;
+    set_point(&point, 5.0f, 10.0f);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 100.0f, NULL, NULL, 0.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!!contains, "Got wrong hit test result %d.\n", contains);
+
+    /* Center point, large tolerance. */
+    contains = FALSE;
+    set_point(&point, 5.0f, 10.0f);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 1.0f, NULL, NULL, 50.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!!contains, "Got wrong hit test result %d.\n", contains);
+
+    /* With transform. */
+    set_matrix_identity(&matrix);
+    scale_matrix(&matrix, 5.0f, 1.0f);
+    contains = FALSE;
+    set_point(&point, 2.0f, 10.0f);
+    hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, point, 1.0f, NULL, &matrix, 0.0f, &contains);
+    ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+    ok(!!contains, "Got wrong hit test result %d.\n", contains);
+
     /* Test GetBounds() and Simplify(). */
     hr = ID2D1RectangleGeometry_GetBounds(geometry, NULL, &rect);
     ok(SUCCEEDED(hr), "Failed to get bounds.\n");
-- 
2.14.1




More information about the wine-patches mailing list