gdiplus: Use the gdiplus type, REAL, rather than FLOAT.
[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
28 #include "objbase.h"
29
30 #include "gdiplus.h"
31 #include "gdiplus_private.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
35
36 /* make sure path has enough space for len more points */
37 static BOOL lengthen_path(GpPath *path, INT len)
38 {
39     /* initial allocation */
40     if(path->datalen == 0){
41         path->datalen = len * 2;
42
43         path->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
44         if(!path->pathdata.Points)   return FALSE;
45
46         path->pathdata.Types = GdipAlloc(path->datalen);
47         if(!path->pathdata.Types){
48             GdipFree(path->pathdata.Points);
49             return FALSE;
50         }
51     }
52     /* reallocation, double size of arrays */
53     else if(path->datalen - path->pathdata.Count < len){
54         while(path->datalen - path->pathdata.Count < len)
55             path->datalen *= 2;
56
57         path->pathdata.Points = HeapReAlloc(GetProcessHeap(), 0,
58             path->pathdata.Points, path->datalen * sizeof(PointF));
59         if(!path->pathdata.Points)  return FALSE;
60
61         path->pathdata.Types = HeapReAlloc(GetProcessHeap(), 0,
62             path->pathdata.Types, path->datalen);
63         if(!path->pathdata.Types)   return FALSE;
64     }
65
66     return TRUE;
67 }
68
69 GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
70     REAL y2, REAL startAngle, REAL sweepAngle)
71 {
72     INT count, old_count, i;
73
74     if(!path)
75         return InvalidParameter;
76
77     count = arc2polybezier(NULL, x1, y1, x2, y2, startAngle, sweepAngle);
78
79     if(count == 0)
80         return Ok;
81     if(!lengthen_path(path, count))
82         return OutOfMemory;
83
84     old_count = path->pathdata.Count;
85     arc2polybezier(&path->pathdata.Points[old_count], x1, y1, x2, y2,
86                    startAngle, sweepAngle);
87
88     for(i = 0; i < count; i++){
89         path->pathdata.Types[old_count + i] = PathPointTypeBezier;
90     }
91
92     path->pathdata.Types[old_count] =
93         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
94     path->newfigure = FALSE;
95     path->pathdata.Count += count;
96
97     return Ok;
98 }
99
100 GpStatus WINGDIPAPI GdipAddPathBeziers(GpPath *path, GDIPCONST GpPointF *points,
101     INT count)
102 {
103     INT i, old_count;
104
105     if(!path || !points || ((count - 1) % 3))
106         return InvalidParameter;
107
108     if(!lengthen_path(path, count))
109         return OutOfMemory;
110
111     old_count = path->pathdata.Count;
112
113     for(i = 0; i < count; i++){
114         path->pathdata.Points[old_count + i].X = points[i].X;
115         path->pathdata.Points[old_count + i].Y = points[i].Y;
116         path->pathdata.Types[old_count + i] = PathPointTypeBezier;
117     }
118
119     path->pathdata.Types[old_count] =
120         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
121     path->newfigure = FALSE;
122     path->pathdata.Count += count;
123
124     return Ok;
125 }
126
127 GpStatus WINGDIPAPI GdipAddPathEllipse(GpPath *path, REAL x, REAL y, REAL width,
128     REAL height)
129 {
130     INT old_count, numpts;
131
132     if(!path)
133         return InvalidParameter;
134
135     if(!lengthen_path(path, MAX_ARC_PTS))
136         return OutOfMemory;
137
138     old_count = path->pathdata.Count;
139     if((numpts = arc2polybezier(&path->pathdata.Points[old_count],  x, y, width,
140                                height, 0.0, 360.0)) != MAX_ARC_PTS){
141         ERR("expected %d points but got %d\n", MAX_ARC_PTS, numpts);
142         return GenericError;
143     }
144
145     memset(&path->pathdata.Types[old_count + 1], PathPointTypeBezier,
146            MAX_ARC_PTS - 1);
147
148     /* An ellipse is an instrinsic figure (always its own subpath). */
149     path->pathdata.Types[old_count] = PathPointTypeStart;
150     path->pathdata.Types[old_count + MAX_ARC_PTS - 1] |= PathPointTypeCloseSubpath;
151     path->newfigure = TRUE;
152     path->pathdata.Count += MAX_ARC_PTS;
153
154     return Ok;
155 }
156
157 GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points,
158     INT count)
159 {
160     INT i, old_count;
161
162     if(!path || !points)
163         return InvalidParameter;
164
165     if(!lengthen_path(path, count))
166         return OutOfMemory;
167
168     old_count = path->pathdata.Count;
169
170     for(i = 0; i < count; i++){
171         path->pathdata.Points[old_count + i].X = points[i].X;
172         path->pathdata.Points[old_count + i].Y = points[i].Y;
173         path->pathdata.Types[old_count + i] = PathPointTypeLine;
174     }
175
176     if(path->newfigure){
177         path->pathdata.Types[old_count] = PathPointTypeStart;
178         path->newfigure = FALSE;
179     }
180
181     path->pathdata.Count += count;
182
183     return Ok;
184 }
185
186 GpStatus WINGDIPAPI GdipAddPathPath(GpPath *path, GDIPCONST GpPath* addingPath,
187     BOOL connect)
188 {
189     INT old_count, count;
190
191     if(!path || !addingPath)
192         return InvalidParameter;
193
194     old_count = path->pathdata.Count;
195     count = addingPath->pathdata.Count;
196
197     if(!lengthen_path(path, count))
198         return OutOfMemory;
199
200     memcpy(&path->pathdata.Points[old_count], addingPath->pathdata.Points,
201            count * sizeof(GpPointF));
202     memcpy(&path->pathdata.Types[old_count], addingPath->pathdata.Types, count);
203
204     if(path->newfigure || !connect)
205         path->pathdata.Types[old_count] = PathPointTypeStart;
206     else
207         path->pathdata.Types[old_count] = PathPointTypeLine;
208
209     path->newfigure = FALSE;
210     path->pathdata.Count += count;
211
212     return Ok;
213 }
214
215 GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
216 {
217     if(!path || !clone)
218         return InvalidParameter;
219
220     *clone = GdipAlloc(sizeof(GpPath));
221     if(!*clone) return OutOfMemory;
222
223     memcpy(*clone, path, sizeof(GpPath));
224
225     (*clone)->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
226     (*clone)->pathdata.Types = GdipAlloc(path->datalen);
227     if(!(*clone)->pathdata.Points || !(*clone)->pathdata.Types){
228         GdipFree(*clone);
229         GdipFree((*clone)->pathdata.Points);
230         GdipFree((*clone)->pathdata.Types);
231         return OutOfMemory;
232     }
233
234     memcpy((*clone)->pathdata.Points, path->pathdata.Points,
235            path->datalen * sizeof(PointF));
236     memcpy((*clone)->pathdata.Types, path->pathdata.Types, path->datalen);
237
238     return Ok;
239 }
240
241 GpStatus WINGDIPAPI GdipClosePathFigure(GpPath* path)
242 {
243     if(!path)
244         return InvalidParameter;
245
246     if(path->pathdata.Count > 0){
247         path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
248         path->newfigure = TRUE;
249     }
250
251     return Ok;
252 }
253
254 GpStatus WINGDIPAPI GdipClosePathFigures(GpPath* path)
255 {
256     INT i;
257
258     if(!path)
259         return InvalidParameter;
260
261     for(i = 1; i < path->pathdata.Count; i++){
262         if(path->pathdata.Types[i] == PathPointTypeStart)
263             path->pathdata.Types[i-1] |= PathPointTypeCloseSubpath;
264     }
265
266     path->newfigure = TRUE;
267
268     return Ok;
269 }
270
271 GpStatus WINGDIPAPI GdipCreatePath(GpFillMode fill, GpPath **path)
272 {
273     if(!path)
274         return InvalidParameter;
275
276     *path = GdipAlloc(sizeof(GpPath));
277     if(!*path)  return OutOfMemory;
278
279     (*path)->fill = fill;
280     (*path)->newfigure = TRUE;
281
282     return Ok;
283 }
284
285 GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
286 {
287     if(!path)
288         return InvalidParameter;
289
290     GdipFree(path->pathdata.Points);
291     GdipFree(path->pathdata.Types);
292     GdipFree(path);
293
294     return Ok;
295 }
296
297 GpStatus WINGDIPAPI GdipGetPathFillMode(GpPath *path, GpFillMode *fillmode)
298 {
299     if(!path || !fillmode)
300         return InvalidParameter;
301
302     *fillmode = path->fill;
303
304     return Ok;
305 }
306
307 GpStatus WINGDIPAPI GdipGetPathPoints(GpPath *path, GpPointF* points, INT count)
308 {
309     if(!path)
310         return InvalidParameter;
311
312     if(count < path->pathdata.Count)
313         return InsufficientBuffer;
314
315     memcpy(points, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF));
316
317     return Ok;
318 }
319
320 GpStatus WINGDIPAPI GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
321 {
322     if(!path)
323         return InvalidParameter;
324
325     if(count < path->pathdata.Count)
326         return InsufficientBuffer;
327
328     memcpy(types, path->pathdata.Types, path->pathdata.Count);
329
330     return Ok;
331 }
332
333 /* Windows expands the bounding box to the maximum possible bounding box
334  * for a given pen.  For example, if a line join can extend past the point
335  * it's joining by x units, the bounding box is extended by x units in every
336  * direction (even though this is too conservative for most cases). */
337 GpStatus WINGDIPAPI GdipGetPathWorldBounds(GpPath* path, GpRectF* bounds,
338     GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
339 {
340     GpPointF * points, temp_pts[4];
341     INT count, i;
342     REAL path_width = 1.0, width, height, temp, low_x, low_y, high_x, high_y;
343
344     /* Matrix and pen can be null. */
345     if(!path || !bounds)
346         return InvalidParameter;
347
348     /* If path is empty just return. */
349     count = path->pathdata.Count;
350     if(count == 0){
351         bounds->X = bounds->Y = bounds->Width = bounds->Height = 0.0;
352         return Ok;
353     }
354
355     points = path->pathdata.Points;
356
357     low_x = high_x = points[0].X;
358     low_y = high_y = points[0].Y;
359
360     for(i = 1; i < count; i++){
361         low_x = min(low_x, points[i].X);
362         low_y = min(low_y, points[i].Y);
363         high_x = max(high_x, points[i].X);
364         high_y = max(high_y, points[i].Y);
365     }
366
367     width = high_x - low_x;
368     height = high_y - low_y;
369
370     /* This looks unusual but it's the only way I can imitate windows. */
371     if(matrix){
372         temp_pts[0].X = low_x;
373         temp_pts[0].Y = low_y;
374         temp_pts[1].X = low_x;
375         temp_pts[1].Y = high_y;
376         temp_pts[2].X = high_x;
377         temp_pts[2].Y = high_y;
378         temp_pts[3].X = high_x;
379         temp_pts[3].Y = low_y;
380
381         GdipTransformMatrixPoints((GpMatrix*)matrix, temp_pts, 4);
382         low_x = temp_pts[0].X;
383         low_y = temp_pts[0].Y;
384
385         for(i = 1; i < 4; i++){
386             low_x = min(low_x, temp_pts[i].X);
387             low_y = min(low_y, temp_pts[i].Y);
388         }
389
390         temp = width;
391         width = height * fabs(matrix->matrix[2]) + width * fabs(matrix->matrix[0]);
392         height = height * fabs(matrix->matrix[3]) + temp * fabs(matrix->matrix[1]);
393     }
394
395     if(pen){
396         path_width = pen->width / 2.0;
397
398         if(count > 2)
399             path_width = max(path_width,  pen->width * pen->miterlimit / 2.0);
400         /* FIXME: this should probably also check for the startcap */
401         if(pen->endcap & LineCapNoAnchor)
402             path_width = max(path_width,  pen->width * 2.2);
403
404         low_x -= path_width;
405         low_y -= path_width;
406         width += 2.0 * path_width;
407         height += 2.0 * path_width;
408     }
409
410     bounds->X = low_x;
411     bounds->Y = low_y;
412     bounds->Width = width;
413     bounds->Height = height;
414
415     return Ok;
416 }
417
418 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
419 {
420     if(!path)
421         return InvalidParameter;
422
423     *count = path->pathdata.Count;
424
425     return Ok;
426 }
427
428 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPointI(GpPath* path, INT x, INT y,
429     GpPen *pen, GpGraphics *graphics, BOOL *result)
430 {
431     static int calls;
432
433     if(!path || !pen)
434         return InvalidParameter;
435
436     if(!(calls++))
437         FIXME("not implemented\n");
438
439     return NotImplemented;
440 }
441
442 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
443 {
444     if(!path)
445         return InvalidParameter;
446
447     path->newfigure = TRUE;
448
449     return Ok;
450 }
451
452 GpStatus WINGDIPAPI GdipResetPath(GpPath *path)
453 {
454     if(!path)
455         return InvalidParameter;
456
457     path->pathdata.Count = 0;
458     path->newfigure = TRUE;
459     path->fill = FillModeAlternate;
460
461     return Ok;
462 }
463
464 GpStatus WINGDIPAPI GdipSetPathFillMode(GpPath *path, GpFillMode fill)
465 {
466     if(!path)
467         return InvalidParameter;
468
469     path->fill = fill;
470
471     return Ok;
472 }
473
474 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
475 {
476     if(!path)
477         return InvalidParameter;
478
479     if(path->pathdata.Count == 0)
480         return Ok;
481
482     return GdipTransformMatrixPoints(matrix, (GpPointF*) path->pathdata.Points,
483                                      path->pathdata.Count);
484 }