From 1271ae3ebdf9b5b6e39c5688b64c8a3f0a98b8d4 Mon Sep 17 00:00:00 2001 From: Bartosz Kosiorek Date: Mon, 7 Nov 2022 17:36:05 +0100 Subject: [PATCH] gdiplus: Add support for widen path with GpCustomLineCap. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45273 --- dlls/gdiplus/gdiplus_private.h | 4 +- dlls/gdiplus/graphicspath.c | 81 ++++++++++++++++++++++++++++--- dlls/gdiplus/tests/graphicspath.c | 13 +++++ 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 77b35659946..baae159d7f9 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -352,8 +352,8 @@ struct GpCustomLineCap{ GpPathData pathdata; BOOL fill; /* TRUE for fill, FALSE for stroke */ GpLineCap basecap; /* cap used together with customLineCap */ - REAL inset; /* how much to adjust the end of the line */ - GpLineJoin join; + REAL inset; /* distance between line end and cap beginning */ + GpLineJoin join; /* joins used for drawing custom cap*/ REAL scale; }; diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index e24053004e0..8bb835c1ca1 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -1982,9 +1982,17 @@ static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint, } } +static void widen_open_figure(const GpPointF *points, int start, int end, + GpPen *pen, REAL pen_width, GpLineCap start_cap, + GpLineCap end_cap, path_list_node_t **last_point); + +static void widen_closed_figure(const GpPointF *points, int start, int end, + GpPen *pen, REAL pen_width, path_list_node_t **last_point); + static void add_anchor(const GpPointF *endpoint, const GpPointF *nextpoint, - REAL pen_width, GpLineCap cap, GpCustomLineCap *custom, path_list_node_t **last_point) + GpPen *pen, GpLineCap cap, GpCustomLineCap *custom, path_list_node_t **last_point) { + REAL pen_width = max(pen->width, 2.0); switch (cap) { default: @@ -2091,6 +2099,68 @@ static void add_anchor(const GpPointF *endpoint, const GpPointF *nextpoint, endpoint->Y + perp_dy, PathPointTypeLine); break; } + case LineCapCustom: + { + REAL segment_dy = nextpoint->Y - endpoint->Y; + REAL segment_dx = nextpoint->X - endpoint->X; + REAL segment_length = sqrtf(segment_dy * segment_dy + segment_dx * segment_dx); + REAL posx, posy; + REAL perp_dx, perp_dy; + REAL sina, cosa; + GpPointF *tmp_points; + + if(!custom) + break; + + if (custom->type == CustomLineCapTypeAdjustableArrow) + { + GpAdjustableArrowCap *arrow = (GpAdjustableArrowCap *)custom; + TRACE("GpAdjustableArrowCap middle_inset: %f height: %f width: %f\n", + arrow->middle_inset, arrow->height, arrow->width); + } + else + TRACE("GpCustomLineCap fill: %d basecap: %d inset: %f join: %d scale: %f pen_width:%f\n", + custom->fill, custom->basecap, custom->inset, custom->join, custom->scale, pen_width); + + /* Coordination where cap needs to be drawn */ + posx = endpoint->X - pen_width * segment_dx / segment_length; + posy = endpoint->Y - pen_width * segment_dy / segment_length; + + sina = -pen_width * custom->scale * segment_dx / segment_length; + cosa = pen_width * custom->scale * segment_dy / segment_length; + + if (!custom->fill) + { + tmp_points = heap_alloc_zero(custom->pathdata.Count * sizeof(GpPoint)); + if (!tmp_points) { + ERR("Out of memory\n"); + return; + } + + for (INT i = 0; i < custom->pathdata.Count; i++) + { + tmp_points[i].X = posx + custom->pathdata.Points[i].X * cosa + (custom->pathdata.Points[i].Y - 1.0) * sina; + tmp_points[i].Y = posy + custom->pathdata.Points[i].X * sina - (custom->pathdata.Points[i].Y - 1.0) * cosa; + } + if ((custom->pathdata.Types[custom->pathdata.Count - 1] & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath) + widen_closed_figure(tmp_points, 0, custom->pathdata.Count - 1, pen, pen_width, last_point); + else + widen_open_figure(tmp_points, 0, custom->pathdata.Count - 1, pen, pen_width, LineCapFlat, LineCapFlat, last_point); + } + else + { + for (INT i = 0; i < custom->pathdata.Count; i++) + { + /* rotation of CustomCap according to line */ + perp_dx = custom->pathdata.Points[i].X * cosa + (custom->pathdata.Points[i].Y - 1.0) * sina; + perp_dy = custom->pathdata.Points[i].X * sina - (custom->pathdata.Points[i].Y - 1.0) * cosa; + *last_point = add_path_list_node(*last_point, posx + perp_dx, + posy + perp_dy, custom->pathdata.Types[i]); + } + } + /* FIXME: The line should be adjusted by the inset value of the custom cap. */ + break; + } } (*last_point)->type |= PathPointTypeCloseSubpath; @@ -2335,16 +2405,15 @@ GpStatus WINGDIPAPI GdipWidenPath(GpPath *path, GpPen *pen, GpMatrix *matrix, if (status == Ok) { - REAL anchor_pen_width = max(pen->width, 2.0); REAL pen_width = (pen->unit == UnitWorld) ? max(pen->width, 1.0) : pen->width; BYTE *types = flat_path->pathdata.Types; last_point = points; - if (pen->endcap > LineCapDiamondAnchor) + if (pen->endcap > LineCapDiamondAnchor && pen->endcap != LineCapCustom) FIXME("unimplemented end cap %x\n", pen->endcap); - if (pen->startcap > LineCapDiamondAnchor) + if (pen->startcap > LineCapDiamondAnchor && pen->startcap != LineCapCustom) FIXME("unimplemented start cap %x\n", pen->startcap); if (pen->dashcap != DashCapFlat) @@ -2395,12 +2464,12 @@ GpStatus WINGDIPAPI GdipWidenPath(GpPath *path, GpPen *pen, GpMatrix *matrix, if (pen->startcap & LineCapAnchorMask) add_anchor(&flat_path->pathdata.Points[subpath_start], &flat_path->pathdata.Points[subpath_start+1], - anchor_pen_width, pen->startcap, pen->customstart, &last_point); + pen, pen->startcap, pen->customstart, &last_point); if (pen->endcap & LineCapAnchorMask) add_anchor(&flat_path->pathdata.Points[i], &flat_path->pathdata.Points[i-1], - anchor_pen_width, pen->endcap, pen->customend, &last_point); + pen, pen->endcap, pen->customend, &last_point); } } diff --git a/dlls/gdiplus/tests/graphicspath.c b/dlls/gdiplus/tests/graphicspath.c index 07e6dd166ad..ab1e7cb2fda 100644 --- a/dlls/gdiplus/tests/graphicspath.c +++ b/dlls/gdiplus/tests/graphicspath.c @@ -1682,9 +1682,12 @@ static void test_widen_cap(void) { LineCapSquareAnchor, 10.0, widenline_capsquareanchor_dashed_path, ARRAY_SIZE(widenline_capsquareanchor_dashed_path), TRUE }, }; + + GpAdjustableArrowCap *arrowcap; GpStatus status; GpPath *path; GpPen *pen; + int i; status = GdipCreatePath(FillModeAlternate, &path); @@ -1749,6 +1752,16 @@ static void test_widen_cap(void) expect(Ok, status); ok_path_fudge(path, widenline_capsquareanchor_multifigure_path, ARRAY_SIZE(widenline_capsquareanchor_multifigure_path), FALSE, 0.000005); + + status = GdipCreateAdjustableArrowCap(4.0, 4.0, TRUE, &arrowcap); + ok(status == Ok, "Failed to create adjustable cap, %d\n", status); + status = GdipSetAdjustableArrowCapMiddleInset(arrowcap, 1.0); + ok(status == Ok, "Failed to set middle inset inadjustable cap, %d\n", status); + status = GdipSetPenCustomEndCap(pen, (GpCustomLineCap*)arrowcap); + ok(status == Ok, "Failed to create custom end cap, %d\n", status); + status = GdipWidenPath(path, pen, NULL, FlatnessDefault); + expect(Ok, status); + GdipDeletePen(pen); GdipDeletePath(path);