From: Bartosz Kosiorek gang65@poczta.onet.pl
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45273 --- dlls/gdiplus/customlinecap.c | 17 ++++++++--------- dlls/gdiplus/gdiplus_private.h | 2 +- dlls/gdiplus/tests/customlinecap.c | 5 +++-- 3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/dlls/gdiplus/customlinecap.c b/dlls/gdiplus/customlinecap.c index c74928eeda6..2e7b000355e 100644 --- a/dlls/gdiplus/customlinecap.c +++ b/dlls/gdiplus/customlinecap.c @@ -96,7 +96,7 @@ static GpStatus init_custom_linecap(GpCustomLineCap *cap, GpPathData *pathdata, cap->pathdata.Count = pathdata->Count;
cap->inset = base_inset; - cap->cap = basecap; + cap->basecap = basecap; cap->join = LineJoinMiter; cap->scale = 1.0;
@@ -193,16 +193,15 @@ GpStatus WINGDIPAPI GdipSetCustomLineCapStrokeCaps(GpCustomLineCap* custom, }
GpStatus WINGDIPAPI GdipSetCustomLineCapBaseCap(GpCustomLineCap* custom, - GpLineCap base) + GpLineCap basecap) { - static int calls; + TRACE("(%p,%u)\n", custom, basecap); + if(!custom || basecap > LineCapTriangle) + return InvalidParameter;
- TRACE("(%p,%u)\n", custom, base); + custom->basecap = basecap;
- if(!(calls++)) - FIXME("not implemented\n"); - - return NotImplemented; + return Ok; }
GpStatus WINGDIPAPI GdipGetCustomLineCapBaseInset(GpCustomLineCap* custom, @@ -264,7 +263,7 @@ GpStatus WINGDIPAPI GdipGetCustomLineCapBaseCap(GpCustomLineCap *customCap, GpLi if(!customCap || !baseCap) return InvalidParameter;
- *baseCap = customCap->cap; + *baseCap = customCap->basecap;
return Ok; } diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 0c55af6614a..77b35659946 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -351,7 +351,7 @@ struct GpCustomLineCap{ CustomLineCapType type; GpPathData pathdata; BOOL fill; /* TRUE for fill, FALSE for stroke */ - GpLineCap cap; /* as far as I can tell, this value is ignored */ + GpLineCap basecap; /* cap used together with customLineCap */ REAL inset; /* how much to adjust the end of the line */ GpLineJoin join; REAL scale; diff --git a/dlls/gdiplus/tests/customlinecap.c b/dlls/gdiplus/tests/customlinecap.c index 26175ca8f66..c475216ff21 100644 --- a/dlls/gdiplus/tests/customlinecap.c +++ b/dlls/gdiplus/tests/customlinecap.c @@ -283,14 +283,15 @@ static void test_create_adjustable_cap(void) ok(base == LineCapTriangle, "Unexpected base cap %d\n", base);
stat = GdipSetCustomLineCapBaseCap((GpCustomLineCap*)cap, LineCapSquare); - todo_wine ok(stat == Ok, "Unexpected return code, %d\n", stat);
stat = GdipGetCustomLineCapBaseCap((GpCustomLineCap*)cap, &base); ok(stat == Ok, "Unexpected return code, %d\n", stat); - todo_wine ok(base == LineCapSquare, "Unexpected base cap %d\n", base);
+ stat = GdipSetCustomLineCapBaseCap((GpCustomLineCap*)cap, LineCapSquareAnchor); + ok(stat == InvalidParameter, "Unexpected return code, %d\n", stat); + /* Base inset */ stat = GdipGetAdjustableArrowCapWidth(cap, &width); ok(stat == Ok, "Unexpected return code, %d\n", stat);
From: Bartosz Kosiorek gang65@poczta.onet.pl
--- dlls/gdiplus/graphicspath.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index d98a5ae4bf6..5ddd96778db 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -1876,7 +1876,7 @@ static void widen_joint(const GpPointF *p1, const GpPointF *p2, const GpPointF * }
static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint, - REAL pen_width, GpLineCap cap, GpCustomLineCap *custom, int add_first_points, + REAL pen_width, GpLineCap cap, int add_first_points, int add_last_point, path_list_node_t **last_point) { switch (cap) @@ -2097,8 +2097,8 @@ static void add_anchor(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, GpCustomLineCap *start_custom, - GpLineCap end_cap, GpCustomLineCap *end_custom, path_list_node_t **last_point) + 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; @@ -2109,21 +2109,21 @@ static void widen_open_figure(const GpPointF *points, int start, int end, prev_point = *last_point;
widen_cap(&points[start], &points[start+1], - pen_width, start_cap, start_custom, FALSE, TRUE, last_point); + 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, end_custom, TRUE, TRUE, last_point); + 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, start_custom, TRUE, FALSE, last_point); + pen_width, start_cap, TRUE, FALSE, last_point);
prev_point->next->type = PathPointTypeStart; (*last_point)->type |= PathPointTypeCloseSubpath; @@ -2267,8 +2267,8 @@ static void widen_dashed_figure(GpPath *path, int start, int end, int closed, tmp_points[num_tmp_points].Y = path->pathdata.Points[i].Y + segment_dy * segment_pos / segment_length;
widen_open_figure(tmp_points, 0, num_tmp_points, pen, pen_width, - draw_start_cap ? pen->startcap : LineCapFlat, pen->customstart, - LineCapFlat, NULL, last_point); + draw_start_cap ? pen->startcap : LineCapFlat, + LineCapFlat, last_point); draw_start_cap = 0; num_tmp_points = 0; } @@ -2301,8 +2301,8 @@ static void widen_dashed_figure(GpPath *path, int start, int end, int closed, { /* last dash overflows last segment */ widen_open_figure(tmp_points, 0, num_tmp_points-1, pen, pen_width, - draw_start_cap ? pen->startcap : LineCapFlat, pen->customstart, - closed ? LineCapFlat : pen->endcap, pen->customend, last_point); + draw_start_cap ? pen->startcap : LineCapFlat, + closed ? LineCapFlat : pen->endcap, last_point); }
heap_free(dash_pattern_scaled); @@ -2377,7 +2377,7 @@ GpStatus WINGDIPAPI GdipWidenPath(GpPath *path, GpPen *pen, GpMatrix *matrix, if (pen->dash != DashStyleSolid) widen_dashed_figure(flat_path, subpath_start, i, 0, pen, pen_width, &last_point); else - widen_open_figure(flat_path->pathdata.Points, subpath_start, i, pen, pen_width, pen->startcap, pen->customstart, pen->endcap, pen->customend, &last_point); + widen_open_figure(flat_path->pathdata.Points, subpath_start, i, pen, pen_width, pen->startcap, pen->endcap, &last_point); } }
From: Bartosz Kosiorek gang65@poczta.onet.pl
--- dlls/gdiplus/customlinecap.c | 10 +++++----- dlls/gdiplus/tests/customlinecap.c | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/dlls/gdiplus/customlinecap.c b/dlls/gdiplus/customlinecap.c index 2e7b000355e..c05aecae29a 100644 --- a/dlls/gdiplus/customlinecap.c +++ b/dlls/gdiplus/customlinecap.c @@ -220,14 +220,14 @@ GpStatus WINGDIPAPI GdipGetCustomLineCapBaseInset(GpCustomLineCap* custom, GpStatus WINGDIPAPI GdipSetCustomLineCapBaseInset(GpCustomLineCap* custom, REAL inset) { - static int calls; - TRACE("(%p,%0.2f)\n", custom, inset);
- if(!(calls++)) - FIXME("not implemented\n"); + if(!custom) + return InvalidParameter;
- return NotImplemented; + custom->inset = inset; + + return Ok; }
/*FIXME: LineJoin completely ignored now */ diff --git a/dlls/gdiplus/tests/customlinecap.c b/dlls/gdiplus/tests/customlinecap.c index c475216ff21..c2272f9ebad 100644 --- a/dlls/gdiplus/tests/customlinecap.c +++ b/dlls/gdiplus/tests/customlinecap.c @@ -165,6 +165,14 @@ static void test_inset(void) expect(Ok, stat); expectf(0.0, inset);
+ stat = GdipSetCustomLineCapBaseInset(custom, 2.0); + expect(Ok, stat); + + inset = (REAL)0xdeadbeef; + stat = GdipGetCustomLineCapBaseInset(custom, &inset); + expect(Ok, stat); + ok(inset == 2.0, "Unexpected inset value %d\n", inset); + GdipDeleteCustomLineCap(custom); GdipDeletePath(path); }
From: Bartosz Kosiorek gang65@poczta.onet.pl
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45273 --- dlls/gdiplus/customlinecap.c | 2 +- dlls/gdiplus/gdiplus_private.h | 4 ++-- dlls/gdiplus/graphicspath.c | 35 +++++++++++++++++++++++++++++-- dlls/gdiplus/tests/graphicspath.c | 13 ++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/dlls/gdiplus/customlinecap.c b/dlls/gdiplus/customlinecap.c index c05aecae29a..3943150d875 100644 --- a/dlls/gdiplus/customlinecap.c +++ b/dlls/gdiplus/customlinecap.c @@ -305,7 +305,7 @@ static void arrowcap_update_path(GpAdjustableArrowCap *cap) points[2].X = cap->width / 2.0; points[2].Y = -cap->height; points[3].X = 0.0; - points[3].Y = -cap->height - cap->middle_inset; + points[3].Y = -cap->height + cap->middle_inset; } else { 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..1758306ef58 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -2091,6 +2091,37 @@ 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 par_dx, par_dy; + REAL perp_dx, perp_dy; + REAL sina, cosa; + + if(!custom) + break; + + 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 + par_dx = -pen_width * segment_dx / segment_length; + par_dy = -pen_width * segment_dy / segment_length; + + sina = -pen_width * custom->scale * segment_dx / segment_length; + cosa = pen_width * custom->scale * segment_dy / segment_length; + + 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 - custom->inset) * sina; + perp_dy = custom->pathdata.Points[i].X * sina - (custom->pathdata.Points[i].Y - custom->inset) * cosa; + *last_point = add_path_list_node(*last_point, endpoint->X + par_dx + perp_dx, + endpoint->Y + par_dy + perp_dy, custom->pathdata.Types[i]); + } + // FIXME: The line should be shorter, and it should ends up when Arrow/Cap is started draw + break; + } }
(*last_point)->type |= PathPointTypeCloseSubpath; @@ -2341,10 +2372,10 @@ GpStatus WINGDIPAPI GdipWidenPath(GpPath *path, GpPen *pen, GpMatrix *matrix,
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) diff --git a/dlls/gdiplus/tests/graphicspath.c b/dlls/gdiplus/tests/graphicspath.c index 07e6dd166ad..213fecef2ca 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, 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);
Bartosz Kosiorek (@gang65) commented about dlls/gdiplus/customlinecap.c:
points[2].X = cap->width / 2.0; points[2].Y = -cap->height; points[3].X = 0.0;
points[3].Y = -cap->height - cap->middle_inset;
points[3].Y = -cap->height + cap->middle_inset;
the middle inset sign was wrong in previous implementation. Now arrow looks perfectly fine.
Bartosz Kosiorek (@gang65) commented about dlls/gdiplus/graphicspath.c:
// Coordination where cap needs to be drawn
par_dx = -pen_width * segment_dx / segment_length;
par_dy = -pen_width * segment_dy / segment_length;
sina = -pen_width * custom->scale * segment_dx / segment_length;
cosa = pen_width * custom->scale * segment_dy / segment_length;
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 - custom->inset) * sina;
perp_dy = custom->pathdata.Points[i].X * sina - (custom->pathdata.Points[i].Y - custom->inset) * cosa;
*last_point = add_path_list_node(*last_point, endpoint->X + par_dx + perp_dx,
endpoint->Y + par_dy + perp_dy, custom->pathdata.Types[i]);
}
// FIXME: The line should be shorter, and it should ends up when Arrow/Cap is started draw
The line should be shortened, to not mix with Customized Cap.
![Screenshot_from_2022-11-08_19-51-40](/uploads/dabd7fb066ec5b47b828621383efcb5e/Screenshot_from_2022-11-08_19-51-40.png)
Screenshot from native GdiPlus: ![Screenshot_from_2022-11-04_19-55-12](/uploads/746b64484bc64224cd685978f04fd883/Screenshot_from_2022-11-04_19-55-12.png)