msi: Fix a typo.
[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 GdipAddPathArcI(GpPath *path, INT x1, INT y1, INT x2,
101    INT y2, REAL startAngle, REAL sweepAngle)
102 {
103     return GdipAddPathArc(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,startAngle,sweepAngle);
104 }
105
106 GpStatus WINGDIPAPI GdipAddPathBezier(GpPath *path, REAL x1, REAL y1, REAL x2,
107     REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
108 {
109     INT old_count;
110
111     if(!path)
112         return InvalidParameter;
113
114     if(!lengthen_path(path, 4))
115         return OutOfMemory;
116
117     old_count = path->pathdata.Count;
118
119     path->pathdata.Points[old_count].X = x1;
120     path->pathdata.Points[old_count].Y = y1;
121     path->pathdata.Points[old_count + 1].X = x2;
122     path->pathdata.Points[old_count + 1].Y = y2;
123     path->pathdata.Points[old_count + 2].X = x3;
124     path->pathdata.Points[old_count + 2].Y = y3;
125     path->pathdata.Points[old_count + 3].X = x4;
126     path->pathdata.Points[old_count + 3].Y = y4;
127
128     path->pathdata.Types[old_count] =
129         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
130     path->pathdata.Types[old_count + 1] = PathPointTypeBezier;
131     path->pathdata.Types[old_count + 2] = PathPointTypeBezier;
132     path->pathdata.Types[old_count + 3] = PathPointTypeBezier;
133
134     path->newfigure = FALSE;
135     path->pathdata.Count += 4;
136
137     return Ok;
138 }
139
140 GpStatus WINGDIPAPI GdipAddPathBezierI(GpPath *path, INT x1, INT y1, INT x2,
141     INT y2, INT x3, INT y3, INT x4, INT y4)
142 {
143     return GdipAddPathBezier(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,(REAL)x3,(REAL)y3,
144                                   (REAL)x4,(REAL)y4);
145 }
146
147 GpStatus WINGDIPAPI GdipAddPathBeziers(GpPath *path, GDIPCONST GpPointF *points,
148     INT count)
149 {
150     INT i, old_count;
151
152     if(!path || !points || ((count - 1) % 3))
153         return InvalidParameter;
154
155     if(!lengthen_path(path, count))
156         return OutOfMemory;
157
158     old_count = path->pathdata.Count;
159
160     for(i = 0; i < count; i++){
161         path->pathdata.Points[old_count + i].X = points[i].X;
162         path->pathdata.Points[old_count + i].Y = points[i].Y;
163         path->pathdata.Types[old_count + i] = PathPointTypeBezier;
164     }
165
166     path->pathdata.Types[old_count] =
167         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
168     path->newfigure = FALSE;
169     path->pathdata.Count += count;
170
171     return Ok;
172 }
173
174 GpStatus WINGDIPAPI GdipAddPathBeziersI(GpPath *path, GDIPCONST GpPoint *points,
175     INT count)
176 {
177     GpPointF *ptsF;
178     GpStatus ret;
179     INT i;
180
181     if(!points || ((count - 1) % 3))
182         return InvalidParameter;
183
184     ptsF = GdipAlloc(sizeof(GpPointF) * count);
185     if(!ptsF)
186         return OutOfMemory;
187
188     for(i = 0; i < count; i++){
189         ptsF[i].X = (REAL)points[i].X;
190         ptsF[i].Y = (REAL)points[i].Y;
191     }
192
193     ret = GdipAddPathBeziers(path, ptsF, count);
194     GdipFree(ptsF);
195
196     return ret;
197 }
198
199 GpStatus WINGDIPAPI GdipAddPathEllipse(GpPath *path, REAL x, REAL y, REAL width,
200     REAL height)
201 {
202     INT old_count, numpts;
203
204     if(!path)
205         return InvalidParameter;
206
207     if(!lengthen_path(path, MAX_ARC_PTS))
208         return OutOfMemory;
209
210     old_count = path->pathdata.Count;
211     if((numpts = arc2polybezier(&path->pathdata.Points[old_count],  x, y, width,
212                                height, 0.0, 360.0)) != MAX_ARC_PTS){
213         ERR("expected %d points but got %d\n", MAX_ARC_PTS, numpts);
214         return GenericError;
215     }
216
217     memset(&path->pathdata.Types[old_count + 1], PathPointTypeBezier,
218            MAX_ARC_PTS - 1);
219
220     /* An ellipse is an intrinsic figure (always is its own subpath). */
221     path->pathdata.Types[old_count] = PathPointTypeStart;
222     path->pathdata.Types[old_count + MAX_ARC_PTS - 1] |= PathPointTypeCloseSubpath;
223     path->newfigure = TRUE;
224     path->pathdata.Count += MAX_ARC_PTS;
225
226     return Ok;
227 }
228
229 GpStatus WINGDIPAPI GdipAddPathEllipseI(GpPath *path, INT x, INT y, INT width,
230     INT height)
231 {
232     return GdipAddPathEllipse(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
233 }
234
235 GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points,
236     INT count)
237 {
238     INT i, old_count;
239
240     if(!path || !points)
241         return InvalidParameter;
242
243     if(!lengthen_path(path, count))
244         return OutOfMemory;
245
246     old_count = path->pathdata.Count;
247
248     for(i = 0; i < count; i++){
249         path->pathdata.Points[old_count + i].X = points[i].X;
250         path->pathdata.Points[old_count + i].Y = points[i].Y;
251         path->pathdata.Types[old_count + i] = PathPointTypeLine;
252     }
253
254     if(path->newfigure){
255         path->pathdata.Types[old_count] = PathPointTypeStart;
256         path->newfigure = FALSE;
257     }
258
259     path->pathdata.Count += count;
260
261     return Ok;
262 }
263
264 GpStatus WINGDIPAPI GdipAddPathLine2I(GpPath *path, GDIPCONST GpPoint *points, INT count)
265 {
266     GpPointF *pointsF;
267     INT i;
268     GpStatus stat;
269
270     if(count <= 0)
271         return InvalidParameter;
272
273     pointsF = GdipAlloc(sizeof(GpPointF) * count);
274     if(!pointsF)    return OutOfMemory;
275
276     for(i = 0;i < count; i++){
277         pointsF[i].X = (REAL)points[i].X;
278         pointsF[i].Y = (REAL)points[i].Y;
279     }
280
281     stat = GdipAddPathLine2(path, pointsF, count);
282
283     GdipFree(pointsF);
284
285     return stat;
286 }
287
288 GpStatus WINGDIPAPI GdipAddPathLine(GpPath *path, REAL x1, REAL y1, REAL x2, REAL y2)
289 {
290     INT old_count;
291
292     if(!path)
293         return InvalidParameter;
294
295     if(!lengthen_path(path, 2))
296         return OutOfMemory;
297
298     old_count = path->pathdata.Count;
299
300     path->pathdata.Points[old_count].X = x1;
301     path->pathdata.Points[old_count].Y = y1;
302     path->pathdata.Points[old_count + 1].X = x2;
303     path->pathdata.Points[old_count + 1].Y = y2;
304
305     path->pathdata.Types[old_count] =
306         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
307     path->pathdata.Types[old_count + 1] = PathPointTypeLine;
308
309     path->newfigure = FALSE;
310     path->pathdata.Count += 2;
311
312     return Ok;
313 }
314
315 GpStatus WINGDIPAPI GdipAddPathLineI(GpPath *path, INT x1, INT y1, INT x2, INT y2)
316 {
317     return GdipAddPathLine(path, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
318 }
319
320 GpStatus WINGDIPAPI GdipAddPathPath(GpPath *path, GDIPCONST GpPath* addingPath,
321     BOOL connect)
322 {
323     INT old_count, count;
324
325     if(!path || !addingPath)
326         return InvalidParameter;
327
328     old_count = path->pathdata.Count;
329     count = addingPath->pathdata.Count;
330
331     if(!lengthen_path(path, count))
332         return OutOfMemory;
333
334     memcpy(&path->pathdata.Points[old_count], addingPath->pathdata.Points,
335            count * sizeof(GpPointF));
336     memcpy(&path->pathdata.Types[old_count], addingPath->pathdata.Types, count);
337
338     if(path->newfigure || !connect)
339         path->pathdata.Types[old_count] = PathPointTypeStart;
340     else
341         path->pathdata.Types[old_count] = PathPointTypeLine;
342
343     path->newfigure = FALSE;
344     path->pathdata.Count += count;
345
346     return Ok;
347 }
348
349 GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
350 {
351     if(!path || !clone)
352         return InvalidParameter;
353
354     *clone = GdipAlloc(sizeof(GpPath));
355     if(!*clone) return OutOfMemory;
356
357     **clone = *path;
358
359     (*clone)->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
360     (*clone)->pathdata.Types = GdipAlloc(path->datalen);
361     if(!(*clone)->pathdata.Points || !(*clone)->pathdata.Types){
362         GdipFree(*clone);
363         GdipFree((*clone)->pathdata.Points);
364         GdipFree((*clone)->pathdata.Types);
365         return OutOfMemory;
366     }
367
368     memcpy((*clone)->pathdata.Points, path->pathdata.Points,
369            path->datalen * sizeof(PointF));
370     memcpy((*clone)->pathdata.Types, path->pathdata.Types, path->datalen);
371
372     return Ok;
373 }
374
375 GpStatus WINGDIPAPI GdipClosePathFigure(GpPath* path)
376 {
377     if(!path)
378         return InvalidParameter;
379
380     if(path->pathdata.Count > 0){
381         path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
382         path->newfigure = TRUE;
383     }
384
385     return Ok;
386 }
387
388 GpStatus WINGDIPAPI GdipClosePathFigures(GpPath* path)
389 {
390     INT i;
391
392     if(!path)
393         return InvalidParameter;
394
395     for(i = 1; i < path->pathdata.Count; i++){
396         if(path->pathdata.Types[i] == PathPointTypeStart)
397             path->pathdata.Types[i-1] |= PathPointTypeCloseSubpath;
398     }
399
400     path->newfigure = TRUE;
401
402     return Ok;
403 }
404
405 GpStatus WINGDIPAPI GdipCreatePath(GpFillMode fill, GpPath **path)
406 {
407     if(!path)
408         return InvalidParameter;
409
410     *path = GdipAlloc(sizeof(GpPath));
411     if(!*path)  return OutOfMemory;
412
413     (*path)->fill = fill;
414     (*path)->newfigure = TRUE;
415
416     return Ok;
417 }
418
419 GpStatus WINGDIPAPI GdipCreatePath2(GDIPCONST GpPointF* points,
420     GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
421 {
422     if(!path)
423         return InvalidParameter;
424
425     *path = GdipAlloc(sizeof(GpPath));
426     if(!*path)  return OutOfMemory;
427
428     (*path)->pathdata.Points = GdipAlloc(count * sizeof(PointF));
429     (*path)->pathdata.Types = GdipAlloc(count);
430
431     if(!(*path)->pathdata.Points || !(*path)->pathdata.Types){
432         GdipFree((*path)->pathdata.Points);
433         GdipFree((*path)->pathdata.Types);
434         GdipFree(*path);
435         return OutOfMemory;
436     }
437
438     memcpy((*path)->pathdata.Points, points, count * sizeof(PointF));
439     memcpy((*path)->pathdata.Types, types, count);
440     (*path)->pathdata.Count = count;
441     (*path)->datalen = count;
442
443     (*path)->fill = fill;
444     (*path)->newfigure = TRUE;
445
446     return Ok;
447 }
448
449 GpStatus WINGDIPAPI GdipCreatePath2I(GDIPCONST GpPoint* points,
450     GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
451 {
452     GpPointF *ptF;
453     GpStatus ret;
454     INT i;
455
456     ptF = GdipAlloc(sizeof(GpPointF)*count);
457
458     for(i = 0;i < count; i++){
459         ptF[i].X = (REAL)points[i].X;
460         ptF[i].Y = (REAL)points[i].Y;
461     }
462
463     ret = GdipCreatePath2(ptF, types, count, fill, path);
464
465     GdipFree(ptF);
466
467     return ret;
468 }
469
470 GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
471 {
472     if(!path)
473         return InvalidParameter;
474
475     GdipFree(path->pathdata.Points);
476     GdipFree(path->pathdata.Types);
477     GdipFree(path);
478
479     return Ok;
480 }
481
482 GpStatus WINGDIPAPI GdipGetPathFillMode(GpPath *path, GpFillMode *fillmode)
483 {
484     if(!path || !fillmode)
485         return InvalidParameter;
486
487     *fillmode = path->fill;
488
489     return Ok;
490 }
491
492 GpStatus WINGDIPAPI GdipGetPathPoints(GpPath *path, GpPointF* points, INT count)
493 {
494     if(!path)
495         return InvalidParameter;
496
497     if(count < path->pathdata.Count)
498         return InsufficientBuffer;
499
500     memcpy(points, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF));
501
502     return Ok;
503 }
504
505 GpStatus WINGDIPAPI GdipGetPathPointsI(GpPath *path, GpPoint* points, INT count)
506 {
507     GpStatus ret;
508     GpPointF *ptf;
509     INT i;
510
511     if(count <= 0)
512         return InvalidParameter;
513
514     ptf = GdipAlloc(sizeof(GpPointF)*count);
515     if(!ptf)    return OutOfMemory;
516
517     ret = GdipGetPathPoints(path,ptf,count);
518     if(ret == Ok)
519         for(i = 0;i < count;i++){
520             points[i].X = roundr(ptf[i].X);
521             points[i].Y = roundr(ptf[i].Y);
522         };
523     GdipFree(ptf);
524
525     return ret;
526 }
527
528 GpStatus WINGDIPAPI GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
529 {
530     if(!path)
531         return InvalidParameter;
532
533     if(count < path->pathdata.Count)
534         return InsufficientBuffer;
535
536     memcpy(types, path->pathdata.Types, path->pathdata.Count);
537
538     return Ok;
539 }
540
541 /* Windows expands the bounding box to the maximum possible bounding box
542  * for a given pen.  For example, if a line join can extend past the point
543  * it's joining by x units, the bounding box is extended by x units in every
544  * direction (even though this is too conservative for most cases). */
545 GpStatus WINGDIPAPI GdipGetPathWorldBounds(GpPath* path, GpRectF* bounds,
546     GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
547 {
548     GpPointF * points, temp_pts[4];
549     INT count, i;
550     REAL path_width = 1.0, width, height, temp, low_x, low_y, high_x, high_y;
551
552     /* Matrix and pen can be null. */
553     if(!path || !bounds)
554         return InvalidParameter;
555
556     /* If path is empty just return. */
557     count = path->pathdata.Count;
558     if(count == 0){
559         bounds->X = bounds->Y = bounds->Width = bounds->Height = 0.0;
560         return Ok;
561     }
562
563     points = path->pathdata.Points;
564
565     low_x = high_x = points[0].X;
566     low_y = high_y = points[0].Y;
567
568     for(i = 1; i < count; i++){
569         low_x = min(low_x, points[i].X);
570         low_y = min(low_y, points[i].Y);
571         high_x = max(high_x, points[i].X);
572         high_y = max(high_y, points[i].Y);
573     }
574
575     width = high_x - low_x;
576     height = high_y - low_y;
577
578     /* This looks unusual but it's the only way I can imitate windows. */
579     if(matrix){
580         temp_pts[0].X = low_x;
581         temp_pts[0].Y = low_y;
582         temp_pts[1].X = low_x;
583         temp_pts[1].Y = high_y;
584         temp_pts[2].X = high_x;
585         temp_pts[2].Y = high_y;
586         temp_pts[3].X = high_x;
587         temp_pts[3].Y = low_y;
588
589         GdipTransformMatrixPoints((GpMatrix*)matrix, temp_pts, 4);
590         low_x = temp_pts[0].X;
591         low_y = temp_pts[0].Y;
592
593         for(i = 1; i < 4; i++){
594             low_x = min(low_x, temp_pts[i].X);
595             low_y = min(low_y, temp_pts[i].Y);
596         }
597
598         temp = width;
599         width = height * fabs(matrix->matrix[2]) + width * fabs(matrix->matrix[0]);
600         height = height * fabs(matrix->matrix[3]) + temp * fabs(matrix->matrix[1]);
601     }
602
603     if(pen){
604         path_width = pen->width / 2.0;
605
606         if(count > 2)
607             path_width = max(path_width,  pen->width * pen->miterlimit / 2.0);
608         /* FIXME: this should probably also check for the startcap */
609         if(pen->endcap & LineCapNoAnchor)
610             path_width = max(path_width,  pen->width * 2.2);
611
612         low_x -= path_width;
613         low_y -= path_width;
614         width += 2.0 * path_width;
615         height += 2.0 * path_width;
616     }
617
618     bounds->X = low_x;
619     bounds->Y = low_y;
620     bounds->Width = width;
621     bounds->Height = height;
622
623     return Ok;
624 }
625
626 GpStatus WINGDIPAPI GdipGetPathWorldBoundsI(GpPath* path, GpRect* bounds,
627     GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
628 {
629     GpStatus ret;
630     GpRectF boundsF;
631
632     ret = GdipGetPathWorldBounds(path,&boundsF,matrix,pen);
633
634     if(ret == Ok){
635         bounds->X      = roundr(boundsF.X);
636         bounds->Y      = roundr(boundsF.Y);
637         bounds->Width  = roundr(boundsF.Width);
638         bounds->Height = roundr(boundsF.Height);
639     }
640
641     return ret;
642 }
643
644 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
645 {
646     if(!path)
647         return InvalidParameter;
648
649     *count = path->pathdata.Count;
650
651     return Ok;
652 }
653
654 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPointI(GpPath* path, INT x, INT y,
655     GpPen *pen, GpGraphics *graphics, BOOL *result)
656 {
657     static int calls;
658
659     if(!path || !pen)
660         return InvalidParameter;
661
662     if(!(calls++))
663         FIXME("not implemented\n");
664
665     return NotImplemented;
666 }
667
668 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
669 {
670     if(!path)
671         return InvalidParameter;
672
673     path->newfigure = TRUE;
674
675     return Ok;
676 }
677
678 GpStatus WINGDIPAPI GdipResetPath(GpPath *path)
679 {
680     if(!path)
681         return InvalidParameter;
682
683     path->pathdata.Count = 0;
684     path->newfigure = TRUE;
685     path->fill = FillModeAlternate;
686
687     return Ok;
688 }
689
690 GpStatus WINGDIPAPI GdipSetPathFillMode(GpPath *path, GpFillMode fill)
691 {
692     if(!path)
693         return InvalidParameter;
694
695     path->fill = fill;
696
697     return Ok;
698 }
699
700 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
701 {
702     if(!path)
703         return InvalidParameter;
704
705     if(path->pathdata.Count == 0)
706         return Ok;
707
708     return GdipTransformMatrixPoints(matrix, path->pathdata.Points,
709                                      path->pathdata.Count);
710 }