gdiplus: Add support for widen path with GpCustomLineCap.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45273
This commit is contained in:
Bartosz Kosiorek 2022-11-07 17:36:05 +01:00 committed by Alexandre Julliard
parent a8e8730769
commit 1271ae3ebd
3 changed files with 90 additions and 8 deletions

View file

@ -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;
};

View file

@ -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);
}
}

View file

@ -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);