From 69fa7457e5d746b71c8fef1f2ffb1924a5bc8d35 Mon Sep 17 00:00:00 2001 From: Evan Stade Date: Wed, 11 Jul 2007 18:06:56 -0700 Subject: [PATCH] gdiplus: Added GdipAddPathArc. --- dlls/gdiplus/gdiplus.c | 107 +++++++++++++++++++++++++++++++++ dlls/gdiplus/gdiplus.spec | 2 +- dlls/gdiplus/gdiplus_private.h | 3 + dlls/gdiplus/graphicspath.c | 31 ++++++++++ include/gdiplusflat.h | 1 + 5 files changed, 143 insertions(+), 1 deletion(-) diff --git a/dlls/gdiplus/gdiplus.c b/dlls/gdiplus/gdiplus.c index 959bcb08290..e821b183970 100644 --- a/dlls/gdiplus/gdiplus.c +++ b/dlls/gdiplus/gdiplus.c @@ -17,12 +17,14 @@ */ #include +#include #include "windef.h" #include "winbase.h" #include "winerror.h" #include "wine/debug.h" #include "gdiplus.h" +#include "gdiplus_private.h" WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); @@ -92,6 +94,111 @@ void WINGDIPAPI GdipFree(void* ptr) HeapFree(GetProcessHeap(), 0, ptr); } +/* Calculates the bezier points needed to fill in the arc portion starting at + * angle start and ending at end. These two angles should be no more than 90 + * degrees from each other. x1, y1, x2, y2 describes the bounding box (upper + * left and width and height). Angles must be in radians. write_first indicates + * that the first bezier point should be written out (usually this is false). + * pt is the array of GpPointFs that gets written to. + **/ +static void add_arc_part(GpPointF * pt, REAL x1, REAL y1, REAL x2, REAL y2, + REAL start, REAL end, BOOL write_first) +{ + REAL center_x, center_y, rad_x, rad_y, cos_start, cos_end, + sin_start, sin_end, a, half; + INT i; + + rad_x = x2 / 2.0; + rad_y = y2 / 2.0; + center_x = x1 + rad_x; + center_y = y1 + rad_y; + + cos_start = cos(start); + cos_end = cos(end); + sin_start = sin(start); + sin_end = sin(end); + + half = (end - start) / 2.0; + a = 4.0 / 3.0 * (1 - cos(half)) / sin(half); + + if(write_first){ + pt[0].X = cos_start; + pt[0].Y = sin_start; + } + pt[1].X = cos_start - a * sin_start; + pt[1].Y = sin_start + a * cos_start; + + pt[3].X = cos_end; + pt[3].Y = sin_end; + pt[2].X = cos_end + a * sin_end; + pt[2].Y = sin_end - a * cos_end; + + /* expand the points back from the unit circle to the ellipse */ + for(i = (write_first ? 0 : 1); i < 4; i ++){ + pt[i].X = pt[i].X * rad_x + center_x; + pt[i].Y = pt[i].Y * rad_y + center_y; + } +} + +/* We plot the curve as if it is on a circle then stretch the points. This + * adjusts the angles so that when we stretch the points they will end in the + * right place. This is only complicated because atan and atan2 do not behave + * conveniently. */ +static void unstretch_angle(REAL * angle, REAL rad_x, REAL rad_y) +{ + REAL stretched; + INT revs_off; + + *angle = deg2rad(*angle); + + if(cos(*angle) == 0 || sin(*angle) == 0) + return; + + stretched = atan2(sin(*angle) / rad_y, cos(*angle) / rad_x); + revs_off = roundr(*angle / (2.0 * M_PI)) - roundr(stretched / (2.0 * M_PI)); + stretched += ((REAL)revs_off) * M_PI * 2.0; + *angle = stretched; +} + +/* Stores the bezier points that correspond to the arc in points. If points is + * null, just return the number of points needed to represent the arc. */ +INT arc2polybezier(GpPointF * points, REAL x1, REAL y1, REAL x2, REAL y2, + REAL startAngle, REAL sweepAngle) +{ + INT i, count; + REAL end_angle, start_angle, endAngle; + + endAngle = startAngle + sweepAngle; + unstretch_angle(&startAngle, x2 / 2.0, y2 / 2.0); + unstretch_angle(&endAngle, x2 / 2.0, y2 / 2.0); + + count = ceilf(fabs(endAngle - startAngle) / M_PI_2) * 3 + 1; + /* don't make more than a full circle */ + count = min(MAX_ARC_PTS, count); + + if(count == 1) + return 0; + if(!points) + return count; + + /* start_angle and end_angle are the iterative variables */ + start_angle = startAngle; + + for(i = 0; i < count - 1; i += 3){ + /* check if we've overshot the end angle */ + if( sweepAngle > 0.0 ) + end_angle = min(start_angle + M_PI_2, endAngle); + else + end_angle = max(start_angle - M_PI_2, endAngle); + + add_arc_part(&points[i], x1, y1, x2, y2, start_angle, end_angle, i == 0); + + start_angle += M_PI_2 * (sweepAngle < 0.0 ? -1.0 : 1.0); + } + + return count; +} + COLORREF ARGB2COLORREF(ARGB color) { /* diff --git a/dlls/gdiplus/gdiplus.spec b/dlls/gdiplus/gdiplus.spec index 2d001ffdebc..22b085efd20 100644 --- a/dlls/gdiplus/gdiplus.spec +++ b/dlls/gdiplus/gdiplus.spec @@ -1,4 +1,4 @@ -@ stub GdipAddPathArc +@ stdcall GdipAddPathArc(ptr long long long long long long) @ stub GdipAddPathArcI @ stub GdipAddPathBezier @ stub GdipAddPathBezierI diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 5bcedd9ca3f..e37c20e3971 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -24,8 +24,11 @@ #include "gdiplus.h" #define GP_DEFAULT_PENSTYLE (PS_GEOMETRIC | PS_ENDCAP_FLAT | PS_JOIN_MITER) +#define MAX_ARC_PTS (13) COLORREF ARGB2COLORREF(ARGB color); +extern INT arc2polybezier(GpPointF * points, REAL x1, REAL y1, REAL x2, REAL y2, + REAL startAngle, REAL sweepAngle); static inline INT roundr(REAL x) { diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 5df628d47b2..e65dabd0d32 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -63,6 +63,37 @@ static BOOL lengthen_path(GpPath *path, INT len) return TRUE; } +GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2, + REAL y2, REAL startAngle, REAL sweepAngle) +{ + INT count, old_count, i; + + if(!path) + return InvalidParameter; + + count = arc2polybezier(NULL, x1, y1, x2, y2, startAngle, sweepAngle); + + if(count == 0) + return Ok; + if(!lengthen_path(path, count)) + return OutOfMemory; + + old_count = path->pathdata.Count; + arc2polybezier(&path->pathdata.Points[old_count], x1, y1, x2, y2, + startAngle, sweepAngle); + + for(i = 0; i < count; i++){ + path->pathdata.Types[old_count + i] = PathPointTypeBezier; + } + + path->pathdata.Types[old_count] = + (path->newfigure ? PathPointTypeStart : PathPointTypeLine); + path->newfigure = FALSE; + path->pathdata.Count += count; + + return Ok; +} + GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points, INT count) { diff --git a/include/gdiplusflat.h b/include/gdiplusflat.h index d9f5f9a5d6d..e49ecc3a2ee 100644 --- a/include/gdiplusflat.h +++ b/include/gdiplusflat.h @@ -48,6 +48,7 @@ GpStatus WINGDIPAPI GdipCreateSolidFill(ARGB,GpSolidFill**); GpStatus WINGDIPAPI GdipGetBrushType(GpBrush*,GpBrushType*); GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush*); +GpStatus WINGDIPAPI GdipAddPathArc(GpPath*,REAL,REAL,REAL,REAL,REAL,REAL); GpStatus WINGDIPAPI GdipAddPathLine2(GpPath*,GDIPCONST GpPointF*,INT); GpStatus WINGDIPAPI GdipClosePathFigure(GpPath*); GpStatus WINGDIPAPI GdipClosePathFigures(GpPath*);