gdiplus: Use passed pen in GdipAddPathWorldBound.
[wine] / dlls / gdiplus / graphicspath.c
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  */
19
20 #include <stdarg.h>
21 #include <math.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "wingdi.h"
27 #include "gdiplus.h"
28 #include "gdiplus_private.h"
29 #include "wine/debug.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
32
33 /* make sure path has enough space for len more points */
34 static BOOL lengthen_path(GpPath *path, INT len)
35 {
36     /* initial allocation */
37     if(path->datalen == 0){
38         path->datalen = len * 2;
39
40         path->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
41         if(!path->pathdata.Points)   return FALSE;
42
43         path->pathdata.Types = GdipAlloc(path->datalen);
44         if(!path->pathdata.Types){
45             GdipFree(path->pathdata.Points);
46             return FALSE;
47         }
48     }
49     /* reallocation, double size of arrays */
50     else if(path->datalen - path->pathdata.Count < len){
51         while(path->datalen - path->pathdata.Count < len)
52             path->datalen *= 2;
53
54         path->pathdata.Points = HeapReAlloc(GetProcessHeap(), 0,
55             path->pathdata.Points, path->datalen * sizeof(PointF));
56         if(!path->pathdata.Points)  return FALSE;
57
58         path->pathdata.Types = HeapReAlloc(GetProcessHeap(), 0,
59             path->pathdata.Types, path->datalen);
60         if(!path->pathdata.Types)   return FALSE;
61     }
62
63     return TRUE;
64 }
65
66 GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
67     REAL y2, REAL startAngle, REAL sweepAngle)
68 {
69     INT count, old_count, i;
70
71     if(!path)
72         return InvalidParameter;
73
74     count = arc2polybezier(NULL, x1, y1, x2, y2, startAngle, sweepAngle);
75
76     if(count == 0)
77         return Ok;
78     if(!lengthen_path(path, count))
79         return OutOfMemory;
80
81     old_count = path->pathdata.Count;
82     arc2polybezier(&path->pathdata.Points[old_count], x1, y1, x2, y2,
83                    startAngle, sweepAngle);
84
85     for(i = 0; i < count; i++){
86         path->pathdata.Types[old_count + i] = PathPointTypeBezier;
87     }
88
89     path->pathdata.Types[old_count] =
90         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
91     path->newfigure = FALSE;
92     path->pathdata.Count += count;
93
94     return Ok;
95 }
96
97 GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points,
98     INT count)
99 {
100     INT i, old_count;
101
102     if(!path || !points)
103         return InvalidParameter;
104
105     if(!lengthen_path(path, count))
106         return OutOfMemory;
107
108     old_count = path->pathdata.Count;
109
110     for(i = 0; i < count; i++){
111         path->pathdata.Points[old_count + i].X = points[i].X;
112         path->pathdata.Points[old_count + i].Y = points[i].Y;
113         path->pathdata.Types[old_count + i] = PathPointTypeLine;
114     }
115
116     if(path->newfigure){
117         path->pathdata.Types[old_count] = PathPointTypeStart;
118         path->newfigure = FALSE;
119     }
120
121     path->pathdata.Count += count;
122
123     return Ok;
124 }
125
126 GpStatus WINGDIPAPI GdipClosePathFigure(GpPath* path)
127 {
128     if(!path)
129         return InvalidParameter;
130
131     if(path->pathdata.Count > 0){
132         path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
133         path->newfigure = TRUE;
134     }
135
136     return Ok;
137 }
138
139 GpStatus WINGDIPAPI GdipClosePathFigures(GpPath* path)
140 {
141     INT i;
142
143     if(!path)
144         return InvalidParameter;
145
146     for(i = 1; i < path->pathdata.Count; i++){
147         if(path->pathdata.Types[i] == PathPointTypeStart)
148             path->pathdata.Types[i-1] |= PathPointTypeCloseSubpath;
149     }
150
151     path->newfigure = TRUE;
152
153     return Ok;
154 }
155
156 GpStatus WINGDIPAPI GdipCreatePath(GpFillMode fill, GpPath **path)
157 {
158     if(!path)
159         return InvalidParameter;
160
161     *path = GdipAlloc(sizeof(GpPath));
162     if(!*path)  return OutOfMemory;
163
164     (*path)->fill = fill;
165     (*path)->newfigure = TRUE;
166
167     return Ok;
168 }
169
170 GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
171 {
172     if(!path)
173         return InvalidParameter;
174
175     GdipFree(path->pathdata.Points);
176     GdipFree(path->pathdata.Types);
177     GdipFree(path);
178
179     return Ok;
180 }
181
182 GpStatus WINGDIPAPI GdipGetPathPoints(GpPath *path, GpPointF* points, INT count)
183 {
184     if(!path)
185         return InvalidParameter;
186
187     if(count < path->pathdata.Count)
188         return InsufficientBuffer;
189
190     memcpy(points, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF));
191
192     return Ok;
193 }
194
195 GpStatus WINGDIPAPI GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
196 {
197     if(!path)
198         return InvalidParameter;
199
200     if(count < path->pathdata.Count)
201         return InsufficientBuffer;
202
203     memcpy(types, path->pathdata.Types, path->pathdata.Count);
204
205     return Ok;
206 }
207
208 /* Windows expands the bounding box to the maximum possible bounding box
209  * for a given pen.  For example, if a line join can extend past the point
210  * it's joining by x units, the bounding box is extended by x units in every
211  * direction (even though this is too conservative for most cases). */
212 GpStatus WINGDIPAPI GdipGetPathWorldBounds(GpPath* path, GpRectF* bounds,
213     GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
214 {
215     /* extrema[0] is upper left corner of bounding box,
216      * extrema[1] is lower right corner */
217     GpPointF extrema[2];
218     GpPointF * points;
219     INT count, i;
220     REAL path_width;
221
222     /* Matrix and pen can be null. */
223     if(!path || !bounds)
224         return InvalidParameter;
225
226     /* If path is empty just return. */
227     count = path->pathdata.Count;
228     if(count == 0){
229         bounds->X = bounds->Y = bounds->Width = bounds->Height = 0.0;
230         return Ok;
231     }
232
233     points = path->pathdata.Points;
234     extrema[0].X = extrema[1].X  = points[0].X;
235     extrema[0].Y = extrema[1].Y = points[0].Y;
236
237     for(i = 1; i < count; i++){
238         extrema[0].X = min(points[i].X, extrema[0].X);
239         extrema[0].Y = min(points[i].Y, extrema[0].Y);
240         extrema[1].X = max(points[i].X, extrema[1].X);
241         extrema[1].Y = max(points[i].Y, extrema[1].Y);
242     }
243
244     /* If matrix is non-null transform the points. */
245     if(matrix){
246         GdipTransformMatrixPoints((GpMatrix*)matrix, extrema, 2);
247     }
248
249     if(pen){
250         path_width = pen->width * pen->miterlimit / 2.0;
251         extrema[0].X -= path_width;
252         extrema[0].Y -= path_width;
253         extrema[1].X += path_width;
254         extrema[1].Y += path_width;
255     }
256
257     bounds->X = extrema[0].X;
258     bounds->Y = extrema[0].Y;
259     bounds->Width = extrema[1].X - extrema[0].X;
260     bounds->Height = extrema[1].Y - extrema[0].Y;
261
262     return Ok;
263 }
264
265 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
266 {
267     if(!path)
268         return InvalidParameter;
269
270     *count = path->pathdata.Count;
271
272     return Ok;
273 }
274
275 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
276 {
277     if(!path)
278         return InvalidParameter;
279
280     path->newfigure = TRUE;
281
282     return Ok;
283 }
284
285 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
286 {
287     if(!path)
288         return InvalidParameter;
289
290     if(path->pathdata.Count == 0)
291         return Ok;
292
293     return GdipTransformMatrixPoints(matrix, (GpPointF*) path->pathdata.Points,
294                                      path->pathdata.Count);
295 }