 
            From: Bernhard Kölbl bkoelbl@codeweavers.com
This is done by adding up the area of all triangulated faces, using Gauss's area formula for a singular triangle. --- dlls/d2d1/geometry.c | 26 +++++++++++- dlls/d2d1/tests/d2d1.c | 95 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c index 3dba8c7dc0d..f36e611b811 100644 --- a/dlls/d2d1/geometry.c +++ b/dlls/d2d1/geometry.c @@ -3858,9 +3858,31 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_Outline(ID2D1PathGeometry1 *i static HRESULT STDMETHODCALLTYPE d2d_path_geometry_ComputeArea(ID2D1PathGeometry1 *iface, const D2D1_MATRIX_3X2_F *transform, float tolerance, float *area) { - FIXME("iface %p, transform %p, tolerance %.8e, area %p stub!\n", iface, transform, tolerance, area); + struct d2d_geometry *geometry = impl_from_ID2D1PathGeometry1(iface); + float result = 0.0f; + UINT32 i;
- return E_NOTIMPL; + TRACE("iface %p, transform %p, tolerance %.8e, area %p.\n", iface, transform, tolerance, area); + + for (i = 0; i < geometry->fill.face_count; ++i) + { + const struct d2d_face *face = &geometry->fill.faces[i]; + D2D1_POINT_2F a = geometry->fill.vertices[face->v[0]], + b = geometry->fill.vertices[face->v[1]], + c = geometry->fill.vertices[face->v[2]]; + + if (transform) + { + d2d_point_transform(&a, transform, a.x, a.y); + d2d_point_transform(&b, transform, b.x, b.y); + d2d_point_transform(&c, transform, c.x, c.y); + } + + result += fabs((a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y)); + } + + *area = result * .5f; + return S_OK; }
static HRESULT STDMETHODCALLTYPE d2d_path_geometry_ComputeLength(ID2D1PathGeometry1 *iface, diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 14a986b019c..4b1329d293f 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -15476,12 +15476,32 @@ static void test_effect_vertex_buffer(BOOL d3d11)
static void test_compute_geometry_area(BOOL d3d11) { + const D2D1_POINT_2F points_arrow[] = + { + { -200.0f, 50.0f }, + { -200.0f, 100.0f }, + { 200.0f, 100.0f }, + { 200.0f, 150.0f }, + { 300.0f, 75.0f }, + { 200.0f, 0.0f }, + { 200.0f, 50.0f }, + }; + const D2D1_POINT_2F points_square[] = + { + { -1.0f, -1.0f }, + { 1.0f, -1.0f }, + { 1.0f, 1.0f }, + { -1.0f, 1.0f }, + }; ID2D1TransformedGeometry *transformed_geometry; ID2D1RectangleGeometry *rectangle_geometry; ID2D1EllipseGeometry *ellipse_geometry; + ID2D1PathGeometry *path_geometry; struct d2d1_test_context ctx; D2D1_MATRIX_3X2_F matrix; + ID2D1GeometrySink *sink; D2D1_ELLIPSE ellipse; + D2D1_POINT_2F point; D2D1_RECT_F rect; HRESULT hr; float area; @@ -15596,6 +15616,81 @@ static void test_compute_geometry_area(BOOL d3d11) ID2D1TransformedGeometry_Release(transformed_geometry); ID2D1RectangleGeometry_Release(rectangle_geometry);
+ hr = ID2D1Factory_CreatePathGeometry(ctx.factory, &path_geometry); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = ID2D1PathGeometry_Open(path_geometry, &sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + set_point(&point, -1.0f, -1.0f); + ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_FILLED); + ID2D1GeometrySink_AddLines(sink, points_square, ARRAY_SIZE(points_square)); + ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED); + + hr = ID2D1GeometrySink_Close(sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ID2D1GeometrySink_Release(sink); + + hr = ID2D1PathGeometry_ComputeArea(path_geometry, NULL, 1.5f, &area); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(area == 4.0f, "Got area %f.\n", area); + + D2D1MakeSkewMatrix(45.0f , 45.0f, point, &matrix); + hr = ID2D1PathGeometry_ComputeArea(path_geometry, &matrix, 1.5f, &area); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(area == 0.0f, "Got area %f.\n", area); + + D2D1MakeSkewMatrix(30.0f , 30.0f, point, &matrix); + hr = ID2D1PathGeometry_ComputeArea(path_geometry, &matrix, 1.5f, &area); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(compare_float(area, 2.666667f, 3), "Got area %f.\n", area); + + ID2D1PathGeometry_Release(path_geometry); + + hr = ID2D1Factory_CreatePathGeometry(ctx.factory, &path_geometry); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = ID2D1PathGeometry_Open(path_geometry, &sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + set_point(&point, -200.0f, 50.0f); + ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_FILLED); + ID2D1GeometrySink_AddLines(sink, points_arrow, ARRAY_SIZE(points_arrow)); + ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED); + + hr = ID2D1GeometrySink_Close(sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ID2D1GeometrySink_Release(sink); + + hr = ID2D1PathGeometry_ComputeArea(path_geometry, NULL, 1.5f, &area); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(area == 27500.0f, "Got area %f.\n", area); + + D2D1MakeRotateMatrix(90.0f, point, &matrix); + hr = ID2D1PathGeometry_ComputeArea(path_geometry, NULL, 1.5f, &area); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(area == 27500.0f, "Got area %f.\n", area); + + ID2D1PathGeometry_Release(path_geometry); + + hr = ID2D1Factory_CreatePathGeometry(ctx.factory, &path_geometry); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = ID2D1PathGeometry_Open(path_geometry, &sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + set_point(&point, -200.0f, 50.0f); + ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW); + ID2D1GeometrySink_AddLines(sink, points_arrow, ARRAY_SIZE(points_arrow)); + ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED); + + hr = ID2D1GeometrySink_Close(sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ID2D1GeometrySink_Release(sink); + + hr = ID2D1PathGeometry_ComputeArea(path_geometry, NULL, 1.5f, &area); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(area == 0.0f, "Got area %f.\n", area); + + ID2D1PathGeometry_Release(path_geometry); + release_test_context(&ctx); }
 
            This should do basically an equivalent of Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES, tolerance) -> Tesselate -> return combined area of primitives.
 
            On Tue Oct 7 16:33:02 2025 +0000, Nikolay Sivov wrote:
This should do basically an equivalent of Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES, tolerance) -> Tesselate -> return combined area of primitives.
So I need to implement tessellation first?
 
            On Tue Oct 7 16:33:02 2025 +0000, Bernhard Kölbl wrote:
So I need to implement tessellation first?
That's the reason why I didn't send anything for ComputeArea() already. You don't necessarily need to implement the Tessellate() method, but my point is ignoring simplification stage is not going to give correct output. Basically area value should match line-simplified rendering.
 
            On Tue Oct 7 16:40:04 2025 +0000, Nikolay Sivov wrote:
That's the reason why I didn't send anything for ComputeArea() already. You don't necessarily need to implement the Tessellate() method, but my point is ignoring simplification stage is not going to give correct output. Basically area value should match line-simplified rendering.
The point here is to area values with some degree of accuracy, and not just some values that are analytically accurate for given shape. For example for polygons flattening tolerance argument will have no effect, because it's already nothing but line segments. But for any other shape it will matter, and we'll need it be consistent between rendering and properties like area values.
 
            Could you try this https://gitlab.winehq.org/nsivov/wine/-/commits/d2d_compute_area? I still have to do some visual testing before I can trust it, but for ellipses at least returned values are close.
 
            This is merged with !9241.
 
            This merge request was closed by Nikolay Sivov.


