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