push 4d5485f9b89f417d46b39b93e8d940437007f325
[wine/hacks.git] / dlls / gdiplus / graphicspath.c
blobed71400ab62140657e521a578f5fc5390e14260b
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
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.
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.
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
20 #include <stdarg.h>
21 #include <math.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "wingdi.h"
28 #include "objbase.h"
30 #include "gdiplus.h"
31 #include "gdiplus_private.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
36 /* make sure path has enough space for len more points */
37 static BOOL lengthen_path(GpPath *path, INT len)
39 /* initial allocation */
40 if(path->datalen == 0){
41 path->datalen = len * 2;
43 path->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
44 if(!path->pathdata.Points) return FALSE;
46 path->pathdata.Types = GdipAlloc(path->datalen);
47 if(!path->pathdata.Types){
48 GdipFree(path->pathdata.Points);
49 return FALSE;
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;
57 path->pathdata.Points = HeapReAlloc(GetProcessHeap(), 0,
58 path->pathdata.Points, path->datalen * sizeof(PointF));
59 if(!path->pathdata.Points) return FALSE;
61 path->pathdata.Types = HeapReAlloc(GetProcessHeap(), 0,
62 path->pathdata.Types, path->datalen);
63 if(!path->pathdata.Types) return FALSE;
66 return TRUE;
69 GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
70 REAL y2, REAL startAngle, REAL sweepAngle)
72 INT count, old_count, i;
74 if(!path)
75 return InvalidParameter;
77 count = arc2polybezier(NULL, x1, y1, x2, y2, startAngle, sweepAngle);
79 if(count == 0)
80 return Ok;
81 if(!lengthen_path(path, count))
82 return OutOfMemory;
84 old_count = path->pathdata.Count;
85 arc2polybezier(&path->pathdata.Points[old_count], x1, y1, x2, y2,
86 startAngle, sweepAngle);
88 for(i = 0; i < count; i++){
89 path->pathdata.Types[old_count + i] = PathPointTypeBezier;
92 path->pathdata.Types[old_count] =
93 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
94 path->newfigure = FALSE;
95 path->pathdata.Count += count;
97 return Ok;
100 GpStatus WINGDIPAPI GdipAddPathBezierI(GpPath *path, INT x1, INT y1, INT x2,
101 INT y2, INT x3, INT y3, INT x4, INT y4)
103 INT old_count;
105 if(!path)
106 return InvalidParameter;
108 if(!lengthen_path(path, 4))
109 return OutOfMemory;
111 old_count = path->pathdata.Count;
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;
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;
128 path->newfigure = FALSE;
129 path->pathdata.Count += 4;
131 return Ok;
134 GpStatus WINGDIPAPI GdipAddPathBeziers(GpPath *path, GDIPCONST GpPointF *points,
135 INT count)
137 INT i, old_count;
139 if(!path || !points || ((count - 1) % 3))
140 return InvalidParameter;
142 if(!lengthen_path(path, count))
143 return OutOfMemory;
145 old_count = path->pathdata.Count;
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;
153 path->pathdata.Types[old_count] =
154 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
155 path->newfigure = FALSE;
156 path->pathdata.Count += count;
158 return Ok;
161 GpStatus WINGDIPAPI GdipAddPathEllipse(GpPath *path, REAL x, REAL y, REAL width,
162 REAL height)
164 INT old_count, numpts;
166 if(!path)
167 return InvalidParameter;
169 if(!lengthen_path(path, MAX_ARC_PTS))
170 return OutOfMemory;
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;
179 memset(&path->pathdata.Types[old_count + 1], PathPointTypeBezier,
180 MAX_ARC_PTS - 1);
182 /* An ellipse is an instrinsic figure (always 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;
188 return Ok;
191 GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points,
192 INT count)
194 INT i, old_count;
196 if(!path || !points)
197 return InvalidParameter;
199 if(!lengthen_path(path, count))
200 return OutOfMemory;
202 old_count = path->pathdata.Count;
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;
210 if(path->newfigure){
211 path->pathdata.Types[old_count] = PathPointTypeStart;
212 path->newfigure = FALSE;
215 path->pathdata.Count += count;
217 return Ok;
220 GpStatus WINGDIPAPI GdipAddPathLineI(GpPath *path, INT x1, INT y1, INT x2, INT y2)
222 INT old_count;
224 if(!path)
225 return InvalidParameter;
227 if(!lengthen_path(path, 2))
228 return OutOfMemory;
230 old_count = path->pathdata.Count;
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;
237 path->pathdata.Types[old_count] =
238 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
239 path->pathdata.Types[old_count + 1] = PathPointTypeLine;
241 path->newfigure = FALSE;
242 path->pathdata.Count += 2;
244 return Ok;
247 GpStatus WINGDIPAPI GdipAddPathPath(GpPath *path, GDIPCONST GpPath* addingPath,
248 BOOL connect)
250 INT old_count, count;
252 if(!path || !addingPath)
253 return InvalidParameter;
255 old_count = path->pathdata.Count;
256 count = addingPath->pathdata.Count;
258 if(!lengthen_path(path, count))
259 return OutOfMemory;
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);
265 if(path->newfigure || !connect)
266 path->pathdata.Types[old_count] = PathPointTypeStart;
267 else
268 path->pathdata.Types[old_count] = PathPointTypeLine;
270 path->newfigure = FALSE;
271 path->pathdata.Count += count;
273 return Ok;
276 GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
278 if(!path || !clone)
279 return InvalidParameter;
281 *clone = GdipAlloc(sizeof(GpPath));
282 if(!*clone) return OutOfMemory;
284 memcpy(*clone, path, sizeof(GpPath));
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;
295 memcpy((*clone)->pathdata.Points, path->pathdata.Points,
296 path->datalen * sizeof(PointF));
297 memcpy((*clone)->pathdata.Types, path->pathdata.Types, path->datalen);
299 return Ok;
302 GpStatus WINGDIPAPI GdipClosePathFigure(GpPath* path)
304 if(!path)
305 return InvalidParameter;
307 if(path->pathdata.Count > 0){
308 path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
309 path->newfigure = TRUE;
312 return Ok;
315 GpStatus WINGDIPAPI GdipClosePathFigures(GpPath* path)
317 INT i;
319 if(!path)
320 return InvalidParameter;
322 for(i = 1; i < path->pathdata.Count; i++){
323 if(path->pathdata.Types[i] == PathPointTypeStart)
324 path->pathdata.Types[i-1] |= PathPointTypeCloseSubpath;
327 path->newfigure = TRUE;
329 return Ok;
332 GpStatus WINGDIPAPI GdipCreatePath(GpFillMode fill, GpPath **path)
334 if(!path)
335 return InvalidParameter;
337 *path = GdipAlloc(sizeof(GpPath));
338 if(!*path) return OutOfMemory;
340 (*path)->fill = fill;
341 (*path)->newfigure = TRUE;
343 return Ok;
346 GpStatus WINGDIPAPI GdipCreatePath2(GDIPCONST GpPointF* points,
347 GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
349 if(!path)
350 return InvalidParameter;
352 *path = GdipAlloc(sizeof(GpPath));
353 if(!*path) return OutOfMemory;
355 (*path)->pathdata.Points = GdipAlloc(count * sizeof(PointF));
356 (*path)->pathdata.Types = GdipAlloc(count);
358 if(!(*path)->pathdata.Points || !(*path)->pathdata.Types){
359 GdipFree((*path)->pathdata.Points);
360 GdipFree((*path)->pathdata.Types);
361 GdipFree(*path);
362 return OutOfMemory;
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;
370 (*path)->fill = fill;
371 (*path)->newfigure = TRUE;
373 return Ok;
376 GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
378 if(!path)
379 return InvalidParameter;
381 GdipFree(path->pathdata.Points);
382 GdipFree(path->pathdata.Types);
383 GdipFree(path);
385 return Ok;
388 GpStatus WINGDIPAPI GdipGetPathFillMode(GpPath *path, GpFillMode *fillmode)
390 if(!path || !fillmode)
391 return InvalidParameter;
393 *fillmode = path->fill;
395 return Ok;
398 GpStatus WINGDIPAPI GdipGetPathPoints(GpPath *path, GpPointF* points, INT count)
400 if(!path)
401 return InvalidParameter;
403 if(count < path->pathdata.Count)
404 return InsufficientBuffer;
406 memcpy(points, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF));
408 return Ok;
411 GpStatus WINGDIPAPI GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
413 if(!path)
414 return InvalidParameter;
416 if(count < path->pathdata.Count)
417 return InsufficientBuffer;
419 memcpy(types, path->pathdata.Types, path->pathdata.Count);
421 return Ok;
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)
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;
435 /* Matrix and pen can be null. */
436 if(!path || !bounds)
437 return InvalidParameter;
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;
446 points = path->pathdata.Points;
448 low_x = high_x = points[0].X;
449 low_y = high_y = points[0].Y;
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);
458 width = high_x - low_x;
459 height = high_y - low_y;
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;
472 GdipTransformMatrixPoints((GpMatrix*)matrix, temp_pts, 4);
473 low_x = temp_pts[0].X;
474 low_y = temp_pts[0].Y;
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);
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]);
486 if(pen){
487 path_width = pen->width / 2.0;
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);
495 low_x -= path_width;
496 low_y -= path_width;
497 width += 2.0 * path_width;
498 height += 2.0 * path_width;
501 bounds->X = low_x;
502 bounds->Y = low_y;
503 bounds->Width = width;
504 bounds->Height = height;
506 return Ok;
509 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
511 if(!path)
512 return InvalidParameter;
514 *count = path->pathdata.Count;
516 return Ok;
519 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPointI(GpPath* path, INT x, INT y,
520 GpPen *pen, GpGraphics *graphics, BOOL *result)
522 static int calls;
524 if(!path || !pen)
525 return InvalidParameter;
527 if(!(calls++))
528 FIXME("not implemented\n");
530 return NotImplemented;
533 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
535 if(!path)
536 return InvalidParameter;
538 path->newfigure = TRUE;
540 return Ok;
543 GpStatus WINGDIPAPI GdipResetPath(GpPath *path)
545 if(!path)
546 return InvalidParameter;
548 path->pathdata.Count = 0;
549 path->newfigure = TRUE;
550 path->fill = FillModeAlternate;
552 return Ok;
555 GpStatus WINGDIPAPI GdipSetPathFillMode(GpPath *path, GpFillMode fill)
557 if(!path)
558 return InvalidParameter;
560 path->fill = fill;
562 return Ok;
565 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
567 if(!path)
568 return InvalidParameter;
570 if(path->pathdata.Count == 0)
571 return Ok;
573 return GdipTransformMatrixPoints(matrix, (GpPointF*) path->pathdata.Points,
574 path->pathdata.Count);