diff --git a/dlls/gdiplus/gdiplus.spec b/dlls/gdiplus/gdiplus.spec index 3036155b2e..775bde391d 100644 --- a/dlls/gdiplus/gdiplus.spec +++ b/dlls/gdiplus/gdiplus.spec @@ -153,7 +153,7 @@ @ stub GdipDrawClosedCurve2I @ stub GdipDrawClosedCurve @ stub GdipDrawClosedCurveI -@ stub GdipDrawCurve2 +@ stdcall GdipDrawCurve2(ptr ptr ptr long long) @ stub GdipDrawCurve2I @ stub GdipDrawCurve3 @ stub GdipDrawCurve3I diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index a4ba0ca0ca..f917b6579a 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -27,6 +27,9 @@ #include "gdiplus_private.h" #include "wine/debug.h" +/* looks-right constant */ +#define TENSION_CONST (0.3) + static inline INT roundr(REAL x) { return (INT) floor(x+0.5); @@ -77,6 +80,34 @@ static GpStatus draw_pie(GpGraphics *graphics, HBRUSH gdibrush, HPEN gdipen, return Ok; } +/* GdipDrawCurve helper function. + * Calculates Bezier points from cardinal spline points. */ +static void calc_curve_bezier(CONST GpPointF *pts, REAL tension, REAL *x1, + REAL *y1, REAL *x2, REAL *y2) +{ + REAL xdiff, ydiff; + + /* calculate tangent */ + xdiff = pts[2].X - pts[0].X; + ydiff = pts[2].Y - pts[0].Y; + + /* apply tangent to get control points */ + *x1 = pts[1].X - tension * xdiff; + *y1 = pts[1].Y - tension * ydiff; + *x2 = pts[1].X + tension * xdiff; + *y2 = pts[1].Y + tension * ydiff; +} + +/* GdipDrawCurve helper function. + * Calculates Bezier points from cardinal spline endpoints. */ +static void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj, + REAL tension, REAL *x, REAL *y) +{ + /* tangent at endpoints is the line from the endpoint to the adjacent point */ + *x = roundr(tension * (xadj - xend) + xend); + *y = roundr(tension * (yadj - yend) + yend); +} + GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics) { if(hdc == NULL) @@ -160,6 +191,58 @@ GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1, return Ok; } +/* Approximates cardinal spline with Bezier curves. */ +GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen, + GDIPCONST GpPointF *points, INT count, REAL tension) +{ + HGDIOBJ old_pen; + + /* PolyBezier expects count*3-2 points. */ + int i, len_pt = count*3-2; + POINT pt[len_pt]; + REAL x1, x2, y1, y2; + + if(!graphics || !pen) + return InvalidParameter; + + tension = tension * TENSION_CONST; + + calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y, + tension, &x1, &y1); + + pt[0].x = roundr(points[0].X); + pt[0].y = roundr(points[0].Y); + pt[1].x = roundr(x1); + pt[1].y = roundr(y1); + + for(i = 0; i < count-2; i++){ + calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2); + + pt[3*i+2].x = roundr(x1); + pt[3*i+2].y = roundr(y1); + pt[3*i+3].x = roundr(points[i+1].X); + pt[3*i+3].y = roundr(points[i+1].Y); + pt[3*i+4].x = roundr(x2); + pt[3*i+4].y = roundr(y2); + } + + calc_curve_bezier_endp(points[count-1].X, points[count-1].Y, + points[count-2].X, points[count-2].Y, tension, &x1, &y1); + + pt[len_pt-2].x = x1; + pt[len_pt-2].y = y1; + pt[len_pt-1].x = roundr(points[count-1].X); + pt[len_pt-1].y = roundr(points[count-1].Y); + + old_pen = SelectObject(graphics->hdc, pen->gdipen); + + PolyBezier(graphics->hdc, pt, len_pt); + + SelectObject(graphics->hdc, old_pen); + + return Ok; +} + GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1, INT y1, INT x2, INT y2) { diff --git a/include/gdiplusflat.h b/include/gdiplusflat.h index 538f1f40e5..e5c46b3059 100644 --- a/include/gdiplusflat.h +++ b/include/gdiplusflat.h @@ -21,6 +21,8 @@ #define WINGDIPAPI __stdcall +#define GDIPCONST const + #ifdef __cplusplus extern "C" { #endif @@ -34,6 +36,7 @@ GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *); GpStatus WINGDIPAPI GdipDrawArc(GpGraphics*,GpPen*,REAL,REAL,REAL,REAL,REAL,REAL); GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics*,GpPen*,REAL,REAL,REAL,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 GdipDrawPie(GpGraphics*,GpPen*,REAL,REAL,REAL,REAL,REAL,REAL); GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics*,GpPen*,INT,INT,INT,INT); diff --git a/include/gdiplusgpstubs.h b/include/gdiplusgpstubs.h index dcafc11f5a..727ca06fe4 100644 --- a/include/gdiplusgpstubs.h +++ b/include/gdiplusgpstubs.h @@ -38,5 +38,6 @@ typedef struct GpSolidFill GpSolidFill; typedef Status GpStatus; typedef Unit GpUnit; typedef BrushType GpBrushType; +typedef PointF GpPointF; #endif diff --git a/include/gdiplustypes.h b/include/gdiplustypes.h index 006fe0923b..38fdf58ede 100644 --- a/include/gdiplustypes.h +++ b/include/gdiplustypes.h @@ -45,7 +45,57 @@ enum Status{ PropertyNotSupported = 20 }; -#ifndef __cplusplus +#ifdef __cplusplus + +class PointF +{ +public: + PointF() + { + X = Y = 0.0f; + } + + PointF(IN const PointF &pt) + { + X = pt.X; + Y = pt.Y; + } + + /* FIXME: missing constructor that takes a SizeF */ + + PointF(IN REAL x, IN REAL y) + { + X = x; + Y = y; + } + + PointF operator+(IN const PointF& pt) const + { + return PointF(X + pt.X, Y + pt.Y); + } + + PointF operator-(IN const PointF& pt) const + { + return PointF(X - pt.X, Y - pt.Y); + } + + BOOL Equals(IN const PointF& pt) + { + return (X == pt.X) && (Y == pt.Y); + } + +public: + REAL X; + REAL Y; +}; + +#else /* end of c++ typedefs */ + +typedef struct PointF +{ + REAL X; + REAL Y; +} PointF; typedef enum Status Status;