quartz: Make dwSamplesProcessed a longlong.
[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 GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
506 {
507     if(!path)
508         return InvalidParameter;
509
510     if(count < path->pathdata.Count)
511         return InsufficientBuffer;
512
513     memcpy(types, path->pathdata.Types, path->pathdata.Count);
514
515     return Ok;
516 }
517
518 /* Windows expands the bounding box to the maximum possible bounding box
519  * for a given pen.  For example, if a line join can extend past the point
520  * it's joining by x units, the bounding box is extended by x units in every
521  * direction (even though this is too conservative for most cases). */
522 GpStatus WINGDIPAPI GdipGetPathWorldBounds(GpPath* path, GpRectF* bounds,
523     GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
524 {
525     GpPointF * points, temp_pts[4];
526     INT count, i;
527     REAL path_width = 1.0, width, height, temp, low_x, low_y, high_x, high_y;
528
529     /* Matrix and pen can be null. */
530     if(!path || !bounds)
531         return InvalidParameter;
532
533     /* If path is empty just return. */
534     count = path->pathdata.Count;
535     if(count == 0){
536         bounds->X = bounds->Y = bounds->Width = bounds->Height = 0.0;
537         return Ok;
538     }
539
540     points = path->pathdata.Points;
541
542     low_x = high_x = points[0].X;
543     low_y = high_y = points[0].Y;
544
545     for(i = 1; i < count; i++){
546         low_x = min(low_x, points[i].X);
547         low_y = min(low_y, points[i].Y);
548         high_x = max(high_x, points[i].X);
549         high_y = max(high_y, points[i].Y);
550     }
551
552     width = high_x - low_x;
553     height = high_y - low_y;
554
555     /* This looks unusual but it's the only way I can imitate windows. */
556     if(matrix){
557         temp_pts[0].X = low_x;
558         temp_pts[0].Y = low_y;
559         temp_pts[1].X = low_x;
560         temp_pts[1].Y = high_y;
561         temp_pts[2].X = high_x;
562         temp_pts[2].Y = high_y;
563         temp_pts[3].X = high_x;
564         temp_pts[3].Y = low_y;
565
566         GdipTransformMatrixPoints((GpMatrix*)matrix, temp_pts, 4);
567         low_x = temp_pts[0].X;
568         low_y = temp_pts[0].Y;
569
570         for(i = 1; i < 4; i++){
571             low_x = min(low_x, temp_pts[i].X);
572             low_y = min(low_y, temp_pts[i].Y);
573         }
574
575         temp = width;
576         width = height * fabs(matrix->matrix[2]) + width * fabs(matrix->matrix[0]);
577         height = height * fabs(matrix->matrix[3]) + temp * fabs(matrix->matrix[1]);
578     }
579
580     if(pen){
581         path_width = pen->width / 2.0;
582
583         if(count > 2)
584             path_width = max(path_width,  pen->width * pen->miterlimit / 2.0);
585         /* FIXME: this should probably also check for the startcap */
586         if(pen->endcap & LineCapNoAnchor)
587             path_width = max(path_width,  pen->width * 2.2);
588
589         low_x -= path_width;
590         low_y -= path_width;
591         width += 2.0 * path_width;
592         height += 2.0 * path_width;
593     }
594
595     bounds->X = low_x;
596     bounds->Y = low_y;
597     bounds->Width = width;
598     bounds->Height = height;
599
600     return Ok;
601 }
602
603 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
604 {
605     if(!path)
606         return InvalidParameter;
607
608     *count = path->pathdata.Count;
609
610     return Ok;
611 }
612
613 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPointI(GpPath* path, INT x, INT y,
614     GpPen *pen, GpGraphics *graphics, BOOL *result)
615 {
616     static int calls;
617
618     if(!path || !pen)
619         return InvalidParameter;
620
621     if(!(calls++))
622         FIXME("not implemented\n");
623
624     return NotImplemented;
625 }
626
627 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
628 {
629     if(!path)
630         return InvalidParameter;
631
632     path->newfigure = TRUE;
633
634     return Ok;
635 }
636
637 GpStatus WINGDIPAPI GdipResetPath(GpPath *path)
638 {
639     if(!path)
640         return InvalidParameter;
641
642     path->pathdata.Count = 0;
643     path->newfigure = TRUE;
644     path->fill = FillModeAlternate;
645
646     return Ok;
647 }
648
649 GpStatus WINGDIPAPI GdipSetPathFillMode(GpPath *path, GpFillMode fill)
650 {
651     if(!path)
652         return InvalidParameter;
653
654     path->fill = fill;
655
656     return Ok;
657 }
658
659 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
660 {
661     if(!path)
662         return InvalidParameter;
663
664     if(path->pathdata.Count == 0)
665         return Ok;
666
667     return GdipTransformMatrixPoints(matrix, path->pathdata.Points,
668                                      path->pathdata.Count);
669 }