[PATCH 2/2] d2d1: Implement GetBounds() for path geometries

Nikolay Sivov nsivov at codeweavers.com
Thu Sep 14 07:10:57 CDT 2017


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/d2d1/d2d1_private.h |   2 +
 dlls/d2d1/geometry.c     | 105 +++++++++++++++++++++++++++++++++++++++++-
 dlls/d2d1/tests/d2d1.c   | 116 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 221 insertions(+), 2 deletions(-)

diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index ff7927be66..f32c4d3f25 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -392,6 +392,8 @@ struct d2d_geometry
             enum d2d_geometry_state state;
             D2D1_FILL_MODE fill_mode;
             UINT32 segment_count;
+
+            D2D1_RECT_F bounds;
         } path;
         struct
         {
diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c
index e38ac3444e..637078ea69 100644
--- a/dlls/d2d1/geometry.c
+++ b/dlls/d2d1/geometry.c
@@ -3079,9 +3079,106 @@ static void STDMETHODCALLTYPE d2d_path_geometry_GetFactory(ID2D1PathGeometry *if
 static HRESULT STDMETHODCALLTYPE d2d_path_geometry_GetBounds(ID2D1PathGeometry *iface,
         const D2D1_MATRIX_3X2_F *transform, D2D1_RECT_F *bounds)
 {
-    FIXME("iface %p, transform %p, bounds %p stub!\n", iface, transform, bounds);
+    struct d2d_geometry *geometry = impl_from_ID2D1PathGeometry(iface);
+    size_t i;
 
-    return E_NOTIMPL;
+    TRACE("iface %p, transform %p, bounds %p.\n", iface, transform, bounds);
+
+    if (geometry->u.path.state != D2D_GEOMETRY_STATE_CLOSED)
+        return D2DERR_WRONG_STATE;
+
+    bounds->left = FLT_MAX;
+    bounds->top = FLT_MAX;
+    bounds->right = -FLT_MAX;
+    bounds->bottom = -FLT_MAX;
+
+    if (!transform)
+    {
+        if (geometry->u.path.bounds.left > geometry->u.path.bounds.right)
+        {
+            for (i = 0; i < geometry->u.path.figure_count; ++i)
+                d2d_rect_union(&geometry->u.path.bounds, &geometry->u.path.figures[i].bounds);
+        }
+
+        *bounds = geometry->u.path.bounds;
+        return S_OK;
+    }
+
+    for (i = 0; i < geometry->u.path.figure_count; ++i)
+    {
+        const struct d2d_figure *figure = &geometry->u.path.figures[i];
+        enum d2d_vertex_type type = D2D_VERTEX_TYPE_NONE;
+        D2D1_RECT_F bezier_bounds;
+        D2D1_POINT_2F p, p1, p2;
+        size_t j, bezier_idx;
+
+        /* Single vertex figures are reduced by CloseFigure(). */
+        if (figure->vertex_count == 0)
+        {
+            d2d_point_transform(&p, transform, figure->bounds.left, figure->bounds.top);
+            d2d_rect_expand(bounds, &p);
+            continue;
+        }
+
+        for (j = 0; j < figure->vertex_count; ++j)
+        {
+            if (figure->vertex_types[j] == D2D_VERTEX_TYPE_NONE)
+                continue;
+
+            p = figure->vertices[j];
+            type = figure->vertex_types[j];
+            d2d_point_transform(&p, transform, p.x, p.y);
+            d2d_rect_expand(bounds, &p);
+            break;
+        }
+
+        for (bezier_idx = 0, ++j; j < figure->vertex_count; ++j)
+        {
+            if (figure->vertex_types[j] == D2D_VERTEX_TYPE_NONE
+                    || figure->vertex_types[j] == D2D_VERTEX_TYPE_SPLIT_BEZIER)
+                continue;
+
+            switch (type)
+            {
+                case D2D_VERTEX_TYPE_LINE:
+                    p = figure->vertices[j];
+                    d2d_point_transform(&p, transform, p.x, p.y);
+                    d2d_rect_expand(bounds, &p);
+                    break;
+
+                case D2D_VERTEX_TYPE_BEZIER:
+                    p1 = figure->original_bezier_controls[bezier_idx++];
+                    d2d_point_transform(&p1, transform, p1.x, p1.y);
+                    p2 = figure->vertices[j];
+                    d2d_point_transform(&p2, transform, p2.x, p2.y);
+                    d2d_rect_get_bezier_bounds(&bezier_bounds, &p, &p1, &p2);
+                    d2d_rect_union(bounds, &bezier_bounds);
+                    p = p2;
+                    break;
+
+                default:
+                    FIXME("Unhandled vertex type %#x.\n", type);
+                    p = figure->vertices[j];
+                    d2d_point_transform(&p, transform, p.x, p.y);
+                    d2d_rect_expand(bounds, &p);
+                    break;
+            }
+
+            type = figure->vertex_types[j];
+        }
+
+        if (type == D2D_VERTEX_TYPE_BEZIER)
+        {
+            p1 = figure->original_bezier_controls[bezier_idx++];
+            d2d_point_transform(&p1, transform, p1.x, p1.y);
+            p2 = figure->vertices[0];
+            d2d_point_transform(&p2, transform, p2.x, p2.y);
+            d2d_rect_get_bezier_bounds(&bezier_bounds, &p, &p1, &p2);
+            d2d_rect_union(bounds, &bezier_bounds);
+        }
+    }
+
+    return S_OK;
 }
 
 static HRESULT STDMETHODCALLTYPE d2d_path_geometry_GetWidenedBounds(ID2D1PathGeometry *iface, float stroke_width,
@@ -3428,6 +3525,10 @@ void d2d_path_geometry_init(struct d2d_geometry *geometry, ID2D1Factory *factory
 {
     d2d_geometry_init(geometry, factory, &identity, (ID2D1GeometryVtbl *)&d2d_path_geometry_vtbl);
     geometry->u.path.ID2D1GeometrySink_iface.lpVtbl = &d2d_geometry_sink_vtbl;
+    geometry->u.path.bounds.left = FLT_MAX;
+    geometry->u.path.bounds.right = -FLT_MAX;
+    geometry->u.path.bounds.top = FLT_MAX;
+    geometry->u.path.bounds.bottom = -FLT_MAX;
 }
 
 static inline struct d2d_geometry *impl_from_ID2D1RectangleGeometry(ID2D1RectangleGeometry *iface)
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c
index f6261295b6..13d4bc88f7 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -1739,6 +1739,7 @@ static void test_path_geometry(void)
     ID2D1Factory *factory;
     BOOL match, contains;
     D2D1_COLOR_F color;
+    D2D1_RECT_F rect;
     ULONG refcount;
     UINT32 count;
     HWND window;
@@ -2216,6 +2217,7 @@ static void test_path_geometry(void)
     ok(SUCCEEDED(hr), "Failed to create path geometry, hr %#x.\n", hr);
     hr = ID2D1PathGeometry_Open(geometry, &sink);
     ok(SUCCEEDED(hr), "Failed to open geometry sink, hr %#x.\n", hr);
+    set_point(&point, 123.0f, 456.0f);
     ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_FILLED);
     ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
     hr = ID2D1GeometrySink_Close(sink);
@@ -2227,6 +2229,102 @@ static void test_path_geometry(void)
     hr = ID2D1PathGeometry_GetSegmentCount(geometry, &count);
     ok(SUCCEEDED(hr), "Failed to get segment count, hr %#x.\n", hr);
     ok(count == 1, "Got unexpected segment count %u.\n", count);
+
+    set_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, NULL, &rect);
+    ok(SUCCEEDED(hr), "Failed to get geometry bounds, hr %#x.\n", hr);
+    match = compare_rect(&rect, 123.0f, 456.0f, 123.0f, 456.0f, 0);
+    ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
+            rect.left, rect.top, rect.right, rect.bottom);
+
+    set_matrix_identity(&matrix);
+    translate_matrix(&matrix, 80.0f, 640.0f);
+    scale_matrix(&matrix, 2.0f, 0.5f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, &matrix, &rect);
+    ok(SUCCEEDED(hr), "Failed to get geometry bounds, hr %#x.\n", hr);
+    match = compare_rect(&rect, 326.0f, 868.0f, 326.0f, 868.0f, 0);
+    ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
+            rect.left, rect.top, rect.right, rect.bottom);
+
+    ID2D1PathGeometry_Release(geometry);
+
+    /* Close right after Open(). */
+    hr = ID2D1Factory_CreatePathGeometry(factory, &geometry);
+    ok(SUCCEEDED(hr), "Failed to create path geometry, hr %#x.\n", hr);
+
+    /* Not open yet. */
+    set_rect(&rect, 1.0f, 2.0f, 3.0f, 4.0f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, NULL, &rect);
+    ok(hr == D2DERR_WRONG_STATE, "Unexpected hr %#x.\n", hr);
+    match = compare_rect(&rect, 1.0f, 2.0f, 3.0f, 4.0f, 0);
+    ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
+            rect.left, rect.top, rect.right, rect.bottom);
+
+    hr = ID2D1PathGeometry_Open(geometry, &sink);
+    ok(SUCCEEDED(hr), "Failed to open geometry sink, hr %#x.\n", hr);
+
+    /* Open, not closed. */
+    set_rect(&rect, 1.0f, 2.0f, 3.0f, 4.0f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, NULL, &rect);
+    ok(hr == D2DERR_WRONG_STATE, "Unexpected hr %#x.\n", hr);
+    match = compare_rect(&rect, 1.0f, 2.0f, 3.0f, 4.0f, 0);
+    ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
+            rect.left, rect.top, rect.right, rect.bottom);
+
+    hr = ID2D1GeometrySink_Close(sink);
+    ok(SUCCEEDED(hr), "Failed to close geometry sink, hr %#x.\n", hr);
+    ID2D1GeometrySink_Release(sink);
+    hr = ID2D1PathGeometry_GetFigureCount(geometry, &count);
+    ok(SUCCEEDED(hr), "Failed to get figure count, hr %#x.\n", hr);
+    ok(count == 0, "Got unexpected figure count %u.\n", count);
+    hr = ID2D1PathGeometry_GetSegmentCount(geometry, &count);
+    ok(SUCCEEDED(hr), "Failed to get segment count, hr %#x.\n", hr);
+    ok(count == 0, "Got unexpected segment count %u.\n", count);
+
+    set_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, NULL, &rect);
+    ok(SUCCEEDED(hr), "Failed to get geometry bounds, hr %#x.\n", hr);
+    ok(rect.left > rect.right && rect.top > rect.bottom,
+            "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n", rect.left, rect.top, rect.right, rect.bottom);
+
+    set_matrix_identity(&matrix);
+    translate_matrix(&matrix, 10.0f, 20.0f);
+    scale_matrix(&matrix, 10.0f, 20.0f);
+    set_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, &matrix, &rect);
+    ok(SUCCEEDED(hr), "Failed to get geometry bounds, hr %#x.\n", hr);
+    ok(rect.left > rect.right && rect.top > rect.bottom,
+            "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n", rect.left, rect.top, rect.right, rect.bottom);
+
+    ID2D1PathGeometry_Release(geometry);
+
+    /* GetBounds() with bezier segments. */
+    hr = ID2D1Factory_CreatePathGeometry(factory, &geometry);
+    ok(SUCCEEDED(hr), "Failed to create path geometry, hr %#x.\n", hr);
+    hr = ID2D1PathGeometry_Open(geometry, &sink);
+    ok(SUCCEEDED(hr), "Failed to open geometry sink, hr %#x.\n", hr);
+    fill_geometry_sink_bezier(sink);
+    hr = ID2D1GeometrySink_Close(sink);
+    ok(SUCCEEDED(hr), "Failed to close geometry sink, hr %#x.\n", hr);
+    ID2D1GeometrySink_Release(sink);
+
+    set_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, NULL, &rect);
+    ok(SUCCEEDED(hr), "Failed to get geometry bounds, hr %#x.\n", hr);
+    match = compare_rect(&rect, 5.0f, 20.0f, 75.0f, 752.0f, 0);
+    ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
+            rect.left, rect.top, rect.right, rect.bottom);
+
+    set_matrix_identity(&matrix);
+    translate_matrix(&matrix, 80.0f, 640.0f);
+    scale_matrix(&matrix, 2.0f, 0.5f);
+    set_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, &matrix, &rect);
+    ok(SUCCEEDED(hr), "Failed to get geometry bounds, hr %#x.\n", hr);
+    match = compare_rect(&rect, 90.0f, 650.0f, 230.0f, 1016.0f, 0);
+    ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
+            rect.left, rect.top, rect.right, rect.bottom);
+
     ID2D1PathGeometry_Release(geometry);
 
     hr = ID2D1Factory_CreatePathGeometry(factory, &geometry);
@@ -2342,6 +2440,24 @@ static void test_path_geometry(void)
     ok(count == 44, "Got unexpected segment count %u.\n", count);
     ID2D1GeometrySink_Release(sink);
 
+    set_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, NULL, &rect);
+    ok(SUCCEEDED(hr), "Failed to get geometry bounds, hr %#x.\n", hr);
+    match = compare_rect(&rect, 5.0f, 20.0f, 235.0f, 300.0f, 0);
+    ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
+            rect.left, rect.top, rect.right, rect.bottom);
+
+    set_matrix_identity(&matrix);
+    translate_matrix(&matrix, 100.0f, 50.0f);
+    scale_matrix(&matrix, 2.0f, 1.5f);
+    rotate_matrix(&matrix, M_PI / 4.0f);
+    set_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f);
+    hr = ID2D1PathGeometry_GetBounds(geometry, &matrix, &rect);
+    ok(SUCCEEDED(hr), "Failed to get geometry bounds, hr %#x.\n", hr);
+    match = compare_rect(&rect, -3.17192993e+02f, 8.71231079e+01f, 4.04055908e+02f, 6.17453125e+02f, 1);
+    ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
+            rect.left, rect.top, rect.right, rect.bottom);
+
     geometry_sink_init(&simplify_sink);
     hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
             NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
-- 
2.14.1




More information about the wine-patches mailing list