Currently rounded parts are approximated with quadratic Bézier curves.
Signed-off-by: Giovanni Mascellani gio@debian.org --- dlls/d2d1/d2d1_private.h | 13 ++ dlls/d2d1/factory.c | 22 ++- dlls/d2d1/geometry.c | 310 +++++++++++++++++++++++++++++++++++++++ dlls/d2d1/tests/d2d1.c | 1 - 4 files changed, 342 insertions(+), 4 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index 2e42d6d550..2c0a14a71f 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -483,6 +483,10 @@ struct d2d_geometry D2D1_RECT_F rect; } rectangle; struct + { + D2D1_ROUNDED_RECT rounded_rect; + } rounded_rectangle; + struct { ID2D1Geometry *src_geometry; D2D_MATRIX_3X2_F transform; @@ -499,6 +503,8 @@ struct d2d_geometry void d2d_path_geometry_init(struct d2d_geometry *geometry, ID2D1Factory *factory) DECLSPEC_HIDDEN; HRESULT d2d_rectangle_geometry_init(struct d2d_geometry *geometry, ID2D1Factory *factory, const D2D1_RECT_F *rect) DECLSPEC_HIDDEN; +HRESULT d2d_rounded_rectangle_geometry_init(struct d2d_geometry *geometry, + ID2D1Factory *factory, const D2D1_ROUNDED_RECT *rounded_rect) DECLSPEC_HIDDEN; void d2d_transformed_geometry_init(struct d2d_geometry *geometry, ID2D1Factory *factory, ID2D1Geometry *src_geometry, const D2D_MATRIX_3X2_F *transform) DECLSPEC_HIDDEN; HRESULT d2d_geometry_group_init(struct d2d_geometry *geometry, ID2D1Factory *factory, @@ -620,4 +626,11 @@ static inline const char *debug_d2d_rect_f(const D2D1_RECT_F *rect) return wine_dbg_sprintf("(%.8e,%.8e)-(%.8e,%.8e)", rect->left, rect->top, rect->right, rect->bottom ); }
+static inline const char *debug_d2d_rounded_rect(const D2D1_ROUNDED_RECT *rounded_rect) +{ + if (!rounded_rect) return "(null)"; + return wine_dbg_sprintf("(%.8e,%.8e)-(%.8e,%.8e)[%.8e,%.8e]", rounded_rect->rect.left, rounded_rect->rect.top, + rounded_rect->rect.right, rounded_rect->rect.bottom, rounded_rect->radiusX, rounded_rect->radiusY ); +} + #endif /* __WINE_D2D1_PRIVATE_H */ diff --git a/dlls/d2d1/factory.c b/dlls/d2d1/factory.c index 48a2efb96b..86284f4ae4 100644 --- a/dlls/d2d1/factory.c +++ b/dlls/d2d1/factory.c @@ -152,11 +152,27 @@ static HRESULT STDMETHODCALLTYPE d2d_factory_CreateRectangleGeometry(ID2D1Factor }
static HRESULT STDMETHODCALLTYPE d2d_factory_CreateRoundedRectangleGeometry(ID2D1Factory2 *iface, - const D2D1_ROUNDED_RECT *rect, ID2D1RoundedRectangleGeometry **geometry) + const D2D1_ROUNDED_RECT *rounded_rect, ID2D1RoundedRectangleGeometry **geometry) { - FIXME("iface %p, rect %p, geometry %p stub!\n", iface, rect, geometry); + struct d2d_geometry *object; + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, rounded_rect %s, geometry %p.\n", iface, debug_d2d_rounded_rect(rounded_rect), geometry); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = d2d_rounded_rectangle_geometry_init(object, (ID2D1Factory *)iface, rounded_rect))) + { + WARN("Failed to initialize rounded rectangle geometry, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + TRACE("Created rounded rectangle geometry %p.\n", object); + *geometry = (ID2D1RoundedRectangleGeometry *)&object->ID2D1Geometry_iface; + + return S_OK; }
static HRESULT STDMETHODCALLTYPE d2d_factory_CreateEllipseGeometry(ID2D1Factory2 *iface, diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c index 6697eccee4..072d360590 100644 --- a/dlls/d2d1/geometry.c +++ b/dlls/d2d1/geometry.c @@ -2257,6 +2257,14 @@ static BOOL d2d_geometry_outline_add_bezier_segment(struct d2d_geometry *geometr return TRUE; }
+static BOOL d2d_geometry_outline_add_arc_quadrant(struct d2d_geometry *geometry, + const D2D1_POINT_2F *p0, const D2D1_POINT_2F *p1, const D2D1_POINT_2F *p2) +{ + FIXME("Approximating arc quadrant with Bezier curve.\n"); + + return d2d_geometry_outline_add_bezier_segment(geometry, p0, p1, p2); +} + static BOOL d2d_geometry_add_figure_outline(struct d2d_geometry *geometry, struct d2d_figure *figure, D2D1_FIGURE_END figure_end) { @@ -3840,6 +3848,307 @@ fail: return E_OUTOFMEMORY; }
+static inline struct d2d_geometry *impl_from_ID2D1RoundedRectangleGeometry(ID2D1RoundedRectangleGeometry *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_geometry, ID2D1Geometry_iface); +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_QueryInterface(ID2D1RoundedRectangleGeometry *iface, + REFIID iid, void **out) +{ + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_ID2D1RoundedRectangleGeometry) + || IsEqualGUID(iid, &IID_ID2D1Geometry) + || IsEqualGUID(iid, &IID_ID2D1Resource) + || IsEqualGUID(iid, &IID_IUnknown)) + { + ID2D1RoundedRectangleGeometry_AddRef(iface); + *out = iface; + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_AddRef(ID2D1RoundedRectangleGeometry *iface) +{ + struct d2d_geometry *geometry = impl_from_ID2D1RoundedRectangleGeometry(iface); + ULONG refcount = InterlockedIncrement(&geometry->refcount); + + TRACE("%p increasing refcount to %u.\n", iface, refcount); + + return refcount; +} + +static ULONG STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_Release(ID2D1RoundedRectangleGeometry *iface) +{ + struct d2d_geometry *geometry = impl_from_ID2D1RoundedRectangleGeometry(iface); + ULONG refcount = InterlockedDecrement(&geometry->refcount); + + TRACE("%p decreasing refcount to %u.\n", iface, refcount); + + if (!refcount) + { + d2d_geometry_cleanup(geometry); + heap_free(geometry); + } + + return refcount; +} + +static void STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_GetFactory(ID2D1RoundedRectangleGeometry *iface, ID2D1Factory **factory) +{ + struct d2d_geometry *geometry = impl_from_ID2D1RoundedRectangleGeometry(iface); + + TRACE("iface %p, factory %p.\n", iface, factory); + + ID2D1Factory_AddRef(*factory = geometry->factory); +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_GetBounds(ID2D1RoundedRectangleGeometry *iface, + const D2D1_MATRIX_3X2_F *transform, D2D1_RECT_F *bounds) +{ + FIXME("iface %p, transform %p, bounds %p stub!\n", iface, transform, bounds); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_GetWidenedBounds(ID2D1RoundedRectangleGeometry *iface, + float stroke_width, ID2D1StrokeStyle *stroke_style, const D2D1_MATRIX_3X2_F *transform, + float tolerance, D2D1_RECT_F *bounds) +{ + FIXME("iface %p, stroke_width %.8e, stroke_style %p, transform %p, tolerance %.8e, bounds %p stub!\n", + iface, stroke_width, stroke_style, transform, tolerance, bounds); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_StrokeContainsPoint(ID2D1RoundedRectangleGeometry *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 %s, stroke_width %.8e, stroke_style %p, transform %p, tolerance %.8e, contains %p stub!\n", + iface, debug_d2d_point_2f(&point), stroke_width, stroke_style, transform, tolerance, contains); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_FillContainsPoint(ID2D1RoundedRectangleGeometry *iface, + D2D1_POINT_2F point, const D2D1_MATRIX_3X2_F *transform, float tolerance, BOOL *contains) +{ + FIXME("iface %p, point %s, transform %p, tolerance %.8e, contains %p stub!\n", + iface, debug_d2d_point_2f(&point), transform, tolerance, contains); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_CompareWithGeometry(ID2D1RoundedRectangleGeometry *iface, + ID2D1Geometry *geometry, const D2D1_MATRIX_3X2_F *transform, float tolerance, D2D1_GEOMETRY_RELATION *relation) +{ + FIXME("iface %p, geometry %p, transform %p, tolerance %.8e, relation %p stub!\n", + iface, geometry, transform, tolerance, relation); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_Simplify(ID2D1RoundedRectangleGeometry *iface, + 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", + iface, option, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_Tessellate(ID2D1RoundedRectangleGeometry *iface, + const D2D1_MATRIX_3X2_F *transform, float tolerance, ID2D1TessellationSink *sink) +{ + FIXME("iface %p, transform %p, tolerance %.8e, sink %p stub!\n", iface, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_CombineWithGeometry(ID2D1RoundedRectangleGeometry *iface, + ID2D1Geometry *geometry, D2D1_COMBINE_MODE combine_mode, const D2D1_MATRIX_3X2_F *transform, + float tolerance, ID2D1SimplifiedGeometrySink *sink) +{ + FIXME("iface %p, geometry %p, combine_mode %#x, transform %p, tolerance %.8e, sink %p stub!\n", + iface, geometry, combine_mode, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_Outline(ID2D1RoundedRectangleGeometry *iface, + const D2D1_MATRIX_3X2_F *transform, float tolerance, ID2D1SimplifiedGeometrySink *sink) +{ + FIXME("iface %p, transform %p, tolerance %.8e, sink %p stub!\n", iface, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_ComputeArea(ID2D1RoundedRectangleGeometry *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); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_ComputeLength(ID2D1RoundedRectangleGeometry *iface, + const D2D1_MATRIX_3X2_F *transform, float tolerance, float *length) +{ + FIXME("iface %p, transform %p, tolerance %.8e, length %p stub!\n", iface, transform, tolerance, length); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_ComputePointAtLength(ID2D1RoundedRectangleGeometry *iface, + float length, const D2D1_MATRIX_3X2_F *transform, float tolerance, D2D1_POINT_2F *point, + D2D1_POINT_2F *tangent) +{ + FIXME("iface %p, length %.8e, transform %p, tolerance %.8e, point %p, tangent %p stub!\n", + iface, length, transform, tolerance, point, tangent); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_Widen(ID2D1RoundedRectangleGeometry *iface, float stroke_width, + ID2D1StrokeStyle *stroke_style, const D2D1_MATRIX_3X2_F *transform, float tolerance, + ID2D1SimplifiedGeometrySink *sink) +{ + FIXME("iface %p, stroke_width %.8e, stroke_style %p, transform %p, tolerance %.8e, sink %p stub!\n", + iface, stroke_width, stroke_style, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static void STDMETHODCALLTYPE d2d_rounded_rectangle_geometry_GetRoundedRect(ID2D1RoundedRectangleGeometry *iface, D2D1_ROUNDED_RECT *rounded_rect) +{ + struct d2d_geometry *geometry = impl_from_ID2D1RoundedRectangleGeometry(iface); + + TRACE("iface %p, rounded_rect %p.\n", iface, rounded_rect); + + *rounded_rect = geometry->u.rounded_rectangle.rounded_rect; +} + +static const struct ID2D1RoundedRectangleGeometryVtbl d2d_rounded_rectangle_geometry_vtbl = +{ + d2d_rounded_rectangle_geometry_QueryInterface, + d2d_rounded_rectangle_geometry_AddRef, + d2d_rounded_rectangle_geometry_Release, + d2d_rounded_rectangle_geometry_GetFactory, + d2d_rounded_rectangle_geometry_GetBounds, + d2d_rounded_rectangle_geometry_GetWidenedBounds, + d2d_rounded_rectangle_geometry_StrokeContainsPoint, + d2d_rounded_rectangle_geometry_FillContainsPoint, + d2d_rounded_rectangle_geometry_CompareWithGeometry, + d2d_rounded_rectangle_geometry_Simplify, + d2d_rounded_rectangle_geometry_Tessellate, + d2d_rounded_rectangle_geometry_CombineWithGeometry, + d2d_rounded_rectangle_geometry_Outline, + d2d_rounded_rectangle_geometry_ComputeArea, + d2d_rounded_rectangle_geometry_ComputeLength, + d2d_rounded_rectangle_geometry_ComputePointAtLength, + d2d_rounded_rectangle_geometry_Widen, + d2d_rounded_rectangle_geometry_GetRoundedRect, +}; + +HRESULT d2d_rounded_rectangle_geometry_init(struct d2d_geometry *geometry, ID2D1Factory *factory, const D2D1_ROUNDED_RECT *rounded_rect) +{ + struct d2d_face *f; + struct d2d_bezier_vertex *bv; + D2D1_POINT_2F *v, v1, v2, v3, v4; + float l, r, t, b; + float rX, rY; + + d2d_geometry_init(geometry, factory, &identity, (ID2D1GeometryVtbl *)&d2d_rounded_rectangle_geometry_vtbl); + geometry->u.rounded_rectangle.rounded_rect = *rounded_rect; + + if (!(geometry->fill.vertices = heap_alloc(8 * sizeof(*geometry->fill.vertices)))) + goto fail; + if (!d2d_array_reserve((void **)&geometry->fill.faces, + &geometry->fill.faces_size, 6, sizeof(*geometry->fill.faces))) + goto fail; + if (!(geometry->fill.bezier_vertices = heap_alloc(12 * sizeof(*geometry->fill.bezier_vertices)))) + goto fail; + + l = min(rounded_rect->rect.left, rounded_rect->rect.right); + r = max(rounded_rect->rect.left, rounded_rect->rect.right); + t = min(rounded_rect->rect.top, rounded_rect->rect.bottom); + b = max(rounded_rect->rect.top, rounded_rect->rect.bottom); + + rX = min(rounded_rect->radiusX, 0.5f*(r-l)); + rY = min(rounded_rect->radiusY, 0.5f*(b-t)); + + d2d_point_set(&v1, r, t); + d2d_point_set(&v2, r, b); + d2d_point_set(&v3, l, b); + d2d_point_set(&v4, l, t); + + v = geometry->fill.vertices; + d2d_point_set(&v[0], l+rX, t); + d2d_point_set(&v[1], r-rX, t); + d2d_point_set(&v[2], r, t+rY); + d2d_point_set(&v[3], r, b-rY); + d2d_point_set(&v[4], r-rX, b); + d2d_point_set(&v[5], l+rX, b); + d2d_point_set(&v[6], l, b-rY); + d2d_point_set(&v[7], l, t+rY); + geometry->fill.vertex_count = 8; + + f = geometry->fill.faces; + d2d_face_set(&f[0], 0, 7, 6); + d2d_face_set(&f[1], 0, 6, 5); + d2d_face_set(&f[2], 0, 5, 4); + d2d_face_set(&f[3], 0, 4, 1); + d2d_face_set(&f[4], 1, 4, 3); + d2d_face_set(&f[5], 1, 3, 2); + geometry->fill.face_count = 6; + + bv = geometry->fill.bezier_vertices; + d2d_bezier_vertex_set(&bv[0], &v[1], 0.0f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[1], &v1, 0.5f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[2], &v[2], 1.0f, 1.0f, -1.0f); + d2d_bezier_vertex_set(&bv[3], &v[3], 0.0f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[4], &v2, 0.5f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[5], &v[4], 1.0f, 1.0f, -1.0f); + d2d_bezier_vertex_set(&bv[6], &v[5], 0.0f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[7], &v3, 0.5f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[8], &v[6], 1.0f, 1.0f, -1.0f); + d2d_bezier_vertex_set(&bv[9], &v[7], 0.0f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[10], &v4, 0.5f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[11], &v[0], 1.0f, 1.0f, -1.0f); + geometry->fill.bezier_vertex_count = 12; + + if (!d2d_geometry_outline_add_line_segment(geometry, &v[0], &v[1])) + goto fail; + if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[1], &v1, &v[2])) + goto fail; + if (!d2d_geometry_outline_add_line_segment(geometry, &v[2], &v[3])) + goto fail; + if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[3], &v2, &v[4])) + goto fail; + if (!d2d_geometry_outline_add_line_segment(geometry, &v[4], &v[5])) + goto fail; + if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[5], &v3, &v[6])) + goto fail; + if (!d2d_geometry_outline_add_line_segment(geometry, &v[6], &v[7])) + goto fail; + if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[7], &v4, &v[0])) + goto fail; + + return S_OK; + +fail: + d2d_geometry_cleanup(geometry); + return E_OUTOFMEMORY; +} + static inline struct d2d_geometry *impl_from_ID2D1TransformedGeometry(ID2D1TransformedGeometry *iface) { return CONTAINING_RECORD(iface, struct d2d_geometry, ID2D1Geometry_iface); @@ -4385,6 +4694,7 @@ struct d2d_geometry *unsafe_impl_from_ID2D1Geometry(ID2D1Geometry *iface) return NULL; assert(iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_path_geometry_vtbl || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_rectangle_geometry_vtbl + || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_rounded_rectangle_geometry_vtbl || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_transformed_geometry_vtbl || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_geometry_group_vtbl); return CONTAINING_RECORD(iface, struct d2d_geometry, ID2D1Geometry_iface); diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 5377c4ea34..d2d59dbcba 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -3790,7 +3790,6 @@ static void test_rounded_rectangle_geometry(void)
set_rounded_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); hr = ID2D1Factory_CreateRoundedRectangleGeometry(factory, &rect, &geometry); -todo_wine ok(SUCCEEDED(hr), "Failed to create geometry, hr %#x.\n", hr); if (FAILED(hr)) {
Currently the ellipse is approximated with quadratic Bézier curves.
Signed-off-by: Giovanni Mascellani gio@debian.org --- dlls/d2d1/d2d1_private.h | 12 ++ dlls/d2d1/factory.c | 20 ++- dlls/d2d1/geometry.c | 282 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 312 insertions(+), 2 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index 2c0a14a71f..1b825cd65a 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -487,6 +487,10 @@ struct d2d_geometry D2D1_ROUNDED_RECT rounded_rect; } rounded_rectangle; struct + { + D2D1_ELLIPSE ellipse; + } ellipse; + struct { ID2D1Geometry *src_geometry; D2D_MATRIX_3X2_F transform; @@ -505,6 +509,8 @@ HRESULT d2d_rectangle_geometry_init(struct d2d_geometry *geometry, ID2D1Factory *factory, const D2D1_RECT_F *rect) DECLSPEC_HIDDEN; HRESULT d2d_rounded_rectangle_geometry_init(struct d2d_geometry *geometry, ID2D1Factory *factory, const D2D1_ROUNDED_RECT *rounded_rect) DECLSPEC_HIDDEN; +HRESULT d2d_ellipse_geometry_init(struct d2d_geometry *geometry, + ID2D1Factory *factory, const D2D1_ELLIPSE *ellipse) DECLSPEC_HIDDEN; void d2d_transformed_geometry_init(struct d2d_geometry *geometry, ID2D1Factory *factory, ID2D1Geometry *src_geometry, const D2D_MATRIX_3X2_F *transform) DECLSPEC_HIDDEN; HRESULT d2d_geometry_group_init(struct d2d_geometry *geometry, ID2D1Factory *factory, @@ -633,4 +639,10 @@ static inline const char *debug_d2d_rounded_rect(const D2D1_ROUNDED_RECT *rounde rounded_rect->rect.right, rounded_rect->rect.bottom, rounded_rect->radiusX, rounded_rect->radiusY ); }
+static inline const char *debug_d2d_ellipse(const D2D1_ELLIPSE *ellipse) +{ + if (!ellipse) return "(null)"; + return wine_dbg_sprintf("(%.8e,%.8e)[%.8e,%.8e]", ellipse->point.x, ellipse->point.y, ellipse->radiusX, ellipse->radiusY ); +} + #endif /* __WINE_D2D1_PRIVATE_H */ diff --git a/dlls/d2d1/factory.c b/dlls/d2d1/factory.c index 86284f4ae4..9fb7d05cca 100644 --- a/dlls/d2d1/factory.c +++ b/dlls/d2d1/factory.c @@ -178,9 +178,25 @@ static HRESULT STDMETHODCALLTYPE d2d_factory_CreateRoundedRectangleGeometry(ID2D static HRESULT STDMETHODCALLTYPE d2d_factory_CreateEllipseGeometry(ID2D1Factory2 *iface, const D2D1_ELLIPSE *ellipse, ID2D1EllipseGeometry **geometry) { - FIXME("iface %p, ellipse %p, geometry %p stub!\n", iface, ellipse, geometry); + struct d2d_geometry *object; + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, ellipse %s, geometry %p.\n", iface, debug_d2d_ellipse(ellipse), geometry); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = d2d_ellipse_geometry_init(object, (ID2D1Factory *)iface, ellipse))) + { + WARN("Failed to initialize ellipse geometry, hr %#x.\n", hr); + heap_free(object); + return hr; + } + + TRACE("Created rounded ellipse %p.\n", object); + *geometry = (ID2D1EllipseGeometry *)&object->ID2D1Geometry_iface; + + return S_OK; }
static HRESULT STDMETHODCALLTYPE d2d_factory_CreateGeometryGroup(ID2D1Factory2 *iface, diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c index 072d360590..c3e2862a53 100644 --- a/dlls/d2d1/geometry.c +++ b/dlls/d2d1/geometry.c @@ -4149,6 +4149,287 @@ fail: return E_OUTOFMEMORY; }
+static inline struct d2d_geometry *impl_from_ID2D1EllipseGeometry(ID2D1EllipseGeometry *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_geometry, ID2D1Geometry_iface); +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_QueryInterface(ID2D1EllipseGeometry *iface, + REFIID iid, void **out) +{ + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_ID2D1EllipseGeometry) + || IsEqualGUID(iid, &IID_ID2D1Geometry) + || IsEqualGUID(iid, &IID_ID2D1Resource) + || IsEqualGUID(iid, &IID_IUnknown)) + { + ID2D1EllipseGeometry_AddRef(iface); + *out = iface; + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE d2d_ellipse_geometry_AddRef(ID2D1EllipseGeometry *iface) +{ + struct d2d_geometry *geometry = impl_from_ID2D1EllipseGeometry(iface); + ULONG refcount = InterlockedIncrement(&geometry->refcount); + + TRACE("%p increasing refcount to %u.\n", iface, refcount); + + return refcount; +} + +static ULONG STDMETHODCALLTYPE d2d_ellipse_geometry_Release(ID2D1EllipseGeometry *iface) +{ + struct d2d_geometry *geometry = impl_from_ID2D1EllipseGeometry(iface); + ULONG refcount = InterlockedDecrement(&geometry->refcount); + + TRACE("%p decreasing refcount to %u.\n", iface, refcount); + + if (!refcount) + { + d2d_geometry_cleanup(geometry); + heap_free(geometry); + } + + return refcount; +} + +static void STDMETHODCALLTYPE d2d_ellipse_geometry_GetFactory(ID2D1EllipseGeometry *iface, ID2D1Factory **factory) +{ + struct d2d_geometry *geometry = impl_from_ID2D1EllipseGeometry(iface); + + TRACE("iface %p, factory %p.\n", iface, factory); + + ID2D1Factory_AddRef(*factory = geometry->factory); +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_GetBounds(ID2D1EllipseGeometry *iface, + const D2D1_MATRIX_3X2_F *transform, D2D1_RECT_F *bounds) +{ + FIXME("iface %p, transform %p, bounds %p stub!\n", iface, transform, bounds); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_GetWidenedBounds(ID2D1EllipseGeometry *iface, + float stroke_width, ID2D1StrokeStyle *stroke_style, const D2D1_MATRIX_3X2_F *transform, + float tolerance, D2D1_RECT_F *bounds) +{ + FIXME("iface %p, stroke_width %.8e, stroke_style %p, transform %p, tolerance %.8e, bounds %p stub!\n", + iface, stroke_width, stroke_style, transform, tolerance, bounds); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_StrokeContainsPoint(ID2D1EllipseGeometry *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 %s, stroke_width %.8e, stroke_style %p, transform %p, tolerance %.8e, contains %p stub!\n", + iface, debug_d2d_point_2f(&point), stroke_width, stroke_style, transform, tolerance, contains); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_FillContainsPoint(ID2D1EllipseGeometry *iface, + D2D1_POINT_2F point, const D2D1_MATRIX_3X2_F *transform, float tolerance, BOOL *contains) +{ + FIXME("iface %p, point %s, transform %p, tolerance %.8e, contains %p stub!\n", + iface, debug_d2d_point_2f(&point), transform, tolerance, contains); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_CompareWithGeometry(ID2D1EllipseGeometry *iface, + ID2D1Geometry *geometry, const D2D1_MATRIX_3X2_F *transform, float tolerance, D2D1_GEOMETRY_RELATION *relation) +{ + FIXME("iface %p, geometry %p, transform %p, tolerance %.8e, relation %p stub!\n", + iface, geometry, transform, tolerance, relation); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_Simplify(ID2D1EllipseGeometry *iface, + 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", + iface, option, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_Tessellate(ID2D1EllipseGeometry *iface, + const D2D1_MATRIX_3X2_F *transform, float tolerance, ID2D1TessellationSink *sink) +{ + FIXME("iface %p, transform %p, tolerance %.8e, sink %p stub!\n", iface, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_CombineWithGeometry(ID2D1EllipseGeometry *iface, + ID2D1Geometry *geometry, D2D1_COMBINE_MODE combine_mode, const D2D1_MATRIX_3X2_F *transform, + float tolerance, ID2D1SimplifiedGeometrySink *sink) +{ + FIXME("iface %p, geometry %p, combine_mode %#x, transform %p, tolerance %.8e, sink %p stub!\n", + iface, geometry, combine_mode, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_Outline(ID2D1EllipseGeometry *iface, + const D2D1_MATRIX_3X2_F *transform, float tolerance, ID2D1SimplifiedGeometrySink *sink) +{ + FIXME("iface %p, transform %p, tolerance %.8e, sink %p stub!\n", iface, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_ComputeArea(ID2D1EllipseGeometry *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); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_ComputeLength(ID2D1EllipseGeometry *iface, + const D2D1_MATRIX_3X2_F *transform, float tolerance, float *length) +{ + FIXME("iface %p, transform %p, tolerance %.8e, length %p stub!\n", iface, transform, tolerance, length); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_ComputePointAtLength(ID2D1EllipseGeometry *iface, + float length, const D2D1_MATRIX_3X2_F *transform, float tolerance, D2D1_POINT_2F *point, + D2D1_POINT_2F *tangent) +{ + FIXME("iface %p, length %.8e, transform %p, tolerance %.8e, point %p, tangent %p stub!\n", + iface, length, transform, tolerance, point, tangent); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_ellipse_geometry_Widen(ID2D1EllipseGeometry *iface, float stroke_width, + ID2D1StrokeStyle *stroke_style, const D2D1_MATRIX_3X2_F *transform, float tolerance, + ID2D1SimplifiedGeometrySink *sink) +{ + FIXME("iface %p, stroke_width %.8e, stroke_style %p, transform %p, tolerance %.8e, sink %p stub!\n", + iface, stroke_width, stroke_style, transform, tolerance, sink); + + return E_NOTIMPL; +} + +static void STDMETHODCALLTYPE d2d_ellipse_geometry_GetRoundedRect(ID2D1EllipseGeometry *iface, D2D1_ELLIPSE *ellipse) +{ + struct d2d_geometry *geometry = impl_from_ID2D1EllipseGeometry(iface); + + TRACE("iface %p, ellipse %p.\n", iface, ellipse); + + *ellipse = geometry->u.ellipse.ellipse; +} + +static const struct ID2D1EllipseGeometryVtbl d2d_ellipse_geometry_vtbl = +{ + d2d_ellipse_geometry_QueryInterface, + d2d_ellipse_geometry_AddRef, + d2d_ellipse_geometry_Release, + d2d_ellipse_geometry_GetFactory, + d2d_ellipse_geometry_GetBounds, + d2d_ellipse_geometry_GetWidenedBounds, + d2d_ellipse_geometry_StrokeContainsPoint, + d2d_ellipse_geometry_FillContainsPoint, + d2d_ellipse_geometry_CompareWithGeometry, + d2d_ellipse_geometry_Simplify, + d2d_ellipse_geometry_Tessellate, + d2d_ellipse_geometry_CombineWithGeometry, + d2d_ellipse_geometry_Outline, + d2d_ellipse_geometry_ComputeArea, + d2d_ellipse_geometry_ComputeLength, + d2d_ellipse_geometry_ComputePointAtLength, + d2d_ellipse_geometry_Widen, + d2d_ellipse_geometry_GetRoundedRect, +}; + +HRESULT d2d_ellipse_geometry_init(struct d2d_geometry *geometry, ID2D1Factory *factory, const D2D1_ELLIPSE *ellipse) +{ + struct d2d_face *f; + struct d2d_bezier_vertex *bv; + D2D1_POINT_2F *v, v1, v2, v3, v4; + float l, r, t, b; + + d2d_geometry_init(geometry, factory, &identity, (ID2D1GeometryVtbl *)&d2d_ellipse_geometry_vtbl); + geometry->u.ellipse.ellipse = *ellipse; + + if (!(geometry->fill.vertices = heap_alloc(4 * sizeof(*geometry->fill.vertices)))) + goto fail; + if (!d2d_array_reserve((void **)&geometry->fill.faces, + &geometry->fill.faces_size, 2, sizeof(*geometry->fill.faces))) + goto fail; + if (!(geometry->fill.bezier_vertices = heap_alloc(12 * sizeof(*geometry->fill.bezier_vertices)))) + goto fail; + + l = ellipse->point.x - ellipse->radiusX; + r = ellipse->point.x + ellipse->radiusX; + t = ellipse->point.y - ellipse->radiusY; + b = ellipse->point.y + ellipse->radiusY; + + d2d_point_set(&v1, r, t); + d2d_point_set(&v2, r, b); + d2d_point_set(&v3, l, b); + d2d_point_set(&v4, l, t); + + v = geometry->fill.vertices; + d2d_point_set(&v[0], ellipse->point.x, t); + d2d_point_set(&v[1], r, ellipse->point.y); + d2d_point_set(&v[2], ellipse->point.x, b); + d2d_point_set(&v[3], l, ellipse->point.y); + geometry->fill.vertex_count = 4; + + f = geometry->fill.faces; + d2d_face_set(&f[0], 0, 3, 2); + d2d_face_set(&f[1], 0, 2, 1); + geometry->fill.face_count = 2; + + bv = geometry->fill.bezier_vertices; + d2d_bezier_vertex_set(&bv[0], &v[0], 0.0f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[1], &v1, 0.5f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[2], &v[1], 1.0f, 1.0f, -1.0f); + d2d_bezier_vertex_set(&bv[3], &v[1], 0.0f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[4], &v2, 0.5f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[5], &v[2], 1.0f, 1.0f, -1.0f); + d2d_bezier_vertex_set(&bv[6], &v[2], 0.0f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[7], &v3, 0.5f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[8], &v[3], 1.0f, 1.0f, -1.0f); + d2d_bezier_vertex_set(&bv[9], &v[3], 0.0f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[10], &v4, 0.5f, 0.0f, -1.0f); + d2d_bezier_vertex_set(&bv[11], &v[0], 1.0f, 1.0f, -1.0f); + geometry->fill.bezier_vertex_count = 12; + + if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[0], &v1, &v[1])) + goto fail; + if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[1], &v2, &v[2])) + goto fail; + if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[2], &v3, &v[3])) + goto fail; + if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[3], &v4, &v[0])) + goto fail; + + return S_OK; + +fail: + d2d_geometry_cleanup(geometry); + return E_OUTOFMEMORY; +} + static inline struct d2d_geometry *impl_from_ID2D1TransformedGeometry(ID2D1TransformedGeometry *iface) { return CONTAINING_RECORD(iface, struct d2d_geometry, ID2D1Geometry_iface); @@ -4695,6 +4976,7 @@ struct d2d_geometry *unsafe_impl_from_ID2D1Geometry(ID2D1Geometry *iface) assert(iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_path_geometry_vtbl || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_rectangle_geometry_vtbl || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_rounded_rectangle_geometry_vtbl + || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_ellipse_geometry_vtbl || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_transformed_geometry_vtbl || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_geometry_group_vtbl); return CONTAINING_RECORD(iface, struct d2d_geometry, ID2D1Geometry_iface);
Il 27/01/20 15:20, Giovanni Mascellani ha scritto:
- bv = geometry->fill.bezier_vertices;
- d2d_bezier_vertex_set(&bv[0], &v[1], 0.0f, 0.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[1], &v1, 0.5f, 0.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[2], &v[2], 1.0f, 1.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[3], &v[3], 0.0f, 0.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[4], &v2, 0.5f, 0.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[5], &v[4], 1.0f, 1.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[6], &v[5], 0.0f, 0.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[7], &v3, 0.5f, 0.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[8], &v[6], 1.0f, 1.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[9], &v[7], 0.0f, 0.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[10], &v4, 0.5f, 0.0f, -1.0f);
- d2d_bezier_vertex_set(&bv[11], &v[0], 1.0f, 1.0f, -1.0f);
- geometry->fill.bezier_vertex_count = 12;
- if (!d2d_geometry_outline_add_line_segment(geometry, &v[0], &v[1]))
goto fail;
- if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[1], &v1, &v[2]))
goto fail;
- if (!d2d_geometry_outline_add_line_segment(geometry, &v[2], &v[3]))
goto fail;
- if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[3], &v2, &v[4]))
goto fail;
- if (!d2d_geometry_outline_add_line_segment(geometry, &v[4], &v[5]))
goto fail;
- if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[5], &v3, &v[6]))
goto fail;
- if (!d2d_geometry_outline_add_line_segment(geometry, &v[6], &v[7]))
goto fail;
- if (!d2d_geometry_outline_add_arc_quadrant(geometry, &v[7], &v4, &v[0]))
goto fail;
I implemented an independent interface for arc quadrant stroking, but filling still uses Bézier structures, since there is no real "interface" for filling.
In line of principle another array arc_vertices might be added to d2d_geometry (again, used to draw Béziers), but then the meaning of UV coords would make no sense for actual arcs.
Giovanni.
On Mon, 27 Jan 2020 at 17:58, Giovanni Mascellani gio@debian.org wrote:
I implemented an independent interface for arc quadrant stroking, but filling still uses Bézier structures, since there is no real "interface" for filling.
In line of principle another array arc_vertices might be added to d2d_geometry (again, used to draw Béziers), but then the meaning of UV coords would make no sense for actual arcs.
You could introduce something along the lines of the following though:
static BOOL d2d_geometry_add_arc_vertices(struct d2d_geometry *geometry, const D2D1_POINT_2F *p0, const D2D1_POINT_2F *p1, const D2D1_POINT_2F *p2) { if (!d2d_array_reserve((void **)geometry->fill.bezier_vertices, ...)) return FALSE;
... d2d_bezier_vertex_set(...); d2d_bezier_vertex_set(...); d2d_bezier_vertex_set(...);
return TRUE; }