diff --git a/dlls/gdiplus/gdiplus.spec b/dlls/gdiplus/gdiplus.spec index d18af7fb338..a805bd88a5a 100644 --- a/dlls/gdiplus/gdiplus.spec +++ b/dlls/gdiplus/gdiplus.spec @@ -178,7 +178,7 @@ @ stdcall GdipDrawLineI(ptr ptr long long long long) @ stdcall GdipDrawLines(ptr ptr ptr long) @ stub GdipDrawLinesI -@ stub GdipDrawPath +@ stdcall GdipDrawPath(ptr ptr ptr) @ stdcall GdipDrawPie(ptr ptr long long long long long long) @ stub GdipDrawPieI @ stub GdipDrawPolygon diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 82260ec4e2b..c16a7448dd0 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -375,6 +375,120 @@ static void draw_polybezier(HDC hdc, GpPen *pen, GDIPCONST GpPointF * pt, GdipFree(ptf); } +/* Converts from gdiplus path point type to gdi path point type. */ +static BYTE convert_path_point_type(BYTE type) +{ + BYTE ret; + + switch(type & PathPointTypePathTypeMask){ + case PathPointTypeBezier: + ret = PT_BEZIERTO; + break; + case PathPointTypeLine: + ret = PT_LINETO; + break; + case PathPointTypeStart: + ret = PT_MOVETO; + break; + default: + ERR("Bad point type\n"); + return 0; + } + + if(type & PathPointTypeCloseSubpath) + ret |= PT_CLOSEFIGURE; + + return ret; +} + +/* Draws a combination of bezier curves and lines between points. */ +static GpStatus draw_poly(HDC hdc, GpPen *pen, GDIPCONST GpPointF * pt, + GDIPCONST BYTE * types, INT count, BOOL caps) +{ + POINT *pti = GdipAlloc(count * sizeof(POINT)); + BYTE *tp = GdipAlloc(count); + GpPointF *ptf = NULL; + REAL x = pt[count - 1].X, y = pt[count - 1].Y; + INT i; + GpStatus status = GenericError; + + if(!count){ + status = Ok; + goto end; + } + if(!pti || !tp){ + status = OutOfMemory; + goto end; + } + + for(i = 0; i < count; i++){ + if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){ + if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier) + || !(types[i + 1] & PathPointTypeBezier)){ + ERR("Bad bezier points\n"); + goto end; + } + + i += 2; + } + } + + if((types[count - 1] & PathPointTypePathTypeMask) == PathPointTypeBezier){ + ptf = GdipAlloc(4 * sizeof(GpPointF)); + memcpy(ptf, &pt[count-4], 4 * sizeof(GpPointF)); + + if(caps){ + if(pen->endcap == LineCapArrowAnchor) + shorten_bezier_amt(ptf, pen->width); + + draw_cap(hdc, pen->color, pen->endcap, pen->width, ptf[3].X, + ptf[3].Y, pt[count - 1].X, pt[count - 1].Y); + } + for(i = 0; i < 4; i ++){ + pti[i + count - 4].x = roundr(ptf[i].X); + pti[i + count - 4].y = roundr(ptf[i].Y); + } + for(i = 0; i < count - 4; i ++){ + pti[i].x = roundr(pt[i].X); + pti[i].y = roundr(pt[i].Y); + } + } + else if((types[count - 1] & PathPointTypePathTypeMask) == PathPointTypeLine){ + if(caps){ + if(pen->endcap == LineCapArrowAnchor) + shorten_line_amt(pt[count-2].X, pt[count-2].Y, &x, &y, pen->width); + + draw_cap(hdc, pen->color, pen->endcap, pen->width, pt[count-2].X, + pt[count-2].Y, pt[count - 1].X, pt[count - 1].Y); + } + pti[count - 1].x = roundr(x); + pti[count - 1].y = roundr(y); + for(i = 0; i < count - 1; i ++){ + pti[i].x = roundr(pt[i].X); + pti[i].y = roundr(pt[i].Y); + } + } + else{ + ERR("Bad path last point\n"); + goto end; + } + + for(i = 0; i < count; i++){ + tp[i] = convert_path_point_type(types[i]); + } + + PolyDraw(hdc, pti, tp, count); + + status = Ok; + +end: + GdipFree(pti); + GdipFree(ptf); + GdipFree(tp); + + return status; +} + GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics) { if(hdc == NULL) @@ -564,6 +678,40 @@ GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST return Ok; } +GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path) +{ + INT save_state, i, this_fig = 0; + GpStatus retval; + + if(!pen || !graphics) + return InvalidParameter; + + save_state = SaveDC(graphics->hdc); + EndPath(graphics->hdc); + SelectObject(graphics->hdc, pen->gdipen); + + for(i = 0; i < path->pathdata.Count; i++){ + if(path->pathdata.Types[i] == PathPointTypeStart){ + retval = draw_poly(graphics->hdc, pen, + &path->pathdata.Points[this_fig], + &path->pathdata.Types[this_fig], i - this_fig, TRUE); + this_fig = i; + + if(retval != Ok) + goto end; + } + } + + retval = draw_poly(graphics->hdc, pen, &path->pathdata.Points[this_fig], + &path->pathdata.Types[this_fig], path->pathdata.Count - this_fig, + TRUE); + +end: + RestoreDC(graphics->hdc, save_state); + + return retval; +} + GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle) { diff --git a/include/gdiplusflat.h b/include/gdiplusflat.h index 04d71a977ae..b5743ebf9e5 100644 --- a/include/gdiplusflat.h +++ b/include/gdiplusflat.h @@ -40,6 +40,7 @@ GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics*,GpPen*,REAL,REAL,REAL,REAL,REAL, GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics*,GpPen*,GDIPCONST GpPointF*,INT,REAL); GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics*,GpPen*,INT,INT,INT,INT); GpStatus WINGDIPAPI GdipDrawLines(GpGraphics*,GpPen*,GDIPCONST GpPointF*,INT); +GpStatus WINGDIPAPI GdipDrawPath(GpGraphics*,GpPen*,GpPath*); GpStatus WINGDIPAPI GdipDrawPie(GpGraphics*,GpPen*,REAL,REAL,REAL,REAL,REAL,REAL); GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics*,GpPen*,INT,INT,INT,INT); GpStatus WINGDIPAPI GdipFillPie(GpGraphics*,GpBrush*,REAL,REAL,REAL,REAL,REAL,REAL);