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