[PATCH] d2d1: Partially implement StrokeContainsPoint() for rectangles
Nikolay Sivov
nsivov at codeweavers.com
Fri Sep 8 09:11:14 CDT 2017
Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
dlls/d2d1/geometry.c | 123 ++++++++++++++++++++++++++++++++++++++++++++--
dlls/d2d1/tests/d2d1.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 248 insertions(+), 4 deletions(-)
diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c
index 8c6688176f..7e2b7245c0 100644
--- a/dlls/d2d1/geometry.c
+++ b/dlls/d2d1/geometry.c
@@ -411,11 +411,16 @@ 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 float d2d_point_length(const D2D1_POINT_2F *p)
+{
+ return sqrtf(d2d_point_dot(p, p));
+}
+
static void d2d_point_normalise(D2D1_POINT_2F *p)
{
float l;
- if ((l = sqrtf(d2d_point_dot(p, p))) != 0.0f)
+ if ((l = d2d_point_length(p)) != 0.0f)
d2d_point_scale(p, 1.0f / l);
}
@@ -3519,15 +3524,125 @@ static HRESULT STDMETHODCALLTYPE d2d_rectangle_geometry_GetWidenedBounds(ID2D1Re
return E_NOTIMPL;
}
+static BOOL d2d_point_on_segment(const D2D1_POINT_2F *q, const D2D1_POINT_2F *p0,
+ const D2D1_POINT_2F *p1, float tolerance)
+{
+ float dot, edge_length, distance;
+ D2D1_POINT_2F v_p, v_q, n;
+
+ d2d_point_subtract(&v_p, p1, p0);
+ if ((edge_length = d2d_point_length(&v_p)) == 0.0f)
+ return FALSE;
+
+ /* Edge distance. */
+ d2d_point_subtract(&v_q, q, p0);
+
+ n.x = -v_p.y;
+ n.y = v_p.x;
+ distance = fabsf(d2d_point_dot(&v_q, &n)) / edge_length;
+ if (distance >= tolerance)
+ return FALSE;
+
+ /* Test if normal and edge intersect. */
+ dot = d2d_point_dot(&v_q, &v_p);
+ return dot >= 0.0f && dot <= edge_length * edge_length;
+}
+
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");
+
+ tolerance = fabsf(tolerance);
+
+ if (transform)
+ {
+ D2D1_POINT_2F points[4], vertices[4], v_s[2], origin;
+ float stroke_widths[2];
+ unsigned int i;
+
+ stroke_width *= 0.5f;
+
+ d2d_point_transform(&v_s[0], transform, stroke_width, 0.0f);
+ d2d_point_transform(&v_s[1], transform, stroke_width, 0.0f);
+ d2d_point_transform(&origin, transform, 0.0f, 0.0f);
+
+ d2d_point_subtract(&v_s[0], &v_s[0], &origin);
+ d2d_point_subtract(&v_s[1], &v_s[1], &origin);
+
+ stroke_widths[0] = d2d_point_length(&v_s[0]);
+ stroke_widths[1] = d2d_point_length(&v_s[1]);
+
+ d2d_point_transform(&points[0], transform, rect->left, rect->top);
+ d2d_point_transform(&points[1], transform, rect->left, rect->bottom);
+ d2d_point_transform(&points[2], transform, rect->right, rect->bottom);
+ d2d_point_transform(&points[3], transform, rect->right, rect->top);
+
+ d2d_point_transform(&vertices[0], transform, rect->left - stroke_width, rect->top - stroke_width);
+ d2d_point_transform(&vertices[1], transform, rect->left - stroke_width, rect->bottom + stroke_width);
+ d2d_point_transform(&vertices[2], transform, rect->right + stroke_width, rect->bottom + stroke_width);
+ d2d_point_transform(&vertices[3], transform, rect->right + stroke_width, rect->top - stroke_width);
+
+ *contains = FALSE;
+
+ for (i = 0; i < ARRAY_SIZE(points); i++)
+ {
+ D2D1_POINT_2F v;
+
+ d2d_point_subtract(&v, &point, &vertices[i]);
+
+ if (d2d_point_dot(&v, &v) < tolerance * tolerance)
+ {
+ *contains = TRUE;
+ break;
+ }
+
+ if (d2d_point_on_segment(&point, &points[i], &points[(i + 1) % 4], stroke_widths[i % 2] + tolerance))
+ {
+ *contains = TRUE;
+ break;
+ }
+ }
+ }
+ 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);
+
+ /* Inside test. */
+ if (d.x <= (s.x - stroke_width) * 0.5f - tolerance && d.y <= (s.y - stroke_width) * 0.5f - tolerance)
+ {
+ *contains = FALSE;
+ return S_OK;
+ }
+
+ if (tolerance == 0.0f)
+ {
+ *contains = d.x < (s.x + stroke_width) * 0.5f && d.y < (s.y + stroke_width) * 0.5f;
+ }
+ else
+ {
+ 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 = d2d_point_dot(&d, &d) < 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..95a010b13e 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -76,6 +76,15 @@ struct expected_geometry_figure
const struct geometry_segment *segments;
};
+struct contains_point_test
+{
+ D2D1_MATRIX_3X2_F transform;
+ D2D1_POINT_2F point;
+ float tolerance;
+ float stroke_width;
+ BOOL contains;
+};
+
static void set_point(D2D1_POINT_2F *point, float x, float y)
{
point->x = x;
@@ -2632,6 +2641,7 @@ static void test_rectangle_geometry(void)
D2D1_RECT_F rect, rect2;
ID2D1Factory *factory;
D2D1_POINT_2F point;
+ unsigned int i;
BOOL contains;
HRESULT hr;
BOOL match;
@@ -2683,6 +2693,23 @@ static void test_rectangle_geometry(void)
{D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {-1.85640793e+01f, 0.0f}, 3, &expected_segments[18]},
{D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {0.0f, 8.99519043e+01f}, 3, &expected_segments[21]},
};
+ static const struct contains_point_test stroke_contains[] =
+ {
+ {{0.0f, 0.0f, 0.0f, 1.0f}, {0.1f, 10.0f}, 0.0f, 1.0f, FALSE},
+ {{0.0f, 0.0f, 0.0f, 1.0f}, {5.0f, 10.0f}, 5.0f, 1.0f, FALSE},
+ {{0.0f, 0.0f, 0.0f, 1.0f}, {4.9f, 10.0f}, 5.0f, 1.0f, TRUE},
+ {{0.0f, 0.0f, 0.0f, 1.0f}, {5.0f, 10.0f}, -5.0f, 1.0f, FALSE},
+ {{0.0f, 0.0f, 0.0f, 1.0f}, {4.9f, 10.0f}, -5.0f, 1.0f, TRUE},
+
+ {{1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 10.0f}, 0.0f, 1.0f, TRUE},
+ {{1.0f, 0.0f, 0.0f, 1.0f}, {0.1f, 10.0f}, 0.0f, 1.0f, TRUE},
+ {{1.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 10.0f}, 0.0f, 1.0f, FALSE},
+ {{1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 10.0f}, 1.0f, 1.0f, TRUE},
+ {{1.0f, 0.0f, 0.0f, 1.0f}, {0.59f, 10.0f}, 1.0f, 1.0f, TRUE},
+ {{1.0f, 0.0f, 0.0f, 1.0f}, {-0.59f, 10.0f}, 1.0f, 1.0f, TRUE},
+ {{1.0f, 0.0f, 0.0f, 1.0f}, {0.59f, 10.0f}, -1.0f, 1.0f, TRUE},
+ {{1.0f, 0.0f, 0.0f, 1.0f}, {-0.59f, 10.0f}, -1.0f, 1.0f, TRUE},
+ };
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &IID_ID2D1Factory, NULL, (void **)&factory);
ok(SUCCEEDED(hr), "Failed to create factory, hr %#x.\n", hr);
@@ -2766,6 +2793,108 @@ 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.4f, 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);
+
+ contains = TRUE;
+ set_point(&point, 0.5f, 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);
+
+ contains = TRUE;
+ set_point(&point, 0.6f, 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);
+
+ /* Negative tolerance. */
+ contains = FALSE;
+ set_point(&point, -0.6f, 10.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);
+
+ contains = FALSE;
+ set_point(&point, 0.6f, 10.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);
+
+ contains = FALSE;
+ set_point(&point, 1.4f, 10.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);
+
+ contains = TRUE;
+ set_point(&point, 1.5f, 10.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. */
+ for (i = 0; i < sizeof(stroke_contains)/sizeof(stroke_contains[0]); i++)
+ {
+ const struct contains_point_test *test = &stroke_contains[i];
+
+ contains = !test->contains;
+ hr = ID2D1RectangleGeometry_StrokeContainsPoint(geometry, test->point, test->stroke_width, NULL,
+ &test->transform, test->tolerance, &contains);
+ ok(SUCCEEDED(hr), "StrokeContainsPoint() failed, hr %#x.\n", hr);
+ ok(contains == test->contains, "%u: got wrong hit test result %d.\n", i, 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