[PATCH 5/5] d2d1: Implement d2d_rectangle_geometry_Simplify().
Henri Verbeet
hverbeet at codeweavers.com
Fri Jun 2 10:56:35 CDT 2017
Signed-off-by: Henri Verbeet <hverbeet at codeweavers.com>
---
dlls/d2d1/d2d1_private.h | 4 +
dlls/d2d1/geometry.c | 27 +++-
dlls/d2d1/tests/d2d1.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 375 insertions(+), 3 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index ee73cf6..adc2e29 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -30,6 +30,10 @@
#endif
#include "dwrite_2.h"
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+#endif
+
enum d2d_brush_type
{
D2D_BRUSH_TYPE_SOLID,
diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c
index d07f68e..ab1d3ba 100644
--- a/dlls/d2d1/geometry.c
+++ b/dlls/d2d1/geometry.c
@@ -2837,10 +2837,33 @@ static HRESULT STDMETHODCALLTYPE d2d_rectangle_geometry_Simplify(ID2D1RectangleG
D2D1_GEOMETRY_SIMPLIFICATION_OPTION option, const D2D1_MATRIX_3X2_F *transform, float tolerance,
ID2D1SimplifiedGeometrySink *sink)
{
- FIXME("iface %p, option %#x, transform %p, tolerance %.8e, sink %p stub!\n",
+ struct d2d_geometry *geometry = impl_from_ID2D1RectangleGeometry(iface);
+ D2D1_RECT_F *rect = &geometry->u.rectangle.rect;
+ D2D1_POINT_2F p[4];
+ unsigned int i;
+
+ TRACE("iface %p, option %#x, transform %p, tolerance %.8e, sink %p.\n",
iface, option, transform, tolerance, sink);
- return E_NOTIMPL;
+ d2d_point_set(&p[0], rect->left, rect->top);
+ d2d_point_set(&p[1], rect->right, rect->top);
+ d2d_point_set(&p[2], rect->right, rect->bottom);
+ d2d_point_set(&p[3], rect->left, rect->bottom);
+
+ if (transform)
+ {
+ for (i = 0; i < ARRAY_SIZE(p); ++i)
+ {
+ d2d_point_transform(&p[i], transform, p[i].x, p[i].y);
+ }
+ }
+
+ ID2D1SimplifiedGeometrySink_SetFillMode(sink, D2D1_FILL_MODE_ALTERNATE);
+ ID2D1SimplifiedGeometrySink_BeginFigure(sink, p[0], D2D1_FIGURE_BEGIN_FILLED);
+ ID2D1SimplifiedGeometrySink_AddLines(sink, &p[1], 3);
+ ID2D1SimplifiedGeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+ return S_OK;
}
static HRESULT STDMETHODCALLTYPE d2d_rectangle_geometry_Tessellate(ID2D1RectangleGeometry *iface,
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c
index 1387a05..7ad4d34 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -33,6 +33,47 @@ struct figure
unsigned int span_count;
};
+struct geometry_sink
+{
+ ID2D1SimplifiedGeometrySink ID2D1SimplifiedGeometrySink_iface;
+
+ struct geometry_figure
+ {
+ D2D1_FIGURE_BEGIN begin;
+ D2D1_FIGURE_END end;
+ D2D1_POINT_2F start_point;
+ struct geometry_segment
+ {
+ enum
+ {
+ SEGMENT_BEZIER,
+ SEGMENT_LINE,
+ } type;
+ union
+ {
+ D2D1_BEZIER_SEGMENT bezier;
+ D2D1_POINT_2F line;
+ } u;
+ } *segments;
+ unsigned int segments_size;
+ unsigned int segment_count;
+ } *figures;
+ unsigned int figures_size;
+ unsigned int figure_count;
+
+ D2D1_FILL_MODE fill_mode;
+ BOOL closed;
+};
+
+struct expected_geometry_figure
+{
+ D2D1_FIGURE_BEGIN begin;
+ D2D1_FIGURE_END end;
+ D2D1_POINT_2F start_point;
+ unsigned int segment_count;
+ const struct geometry_segment *segments;
+};
+
static void set_point(D2D1_POINT_2F *point, float x, float y)
{
point->x = x;
@@ -169,6 +210,12 @@ static BOOL compare_float(float f, float g, unsigned int ulps)
return TRUE;
}
+static BOOL compare_point(const D2D1_POINT_2F *point, float x, float y, unsigned int ulps)
+{
+ return compare_float(point->x, x, ulps)
+ && compare_float(point->y, y, ulps);
+}
+
static BOOL compare_rect(const D2D1_RECT_F *rect, float left, float top, float right, float bottom, unsigned int ulps)
{
return compare_float(rect->left, left, ulps)
@@ -177,6 +224,14 @@ static BOOL compare_rect(const D2D1_RECT_F *rect, float left, float top, float r
&& compare_float(rect->bottom, bottom, ulps);
}
+static BOOL compare_bezier_segment(const D2D1_BEZIER_SEGMENT *b, float x1, float y1,
+ float x2, float y2, float x3, float y3, unsigned int ulps)
+{
+ return compare_point(&b->point1, x1, y1, ulps)
+ && compare_point(&b->point2, x2, y2, ulps)
+ && compare_point(&b->point3, x3, y3, ulps);
+}
+
static BOOL compare_sha1(void *data, unsigned int pitch, unsigned int bpp,
unsigned int w, unsigned int h, const char *ref_sha1)
{
@@ -615,6 +670,164 @@ static ID2D1RenderTarget *create_render_target(IDXGISurface *surface)
return create_render_target_desc(surface, &desc);
}
+static inline struct geometry_sink *impl_from_ID2D1SimplifiedGeometrySink(ID2D1SimplifiedGeometrySink *iface)
+{
+ return CONTAINING_RECORD(iface, struct geometry_sink, ID2D1SimplifiedGeometrySink_iface);
+}
+
+static HRESULT STDMETHODCALLTYPE geometry_sink_QueryInterface(ID2D1SimplifiedGeometrySink *iface,
+ REFIID iid, void **out)
+{
+ if (IsEqualGUID(iid, &IID_ID2D1SimplifiedGeometrySink)
+ || IsEqualGUID(iid, &IID_IUnknown))
+ {
+ *out = iface;
+ return S_OK;
+ }
+
+ *out = NULL;
+ return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE geometry_sink_AddRef(ID2D1SimplifiedGeometrySink *iface)
+{
+ return 0;
+}
+
+static ULONG STDMETHODCALLTYPE geometry_sink_Release(ID2D1SimplifiedGeometrySink *iface)
+{
+ return 0;
+}
+
+static void STDMETHODCALLTYPE geometry_sink_SetFillMode(ID2D1SimplifiedGeometrySink *iface, D2D1_FILL_MODE mode)
+{
+ struct geometry_sink *sink = impl_from_ID2D1SimplifiedGeometrySink(iface);
+
+ sink->fill_mode = mode;
+}
+
+static void STDMETHODCALLTYPE geometry_sink_SetSegmentFlags(ID2D1SimplifiedGeometrySink *iface,
+ D2D1_PATH_SEGMENT flags)
+{
+ ok(0, "Got unexpected segment flags %#x.\n", flags);
+}
+
+static void STDMETHODCALLTYPE geometry_sink_BeginFigure(ID2D1SimplifiedGeometrySink *iface,
+ D2D1_POINT_2F start_point, D2D1_FIGURE_BEGIN figure_begin)
+{
+ struct geometry_sink *sink = impl_from_ID2D1SimplifiedGeometrySink(iface);
+ struct geometry_figure *figure;
+
+ if (sink->figure_count == sink->figures_size)
+ {
+ sink->figures_size *= 2;
+ sink->figures = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sink->figures,
+ sink->figures_size * sizeof(*sink->figures));
+ }
+ figure = &sink->figures[sink->figure_count++];
+
+ figure->begin = figure_begin;
+ figure->start_point = start_point;
+ figure->segments_size = 4;
+ figure->segments = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ figure->segments_size * sizeof(*figure->segments));
+}
+
+static struct geometry_segment *geometry_figure_add_segment(struct geometry_figure *figure)
+{
+ if (figure->segment_count == figure->segments_size)
+ {
+ figure->segments_size *= 2;
+ figure->segments = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, figure->segments,
+ figure->segments_size * sizeof(*figure->segments));
+ }
+ return &figure->segments[figure->segment_count++];
+}
+
+static void STDMETHODCALLTYPE geometry_sink_AddLines(ID2D1SimplifiedGeometrySink *iface,
+ const D2D1_POINT_2F *points, UINT32 count)
+{
+ struct geometry_sink *sink = impl_from_ID2D1SimplifiedGeometrySink(iface);
+ struct geometry_figure *figure = &sink->figures[sink->figure_count - 1];
+ struct geometry_segment *segment;
+ unsigned int i;
+
+ for (i = 0; i < count; ++i)
+ {
+ segment = geometry_figure_add_segment(figure);
+ segment->type = SEGMENT_LINE;
+ segment->u.line = points[i];
+ }
+}
+
+static void STDMETHODCALLTYPE geometry_sink_AddBeziers(ID2D1SimplifiedGeometrySink *iface,
+ const D2D1_BEZIER_SEGMENT *beziers, UINT32 count)
+{
+ struct geometry_sink *sink = impl_from_ID2D1SimplifiedGeometrySink(iface);
+ struct geometry_figure *figure = &sink->figures[sink->figure_count - 1];
+ struct geometry_segment *segment;
+ unsigned int i;
+
+ for (i = 0; i < count; ++i)
+ {
+ segment = geometry_figure_add_segment(figure);
+ segment->type = SEGMENT_BEZIER;
+ segment->u.bezier = beziers[i];
+ }
+}
+
+static void STDMETHODCALLTYPE geometry_sink_EndFigure(ID2D1SimplifiedGeometrySink *iface,
+ D2D1_FIGURE_END figure_end)
+{
+ struct geometry_sink *sink = impl_from_ID2D1SimplifiedGeometrySink(iface);
+ struct geometry_figure *figure = &sink->figures[sink->figure_count - 1];
+
+ figure->end = figure_end;
+}
+
+static HRESULT STDMETHODCALLTYPE geometry_sink_Close(ID2D1SimplifiedGeometrySink *iface)
+{
+ struct geometry_sink *sink = impl_from_ID2D1SimplifiedGeometrySink(iface);
+
+ sink->closed = TRUE;
+
+ return S_OK;
+}
+
+static const struct ID2D1SimplifiedGeometrySinkVtbl geometry_sink_vtbl =
+{
+ geometry_sink_QueryInterface,
+ geometry_sink_AddRef,
+ geometry_sink_Release,
+ geometry_sink_SetFillMode,
+ geometry_sink_SetSegmentFlags,
+ geometry_sink_BeginFigure,
+ geometry_sink_AddLines,
+ geometry_sink_AddBeziers,
+ geometry_sink_EndFigure,
+ geometry_sink_Close,
+};
+
+static void geometry_sink_init(struct geometry_sink *sink)
+{
+ memset(sink, 0, sizeof(*sink));
+ sink->ID2D1SimplifiedGeometrySink_iface.lpVtbl = &geometry_sink_vtbl;
+ sink->figures_size = 4;
+ sink->figures = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ sink->figures_size * sizeof(*sink->figures));
+}
+
+static void geometry_sink_cleanup(struct geometry_sink *sink)
+{
+ unsigned int i;
+
+ for (i = 0; i < sink->figure_count; ++i)
+ {
+ HeapFree(GetProcessHeap(), 0, sink->figures[i].segments);
+ }
+ HeapFree(GetProcessHeap(), 0, sink->figures);
+}
+
static void test_clip(void)
{
IDXGISwapChain *swapchain;
@@ -1836,10 +2049,85 @@ static void test_path_geometry(void)
DestroyWindow(window);
}
+#define geometry_sink_check(a, b, c, d, e) geometry_sink_check_(__LINE__, a, b, c, d, e)
+static void geometry_sink_check_(unsigned int line, const struct geometry_sink *sink, D2D1_FILL_MODE fill_mode,
+ unsigned int figure_count, const struct expected_geometry_figure *expected_figures, unsigned int ulps)
+{
+ const struct geometry_segment *segment, *expected_segment;
+ const struct expected_geometry_figure *expected_figure;
+ const struct geometry_figure *figure;
+ unsigned int i, j;
+ BOOL match;
+
+ ok_(__FILE__, line)(sink->fill_mode == D2D1_FILL_MODE_ALTERNATE,
+ "Got unexpected fill mode %#x.\n", sink->fill_mode);
+ ok_(__FILE__, line)(sink->figure_count == 1, "Got unexpected figure count %u.\n", sink->figure_count);
+ ok_(__FILE__, line)(!sink->closed, "Sink is closed.\n");
+
+ for (i = 0; i < figure_count; ++i)
+ {
+ expected_figure = &expected_figures[i];
+ figure = &sink->figures[i];
+
+ ok_(__FILE__, line)(figure->begin == expected_figure->begin,
+ "Got unexpected figure %u begin %#x, expected %#x.\n",
+ i, figure->begin, expected_figure->begin);
+ ok_(__FILE__, line)(figure->end == expected_figure->end,
+ "Got unexpected figure %u end %#x, expected %#x.\n",
+ i, figure->end, expected_figure->end);
+ match = compare_point(&figure->start_point,
+ expected_figure->start_point.x, expected_figure->start_point.y, ulps);
+ ok_(__FILE__, line)(match, "Got unexpected figure %u start point {%.8e, %.8e}, expected {%.8e, %.8e}.\n",
+ i, figure->start_point.x, figure->start_point.y,
+ expected_figure->start_point.x, expected_figure->start_point.y);
+ ok_(__FILE__, line)(figure->segment_count == expected_figure->segment_count,
+ "Got unexpected figure %u segment count %u, expected %u.\n",
+ i, figure->segment_count, expected_figure->segment_count);
+
+ for (j = 0; j < figure->segment_count; ++j)
+ {
+ expected_segment = &expected_figure->segments[j];
+ segment = &figure->segments[j];
+ ok_(__FILE__, line)(segment->type == expected_segment->type,
+ "Got unexpected figure %u, segment %u type %#x, expected %#x.\n",
+ i, j, segment->type, expected_segment->type);
+ switch (segment->type)
+ {
+ case SEGMENT_LINE:
+ match = compare_point(&segment->u.line,
+ expected_segment->u.line.x, expected_segment->u.line.y, ulps);
+ ok_(__FILE__, line)(match, "Got unexpected figure %u segment %u {%.8e, %.8e}, "
+ "expected {%.8e, %.8e}.\n",
+ i, j, segment->u.line.x, segment->u.line.y,
+ expected_segment->u.line.x, expected_segment->u.line.y);
+ break;
+
+ case SEGMENT_BEZIER:
+ match = compare_bezier_segment(&segment->u.bezier,
+ expected_segment->u.bezier.point1.x, expected_segment->u.bezier.point1.y,
+ expected_segment->u.bezier.point2.x, expected_segment->u.bezier.point2.y,
+ expected_segment->u.bezier.point3.x, expected_segment->u.bezier.point3.y,
+ ulps);
+ ok_(__FILE__, line)(match, "Got unexpected figure %u segment %u "
+ "{%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}, "
+ "expected {%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}.\n",
+ i, j, segment->u.bezier.point1.x, segment->u.bezier.point1.y,
+ segment->u.bezier.point2.x, segment->u.bezier.point2.y,
+ segment->u.bezier.point3.x, segment->u.bezier.point3.y,
+ expected_segment->u.bezier.point1.x, expected_segment->u.bezier.point1.y,
+ expected_segment->u.bezier.point2.x, expected_segment->u.bezier.point2.y,
+ expected_segment->u.bezier.point3.x, expected_segment->u.bezier.point3.y);
+ break;
+ }
+ }
+ }
+}
+
static void test_rectangle_geometry(void)
{
ID2D1TransformedGeometry *transformed_geometry;
ID2D1RectangleGeometry *geometry;
+ struct geometry_sink sink;
D2D1_MATRIX_3X2_F matrix;
D2D1_RECT_F rect, rect2;
ID2D1Factory *factory;
@@ -1848,6 +2136,33 @@ static void test_rectangle_geometry(void)
HRESULT hr;
BOOL match;
+ static const struct geometry_segment expected_segments[] =
+ {
+ /* Figure 0. */
+ {SEGMENT_LINE, {{{10.0f, 0.0f}}}},
+ {SEGMENT_LINE, {{{10.0f, 20.0f}}}},
+ {SEGMENT_LINE, {{{ 0.0f, 20.0f}}}},
+ /* Figure 1. */
+ {SEGMENT_LINE, {{{4.42705116e+01f, 1.82442951e+01f}}}},
+ {SEGMENT_LINE, {{{7.95376282e+01f, 5.06049728e+01f}}}},
+ {SEGMENT_LINE, {{{5.52671127e+01f, 6.23606796e+01f}}}},
+ /* Figure 2. */
+ {SEGMENT_LINE, {{{25.0f, 15.0f}}}},
+ {SEGMENT_LINE, {{{25.0f, 55.0f}}}},
+ {SEGMENT_LINE, {{{25.0f, 55.0f}}}},
+ /* Figure 3. */
+ {SEGMENT_LINE, {{{35.0f, 45.0f}}}},
+ {SEGMENT_LINE, {{{35.0f, 45.0f}}}},
+ {SEGMENT_LINE, {{{30.0f, 45.0f}}}},
+ };
+ static const struct expected_geometry_figure expected_figures[] =
+ {
+ {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, { 0.0f, 0.0f}, 3, &expected_segments[0]},
+ {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {20.0f, 30.0f}, 3, &expected_segments[3]},
+ {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {25.0f, 15.0f}, 3, &expected_segments[6]},
+ {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {30.0f, 45.0f}, 3, &expected_segments[9]},
+ };
+
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &IID_ID2D1Factory, NULL, (void **)&factory);
ok(SUCCEEDED(hr), "Failed to create factory, hr %#x.\n", hr);
@@ -1930,12 +2245,24 @@ 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);
- /* Test GetBounds(). */
+ /* Test GetBounds() and Simplify(). */
hr = ID2D1RectangleGeometry_GetBounds(geometry, NULL, &rect);
ok(SUCCEEDED(hr), "Failed to get bounds.\n");
match = compare_rect(&rect, 0.0f, 0.0f, 10.0f, 20.0f, 0);
ok(match, "Got unexpected bounds {%.8e, %.8e, %.8e, %.8e}.\n",
rect.left, rect.top, rect.right, rect.bottom);
+ geometry_sink_init(&sink);
+ hr = ID2D1RectangleGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ NULL, 0.0f, &sink.ID2D1SimplifiedGeometrySink_iface);
+ ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+ geometry_sink_check(&sink, D2D1_FILL_MODE_ALTERNATE, 1, &expected_figures[0], 0);
+ geometry_sink_cleanup(&sink);
+ geometry_sink_init(&sink);
+ hr = ID2D1RectangleGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+ NULL, 0.0f, &sink.ID2D1SimplifiedGeometrySink_iface);
+ ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+ geometry_sink_check(&sink, D2D1_FILL_MODE_ALTERNATE, 1, &expected_figures[0], 0);
+ geometry_sink_cleanup(&sink);
set_matrix_identity(&matrix);
translate_matrix(&matrix, 20.0f, 30.0f);
@@ -1946,6 +2273,12 @@ static void test_rectangle_geometry(void)
match = compare_rect(&rect, 2.00000000e+01f, 1.82442951e+01f, 7.95376282e+01f, 6.23606796e+01f, 0);
ok(match, "Got unexpected bounds {%.8e, %.8e, %.8e, %.8e}.\n",
rect.left, rect.top, rect.right, rect.bottom);
+ geometry_sink_init(&sink);
+ hr = ID2D1RectangleGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ &matrix, 0.0f, &sink.ID2D1SimplifiedGeometrySink_iface);
+ ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+ geometry_sink_check(&sink, D2D1_FILL_MODE_ALTERNATE, 1, &expected_figures[1], 1);
+ geometry_sink_cleanup(&sink);
set_matrix_identity(&matrix);
translate_matrix(&matrix, 25.0f, 15.0f);
@@ -1955,6 +2288,12 @@ static void test_rectangle_geometry(void)
match = compare_rect(&rect, 25.0f, 15.0f, 25.0f, 55.0f, 0);
ok(match, "Got unexpected bounds {%.8e, %.8e, %.8e, %.8e}.\n",
rect.left, rect.top, rect.right, rect.bottom);
+ geometry_sink_init(&sink);
+ hr = ID2D1RectangleGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+ &matrix, 0.0f, &sink.ID2D1SimplifiedGeometrySink_iface);
+ ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+ geometry_sink_check(&sink, D2D1_FILL_MODE_ALTERNATE, 1, &expected_figures[2], 0);
+ geometry_sink_cleanup(&sink);
set_matrix_identity(&matrix);
translate_matrix(&matrix, 30.0f, 45.0f);
@@ -1964,6 +2303,12 @@ static void test_rectangle_geometry(void)
match = compare_rect(&rect, 30.0f, 45.0f, 35.0f, 45.0f, 0);
ok(match, "Got unexpected bounds {%.8e, %.8e, %.8e, %.8e}.\n",
rect.left, rect.top, rect.right, rect.bottom);
+ geometry_sink_init(&sink);
+ hr = ID2D1RectangleGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+ &matrix, 0.0f, &sink.ID2D1SimplifiedGeometrySink_iface);
+ ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+ geometry_sink_check(&sink, D2D1_FILL_MODE_ALTERNATE, 1, &expected_figures[3], 0);
+ geometry_sink_cleanup(&sink);
set_matrix_identity(&matrix);
scale_matrix(&matrix, 4.0f, 5.0f);
--
2.1.4
More information about the wine-patches
mailing list