From: Bartosz Kosiorek gang65@poczta.onet.pl
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45273 --- dlls/gdiplus/customlinecap.c | 18 ++--- dlls/gdiplus/gdiplus_private.h | 4 +- dlls/gdiplus/graphicspath.c | 128 ++++++++++++++++++++++-------- dlls/gdiplus/tests/graphicspath.c | 13 +++ 4 files changed, 117 insertions(+), 46 deletions(-)
diff --git a/dlls/gdiplus/customlinecap.c b/dlls/gdiplus/customlinecap.c index c05aecae29a..347efd2e259 100644 --- a/dlls/gdiplus/customlinecap.c +++ b/dlls/gdiplus/customlinecap.c @@ -299,24 +299,24 @@ static void arrowcap_update_path(GpAdjustableArrowCap *cap) memcpy(cap->cap.pathdata.Types, types_filled, sizeof(types_filled)); cap->cap.pathdata.Count = 4; points[0].X = -cap->width / 2.0; - points[0].Y = -cap->height; + points[0].Y = -cap->height - 1.0; points[1].X = 0.0; - points[1].Y = 0.0; + points[1].Y = -1.0; points[2].X = cap->width / 2.0; - points[2].Y = -cap->height; + points[2].Y = -cap->height - 1.0; points[3].X = 0.0; - points[3].Y = -cap->height - cap->middle_inset; + points[3].Y = -cap->height + cap->middle_inset - 1.0; } else { memcpy(cap->cap.pathdata.Types, types_unfilled, sizeof(types_unfilled)); cap->cap.pathdata.Count = 3; - points[0].X = -cap->width / 4.0; - points[0].Y = -cap->height / 2.0; + points[0].X = -cap->width / 2.0; + points[0].Y = -cap->height - 1.0; points[1].X = 0.0; - points[1].Y = 0.0; - points[2].X = cap->width / 4.0; - points[2].Y = -cap->height / 2.0; + points[1].Y = -1.0; + points[2].X = cap->width / 2.0; + points[2].Y = -cap->height - 1.0; }
if (cap->width == 0.0) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 77b35659946..51b83b3242a 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 and cap */ + GpLineJoin join; /* joins used for drawing custom cap*/ REAL scale; };
diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 5ddd96778db..4b467474fda 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -1982,9 +1982,43 @@ 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) +{ + int i; + path_list_node_t *prev_point; + + if (end <= start || pen_width == 0.0) + return; + + prev_point = *last_point; + + widen_cap(&points[start], &points[start+1], + pen_width, start_cap, FALSE, TRUE, last_point); + + for (i=start+1; i<end; i++) + widen_joint(&points[i-1], &points[i], &points[i+1], + pen, pen_width, last_point); + + widen_cap(&points[end], &points[end-1], + pen_width, end_cap, TRUE, TRUE, last_point); + + for (i=end-1; i>start; i--) + widen_joint(&points[i+1], &points[i], &points[i-1], + pen, pen_width, last_point); + + widen_cap(&points[start], &points[start+1], + pen_width, start_cap, TRUE, FALSE, last_point); + + prev_point->next->type = PathPointTypeStart; + (*last_point)->type |= PathPointTypeCloseSubpath; +} + 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,41 +2125,66 @@ static void add_anchor(const GpPointF *endpoint, const GpPointF *nextpoint, endpoint->Y + perp_dy, PathPointTypeLine); break; } - } - - (*last_point)->type |= PathPointTypeCloseSubpath; -} - -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) -{ - int i; - path_list_node_t *prev_point; - - if (end <= start || pen_width == 0.0) - return; - - prev_point = *last_point; + 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;
- widen_cap(&points[start], &points[start+1], - pen_width, start_cap, FALSE, TRUE, last_point); + if(!custom) + break;
- for (i=start+1; i<end; i++) - widen_joint(&points[i-1], &points[i], &points[i+1], - pen, pen_width, last_point); + 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);
- widen_cap(&points[end], &points[end-1], - pen_width, end_cap, TRUE, TRUE, last_point); + // 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;
- for (i=end-1; i>start; i--) - widen_joint(&points[i+1], &points[i], &points[i-1], - pen, pen_width, last_point); + sina = -pen_width * custom->scale * segment_dx / segment_length; + cosa = pen_width * custom->scale * segment_dy / segment_length;
- widen_cap(&points[start], &points[start+1], - pen_width, start_cap, TRUE, FALSE, last_point); + if (custom->type == CustomLineCapTypeAdjustableArrow && custom->pathdata.Count == 3) + { + tmp_points = heap_alloc_zero(3 * sizeof(GpPoint)); + if (!tmp_points) { + ERR("Out of memory\n"); + return; + } + tmp_points[0].X = posx + custom->pathdata.Points[0].X * cosa + custom->pathdata.Points[0].Y * sina; + tmp_points[0].Y = posy + custom->pathdata.Points[0].X * sina - custom->pathdata.Points[0].Y * cosa; + tmp_points[1].X = posx + custom->pathdata.Points[1].X * cosa + custom->pathdata.Points[1].Y * sina; + tmp_points[1].Y = posy + custom->pathdata.Points[1].X * sina - custom->pathdata.Points[1].Y * cosa; + tmp_points[2].X = posx + custom->pathdata.Points[2].X * cosa + custom->pathdata.Points[2].Y * sina;; + tmp_points[2].Y = posy + custom->pathdata.Points[2].X * sina - custom->pathdata.Points[2].Y * cosa; + + widen_open_figure(tmp_points, 0, 2, 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 * sina; + perp_dy = custom->pathdata.Points[i].X * sina - custom->pathdata.Points[i].Y * cosa; + *last_point = add_path_list_node(*last_point, posx + perp_dx, + posy + perp_dy, custom->pathdata.Types[i]); + } + // FIXME: The line should be shorter, and it should ends up when Arrow/Cap is started draw + break; + } + }
- prev_point->next->type = PathPointTypeStart; (*last_point)->type |= PathPointTypeCloseSubpath; }
@@ -2335,16 +2394,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 +2453,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);