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
27 #include "gdiplus_private.h"
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus
);
32 /* looks-right constants */
33 #define TENSION_CONST (0.3)
34 #define ANCHOR_WIDTH (2.0)
35 #define MAX_ITERS (50)
37 /* Converts angle (in degrees) to x/y coordinates */
38 static void deg2xy(REAL angle
, REAL x_0
, REAL y_0
, REAL
*x
, REAL
*y
)
40 REAL radAngle
, hypotenuse
;
42 radAngle
= deg2rad(angle
);
43 hypotenuse
= 50.0; /* arbitrary */
45 *x
= x_0
+ cos(radAngle
) * hypotenuse
;
46 *y
= y_0
+ sin(radAngle
) * hypotenuse
;
49 /* Converts from gdiplus path point type to gdi path point type. */
50 static BYTE
convert_path_point_type(BYTE type
)
54 switch(type
& PathPointTypePathTypeMask
){
55 case PathPointTypeBezier
:
58 case PathPointTypeLine
:
61 case PathPointTypeStart
:
65 ERR("Bad point type\n");
69 if(type
& PathPointTypeCloseSubpath
)
70 ret
|= PT_CLOSEFIGURE
;
75 /* GdipDrawPie/GdipFillPie helper function */
76 static GpStatus
draw_pie(GpGraphics
*graphics
, HBRUSH gdibrush
, HPEN gdipen
,
77 REAL x
, REAL y
, REAL width
, REAL height
, REAL startAngle
, REAL sweepAngle
)
80 REAL x_0
, y_0
, x_1
, y_1
, x_2
, y_2
;
83 return InvalidParameter
;
85 save_state
= SaveDC(graphics
->hdc
);
86 EndPath(graphics
->hdc
);
87 SelectObject(graphics
->hdc
, gdipen
);
88 SelectObject(graphics
->hdc
, gdibrush
);
90 x_0
= x
+ (width
/2.0);
91 y_0
= y
+ (height
/2.0);
93 deg2xy(startAngle
+sweepAngle
, x_0
, y_0
, &x_1
, &y_1
);
94 deg2xy(startAngle
, x_0
, y_0
, &x_2
, &y_2
);
96 Pie(graphics
->hdc
, roundr(x
), roundr(y
), roundr(x
+width
), roundr(y
+height
),
97 roundr(x_1
), roundr(y_1
), roundr(x_2
), roundr(y_2
));
99 RestoreDC(graphics
->hdc
, save_state
);
104 /* GdipDrawCurve helper function.
105 * Calculates Bezier points from cardinal spline points. */
106 static void calc_curve_bezier(CONST GpPointF
*pts
, REAL tension
, REAL
*x1
,
107 REAL
*y1
, REAL
*x2
, REAL
*y2
)
111 /* calculate tangent */
112 xdiff
= pts
[2].X
- pts
[0].X
;
113 ydiff
= pts
[2].Y
- pts
[0].Y
;
115 /* apply tangent to get control points */
116 *x1
= pts
[1].X
- tension
* xdiff
;
117 *y1
= pts
[1].Y
- tension
* ydiff
;
118 *x2
= pts
[1].X
+ tension
* xdiff
;
119 *y2
= pts
[1].Y
+ tension
* ydiff
;
122 /* GdipDrawCurve helper function.
123 * Calculates Bezier points from cardinal spline endpoints. */
124 static void calc_curve_bezier_endp(REAL xend
, REAL yend
, REAL xadj
, REAL yadj
,
125 REAL tension
, REAL
*x
, REAL
*y
)
127 /* tangent at endpoints is the line from the endpoint to the adjacent point */
128 *x
= roundr(tension
* (xadj
- xend
) + xend
);
129 *y
= roundr(tension
* (yadj
- yend
) + yend
);
132 /* Draws the linecap the specified color and size on the hdc. The linecap is in
133 * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. */
134 static void draw_cap(HDC hdc
, COLORREF color
, GpLineCap cap
, REAL size
,
135 const GpCustomLineCap
*custom
, REAL x1
, REAL y1
, REAL x2
, REAL y2
)
137 HGDIOBJ oldbrush
, oldpen
;
138 GpMatrix
*matrix
= NULL
;
141 PointF
*custptf
= NULL
;
142 POINT pt
[4], *custpt
= NULL
;
144 REAL theta
, dsmall
, dbig
, dx
, dy
;
149 theta
= atan2(y2
- y1
, x2
- x1
);
151 theta
= M_PI_2
* (y2
> y1
? 1.0 : -1.0);
156 brush
= CreateSolidBrush(color
);
157 lb
.lbStyle
= BS_SOLID
;
160 pen
= ExtCreatePen(PS_GEOMETRIC
| PS_SOLID
| PS_ENDCAP_FLAT
,
161 (cap
== LineCapCustom
? size
: 1), &lb
, 0, NULL
);
162 oldbrush
= SelectObject(hdc
, brush
);
163 oldpen
= SelectObject(hdc
, pen
);
169 case LineCapSquareAnchor
:
170 case LineCapDiamondAnchor
:
171 size
= size
* (cap
& LineCapNoAnchor
? ANCHOR_WIDTH
: 1.0) / 2.0;
172 if(cap
== LineCapDiamondAnchor
){
173 dsmall
= cos(theta
+ M_PI_2
) * size
;
174 dbig
= sin(theta
+ M_PI_2
) * size
;
177 dsmall
= cos(theta
+ M_PI_4
) * size
;
178 dbig
= sin(theta
+ M_PI_4
) * size
;
181 /* calculating the latter points from the earlier points makes them
182 * look a little better because of rounding issues */
183 pt
[0].x
= roundr(x2
- dsmall
);
184 pt
[1].x
= roundr(((REAL
)pt
[0].x
) + dbig
+ dsmall
);
186 pt
[0].y
= roundr(y2
- dbig
);
187 pt
[3].y
= roundr(((REAL
)pt
[0].y
) + dsmall
+ dbig
);
189 pt
[1].y
= roundr(y2
- dsmall
);
190 pt
[2].y
= roundr(dbig
+ dsmall
+ ((REAL
)pt
[1].y
));
192 pt
[3].x
= roundr(x2
- dbig
);
193 pt
[2].x
= roundr(((REAL
)pt
[3].x
) + dsmall
+ dbig
);
198 case LineCapArrowAnchor
:
199 size
= size
* 4.0 / sqrt(3.0);
201 dx
= cos(M_PI
/ 6.0 + theta
) * size
;
202 dy
= sin(M_PI
/ 6.0 + theta
) * size
;
204 pt
[0].x
= roundr(x2
- dx
);
205 pt
[0].y
= roundr(y2
- dy
);
207 dx
= cos(- M_PI
/ 6.0 + theta
) * size
;
208 dy
= sin(- M_PI
/ 6.0 + theta
) * size
;
210 pt
[1].x
= roundr(x2
- dx
);
211 pt
[1].y
= roundr(y2
- dy
);
213 pt
[2].x
= roundr(x2
);
214 pt
[2].y
= roundr(y2
);
219 case LineCapRoundAnchor
:
220 dx
= dy
= ANCHOR_WIDTH
* size
/ 2.0;
222 x2
= (REAL
) roundr(x2
- dx
);
223 y2
= (REAL
) roundr(y2
- dy
);
225 Ellipse(hdc
, (INT
) x2
, (INT
) y2
, roundr(x2
+ 2.0 * dx
),
226 roundr(y2
+ 2.0 * dy
));
228 case LineCapTriangle
:
230 dx
= cos(M_PI_2
+ theta
) * size
;
231 dy
= sin(M_PI_2
+ theta
) * size
;
233 /* Using roundr here can make the triangle float off the end of the
235 pt
[0].x
= ((x2
- x1
) >= 0 ? floorf(x2
- dx
) : ceilf(x2
- dx
));
236 pt
[0].y
= ((y2
- y1
) >= 0 ? floorf(y2
- dy
) : ceilf(y2
- dy
));
237 pt
[1].x
= roundr(pt
[0].x
+ 2.0 * dx
);
238 pt
[1].y
= roundr(pt
[0].y
+ 2.0 * dy
);
240 dx
= cos(theta
) * size
;
241 dy
= sin(theta
) * size
;
243 pt
[2].x
= roundr(x2
+ dx
);
244 pt
[2].y
= roundr(y2
+ dy
);
250 dx
= -cos(M_PI_2
+ theta
) * size
;
251 dy
= -sin(M_PI_2
+ theta
) * size
;
253 pt
[0].x
= ((x2
- x1
) >= 0 ? floorf(x2
- dx
) : ceilf(x2
- dx
));
254 pt
[0].y
= ((y2
- y1
) >= 0 ? floorf(y2
- dy
) : ceilf(y2
- dy
));
255 pt
[1].x
= roundr(pt
[0].x
+ 2.0 * dx
);
256 pt
[1].y
= roundr(pt
[0].y
+ 2.0 * dy
);
258 dx
= dy
= size
/ 2.0;
260 x2
= (REAL
) roundr(x2
- dx
);
261 y2
= (REAL
) roundr(y2
- dy
);
263 Pie(hdc
, (INT
) x2
, (INT
) y2
, roundr(x2
+ 2.0 * dx
),
264 roundr(y2
+ 2.0 * dy
), pt
[0].x
, pt
[0].y
, pt
[1].x
, pt
[1].y
);
271 FIXME("fill-path custom line caps not implemented\n");
275 count
= custom
->pathdata
.Count
;
276 custptf
= GdipAlloc(count
* sizeof(PointF
));
277 custpt
= GdipAlloc(count
* sizeof(POINT
));
278 tp
= GdipAlloc(count
);
280 if(!custptf
|| !custpt
|| !tp
|| (GdipCreateMatrix(&matrix
) != Ok
))
283 memcpy(custptf
, custom
->pathdata
.Points
, count
* sizeof(PointF
));
285 GdipScaleMatrix(matrix
, size
, size
, MatrixOrderAppend
);
286 GdipRotateMatrix(matrix
, (180.0 / M_PI
) * (theta
- M_PI_2
),
288 GdipTranslateMatrix(matrix
, x2
, y2
, MatrixOrderAppend
);
289 GdipTransformMatrixPoints(matrix
, custptf
, count
);
291 for(i
= 0; i
< count
; i
++){
292 custpt
[i
].x
= roundr(custptf
[i
].X
);
293 custpt
[i
].y
= roundr(custptf
[i
].Y
);
294 tp
[i
] = convert_path_point_type(custom
->pathdata
.Types
[i
]);
297 PolyDraw(hdc
, custpt
, tp
, count
);
303 GdipDeleteMatrix(matrix
);
309 SelectObject(hdc
, oldbrush
);
310 SelectObject(hdc
, oldpen
);
315 /* Shortens the line by the given percent by changing x2, y2.
316 * If percent is > 1.0 then the line will change direction. */
317 static void shorten_line_percent(REAL x1
, REAL y1
, REAL
*x2
, REAL
*y2
, REAL percent
)
319 REAL dist
, theta
, dx
, dy
;
321 if((y1
== *y2
) && (x1
== *x2
))
324 dist
= sqrt((*x2
- x1
) * (*x2
- x1
) + (*y2
- y1
) * (*y2
- y1
)) * percent
;
325 theta
= (*x2
== x1
? M_PI_2
: atan((*y2
- y1
) / (*x2
- x1
)));
326 dx
= cos(theta
) * dist
;
327 dy
= sin(theta
) * dist
;
329 *x2
= *x2
+ fabs(dx
) * (*x2
> x1
? -1.0 : 1.0);
330 *y2
= *y2
+ fabs(dy
) * (*y2
> y1
? -1.0 : 1.0);
333 /* Shortens the line by the given amount by changing x2, y2.
334 * If the amount is greater than the distance, the line will become length 0. */
335 static void shorten_line_amt(REAL x1
, REAL y1
, REAL
*x2
, REAL
*y2
, REAL amt
)
337 REAL dx
, dy
, percent
;
341 if(dx
== 0 && dy
== 0)
344 percent
= amt
/ sqrt(dx
* dx
+ dy
* dy
);
351 shorten_line_percent(x1
, y1
, x2
, y2
, percent
);
354 /* Draws lines between the given points, and if caps is true then draws an endcap
355 * at the end of the last line. FIXME: Startcaps not implemented. */
356 static GpStatus
draw_polyline(HDC hdc
, GpPen
*pen
, GDIPCONST GpPointF
* pt
,
357 INT count
, BOOL caps
)
360 REAL x
= pt
[count
- 1].X
, y
= pt
[count
- 1].Y
;
362 GpStatus status
= GenericError
;
367 pti
= GdipAlloc(count
* sizeof(POINT
));
370 status
= OutOfMemory
;
375 if(pen
->endcap
== LineCapArrowAnchor
)
376 shorten_line_amt(pt
[count
-2].X
, pt
[count
-2].Y
, &x
, &y
, pen
->width
);
378 draw_cap(hdc
, pen
->color
, pen
->endcap
, pen
->width
, pen
->customend
,
379 pt
[count
-2].X
, pt
[count
-2].Y
, pt
[count
- 1].X
, pt
[count
- 1].Y
);
382 for(i
= 0; i
< count
- 1; i
++){
383 pti
[i
].x
= roundr(pt
[i
].X
);
384 pti
[i
].y
= roundr(pt
[i
].Y
);
387 pti
[i
].x
= roundr(x
);
388 pti
[i
].y
= roundr(y
);
390 Polyline(hdc
, pti
, count
);
398 /* Conducts a linear search to find the bezier points that will back off
399 * the endpoint of the curve by a distance of amt. Linear search works
400 * better than binary in this case because there are multiple solutions,
401 * and binary searches often find a bad one. I don't think this is what
402 * Windows does but short of rendering the bezier without GDI's help it's
403 * the best we can do. */
404 static void shorten_bezier_amt(GpPointF
* pt
, REAL amt
)
407 REAL percent
= 0.00, dx
, dy
, origx
= pt
[3].X
, origy
= pt
[3].Y
, diff
= -1.0;
410 memcpy(origpt
, pt
, sizeof(GpPointF
) * 4);
412 for(i
= 0; (i
< MAX_ITERS
) && (diff
< amt
); i
++){
413 /* reset bezier points to original values */
414 memcpy(pt
, origpt
, sizeof(GpPointF
) * 4);
415 /* Perform magic on bezier points. Order is important here.*/
416 shorten_line_percent(pt
[2].X
, pt
[2].Y
, &pt
[3].X
, &pt
[3].Y
, percent
);
417 shorten_line_percent(pt
[1].X
, pt
[1].Y
, &pt
[2].X
, &pt
[2].Y
, percent
);
418 shorten_line_percent(pt
[2].X
, pt
[2].Y
, &pt
[3].X
, &pt
[3].Y
, percent
);
419 shorten_line_percent(pt
[0].X
, pt
[0].Y
, &pt
[1].X
, &pt
[1].Y
, percent
);
420 shorten_line_percent(pt
[1].X
, pt
[1].Y
, &pt
[2].X
, &pt
[2].Y
, percent
);
421 shorten_line_percent(pt
[2].X
, pt
[2].Y
, &pt
[3].X
, &pt
[3].Y
, percent
);
423 dx
= pt
[3].X
- origx
;
424 dy
= pt
[3].Y
- origy
;
426 diff
= sqrt(dx
* dx
+ dy
* dy
);
427 percent
+= 0.0005 * amt
;
431 /* Draws bezier curves between given points, and if caps is true then draws an
432 * endcap at the end of the last line. FIXME: Startcaps not implemented. */
433 static GpStatus
draw_polybezier(HDC hdc
, GpPen
*pen
, GDIPCONST GpPointF
* pt
,
434 INT count
, BOOL caps
)
439 GpStatus status
= GenericError
;
444 pti
= GdipAlloc(count
* sizeof(POINT
));
445 ptf
= GdipAlloc(4 * sizeof(GpPointF
));
448 status
= OutOfMemory
;
452 memcpy(ptf
, &pt
[count
-4], 4 * sizeof(GpPointF
));
455 if(pen
->endcap
== LineCapArrowAnchor
)
456 shorten_bezier_amt(ptf
, pen
->width
);
458 /* the direction of the line cap is parallel to the direction at the
459 * end of the bezier (which, if it has been shortened, is not the same
460 * as the direction from pt[count-2] to pt[count-1]) */
461 draw_cap(hdc
, pen
->color
, pen
->endcap
, pen
->width
, pen
->customend
,
462 pt
[count
- 1].X
- (ptf
[3].X
- ptf
[2].X
),
463 pt
[count
- 1].Y
- (ptf
[3].Y
- ptf
[2].Y
),
464 pt
[count
- 1].X
, pt
[count
- 1].Y
);
467 for(i
= 0; i
< count
- 4; i
++){
468 pti
[i
].x
= roundr(pt
[i
].X
);
469 pti
[i
].y
= roundr(pt
[i
].Y
);
471 for(i
= 0; i
< 4; i
++){
472 pti
[i
+ count
- 4].x
= roundr(ptf
[i
].X
);
473 pti
[i
+ count
- 4].y
= roundr(ptf
[i
].Y
);
476 PolyBezier(hdc
, pti
, count
);
487 /* Draws a combination of bezier curves and lines between points. */
488 static GpStatus
draw_poly(HDC hdc
, GpPen
*pen
, GDIPCONST GpPointF
* pt
,
489 GDIPCONST BYTE
* types
, INT count
, BOOL caps
)
491 POINT
*pti
= GdipAlloc(count
* sizeof(POINT
));
492 BYTE
*tp
= GdipAlloc(count
);
493 GpPointF
*ptf
= NULL
;
494 REAL x
= pt
[count
- 1].X
, y
= pt
[count
- 1].Y
;
496 GpStatus status
= GenericError
;
503 status
= OutOfMemory
;
507 for(i
= 0; i
< count
; i
++){
508 if((types
[i
] & PathPointTypePathTypeMask
) == PathPointTypeBezier
){
509 if((i
+ 2 >= count
) || !(types
[i
+ 1] & PathPointTypeBezier
)
510 || !(types
[i
+ 1] & PathPointTypeBezier
)){
511 ERR("Bad bezier points\n");
518 for(i
= 0; i
< count
; i
++){
519 pti
[i
].x
= roundr(pt
[i
].X
);
520 pti
[i
].y
= roundr(pt
[i
].Y
);
523 /* If we are drawing caps, go through the points and adjust them accordingly,
524 * and draw the caps. */
526 switch(types
[count
- 1] & PathPointTypePathTypeMask
){
527 case PathPointTypeBezier
:
528 ptf
= GdipAlloc(4 * sizeof(GpPointF
));
530 status
= OutOfMemory
;
533 memcpy(ptf
, &pt
[count
- 4], 4 * sizeof(GpPointF
));
535 if(pen
->endcap
== LineCapArrowAnchor
)
536 shorten_bezier_amt(ptf
, pen
->width
);
538 draw_cap(hdc
, pen
->color
, pen
->endcap
, pen
->width
, pen
->customend
,
539 pt
[count
- 1].X
- (ptf
[3].X
- ptf
[2].X
),
540 pt
[count
- 1].Y
- (ptf
[3].Y
- ptf
[2].Y
),
541 pt
[count
- 1].X
, pt
[count
- 1].Y
);
543 for(i
= 0; i
< 4; i
++){
544 pti
[i
+ count
- 4].x
= roundr(ptf
[i
].X
);
545 pti
[i
+ count
- 4].y
= roundr(ptf
[i
].Y
);
550 case PathPointTypeLine
:
551 if(pen
->endcap
== LineCapArrowAnchor
)
552 shorten_line_amt(pt
[count
- 2].X
, pt
[count
- 2].Y
, &x
, &y
,
555 draw_cap(hdc
, pen
->color
, pen
->endcap
, pen
->width
, pen
->customend
,
556 pt
[count
- 2].X
, pt
[count
- 2].Y
, pt
[count
- 1].X
,
559 pti
[count
- 1].x
= roundr(x
);
560 pti
[count
- 1].y
= roundr(y
);
564 ERR("Bad path last point\n");
569 for(i
= 0; i
< count
; i
++){
570 tp
[i
] = convert_path_point_type(types
[i
]);
573 PolyDraw(hdc
, pti
, tp
, count
);
585 GpStatus WINGDIPAPI
GdipCreateFromHDC(HDC hdc
, GpGraphics
**graphics
)
591 return InvalidParameter
;
593 *graphics
= GdipAlloc(sizeof(GpGraphics
));
594 if(!*graphics
) return OutOfMemory
;
596 (*graphics
)->hdc
= hdc
;
597 (*graphics
)->hwnd
= NULL
;
598 (*graphics
)->smoothing
= SmoothingModeDefault
;
599 (*graphics
)->compqual
= CompositingQualityDefault
;
600 (*graphics
)->interpolation
= InterpolationModeDefault
;
601 (*graphics
)->pixeloffset
= PixelOffsetModeDefault
;
606 GpStatus WINGDIPAPI
GdipCreateFromHWND(HWND hwnd
, GpGraphics
**graphics
)
610 if((ret
= GdipCreateFromHDC(GetDC(hwnd
), graphics
)) != Ok
)
613 (*graphics
)->hwnd
= hwnd
;
618 GpStatus WINGDIPAPI
GdipDeleteGraphics(GpGraphics
*graphics
)
620 if(!graphics
) return InvalidParameter
;
622 ReleaseDC(graphics
->hwnd
, graphics
->hdc
);
624 HeapFree(GetProcessHeap(), 0, graphics
);
629 GpStatus WINGDIPAPI
GdipDrawArc(GpGraphics
*graphics
, GpPen
*pen
, REAL x
,
630 REAL y
, REAL width
, REAL height
, REAL startAngle
, REAL sweepAngle
)
632 INT save_state
, num_pts
;
633 GpPointF points
[MAX_ARC_PTS
];
636 if(!graphics
|| !pen
)
637 return InvalidParameter
;
639 num_pts
= arc2polybezier(points
, x
, y
, width
, height
, startAngle
, sweepAngle
);
641 save_state
= SaveDC(graphics
->hdc
);
642 EndPath(graphics
->hdc
);
643 SelectObject(graphics
->hdc
, pen
->gdipen
);
645 retval
= draw_polybezier(graphics
->hdc
, pen
, points
, num_pts
, TRUE
);
647 RestoreDC(graphics
->hdc
, save_state
);
652 GpStatus WINGDIPAPI
GdipDrawBezier(GpGraphics
*graphics
, GpPen
*pen
, REAL x1
,
653 REAL y1
, REAL x2
, REAL y2
, REAL x3
, REAL y3
, REAL x4
, REAL y4
)
659 if(!graphics
|| !pen
)
660 return InvalidParameter
;
671 save_state
= SaveDC(graphics
->hdc
);
672 EndPath(graphics
->hdc
);
673 SelectObject(graphics
->hdc
, pen
->gdipen
);
675 retval
= draw_polybezier(graphics
->hdc
, pen
, pt
, 4, TRUE
);
677 RestoreDC(graphics
->hdc
, save_state
);
682 /* Approximates cardinal spline with Bezier curves. */
683 GpStatus WINGDIPAPI
GdipDrawCurve2(GpGraphics
*graphics
, GpPen
*pen
,
684 GDIPCONST GpPointF
*points
, INT count
, REAL tension
)
686 /* PolyBezier expects count*3-2 points. */
687 INT i
, len_pt
= count
*3-2, save_state
;
692 if(!graphics
|| !pen
)
693 return InvalidParameter
;
695 pt
= GdipAlloc(len_pt
* sizeof(GpPointF
));
696 tension
= tension
* TENSION_CONST
;
698 calc_curve_bezier_endp(points
[0].X
, points
[0].Y
, points
[1].X
, points
[1].Y
,
701 pt
[0].X
= points
[0].X
;
702 pt
[0].Y
= points
[0].Y
;
706 for(i
= 0; i
< count
-2; i
++){
707 calc_curve_bezier(&(points
[i
]), tension
, &x1
, &y1
, &x2
, &y2
);
711 pt
[3*i
+3].X
= points
[i
+1].X
;
712 pt
[3*i
+3].Y
= points
[i
+1].Y
;
717 calc_curve_bezier_endp(points
[count
-1].X
, points
[count
-1].Y
,
718 points
[count
-2].X
, points
[count
-2].Y
, tension
, &x1
, &y1
);
722 pt
[len_pt
-1].X
= points
[count
-1].X
;
723 pt
[len_pt
-1].Y
= points
[count
-1].Y
;
725 save_state
= SaveDC(graphics
->hdc
);
726 EndPath(graphics
->hdc
);
727 SelectObject(graphics
->hdc
, pen
->gdipen
);
729 retval
= draw_polybezier(graphics
->hdc
, pen
, pt
, len_pt
, TRUE
);
732 RestoreDC(graphics
->hdc
, save_state
);
737 GpStatus WINGDIPAPI
GdipDrawLineI(GpGraphics
*graphics
, GpPen
*pen
, INT x1
,
738 INT y1
, INT x2
, INT y2
)
744 if(!pen
|| !graphics
)
745 return InvalidParameter
;
752 save_state
= SaveDC(graphics
->hdc
);
753 EndPath(graphics
->hdc
);
754 SelectObject(graphics
->hdc
, pen
->gdipen
);
756 retval
= draw_polyline(graphics
->hdc
, pen
, pt
, 2, TRUE
);
758 RestoreDC(graphics
->hdc
, save_state
);
763 GpStatus WINGDIPAPI
GdipDrawLines(GpGraphics
*graphics
, GpPen
*pen
, GDIPCONST
764 GpPointF
*points
, INT count
)
769 if(!pen
|| !graphics
|| (count
< 2))
770 return InvalidParameter
;
772 save_state
= SaveDC(graphics
->hdc
);
773 EndPath(graphics
->hdc
);
774 SelectObject(graphics
->hdc
, pen
->gdipen
);
776 retval
= draw_polyline(graphics
->hdc
, pen
, points
, count
, TRUE
);
778 RestoreDC(graphics
->hdc
, save_state
);
783 GpStatus WINGDIPAPI
GdipDrawPath(GpGraphics
*graphics
, GpPen
*pen
, GpPath
*path
)
788 if(!pen
|| !graphics
)
789 return InvalidParameter
;
791 save_state
= SaveDC(graphics
->hdc
);
792 EndPath(graphics
->hdc
);
793 SelectObject(graphics
->hdc
, pen
->gdipen
);
795 retval
= draw_poly(graphics
->hdc
, pen
, path
->pathdata
.Points
,
796 path
->pathdata
.Types
, path
->pathdata
.Count
, TRUE
);
798 RestoreDC(graphics
->hdc
, save_state
);
803 GpStatus WINGDIPAPI
GdipDrawPie(GpGraphics
*graphics
, GpPen
*pen
, REAL x
,
804 REAL y
, REAL width
, REAL height
, REAL startAngle
, REAL sweepAngle
)
807 return InvalidParameter
;
809 return draw_pie(graphics
, GetStockObject(NULL_BRUSH
), pen
->gdipen
, x
, y
,
810 width
, height
, startAngle
, sweepAngle
);
813 GpStatus WINGDIPAPI
GdipDrawRectangleI(GpGraphics
*graphics
, GpPen
*pen
, INT x
,
814 INT y
, INT width
, INT height
)
818 if(!pen
|| !graphics
)
819 return InvalidParameter
;
821 save_state
= SaveDC(graphics
->hdc
);
822 EndPath(graphics
->hdc
);
823 SelectObject(graphics
->hdc
, pen
->gdipen
);
824 SelectObject(graphics
->hdc
, GetStockObject(NULL_BRUSH
));
826 Rectangle(graphics
->hdc
, x
, y
, x
+ width
, y
+ height
);
828 RestoreDC(graphics
->hdc
, save_state
);
833 GpStatus WINGDIPAPI
GdipFillPath(GpGraphics
*graphics
, GpBrush
*brush
, GpPath
*path
)
838 if(!brush
|| !graphics
|| !path
)
839 return InvalidParameter
;
841 save_state
= SaveDC(graphics
->hdc
);
842 EndPath(graphics
->hdc
);
843 SelectObject(graphics
->hdc
, brush
->gdibrush
);
844 SetPolyFillMode(graphics
->hdc
, (path
->fill
== FillModeAlternate
? ALTERNATE
847 BeginPath(graphics
->hdc
);
848 retval
= draw_poly(graphics
->hdc
, NULL
, path
->pathdata
.Points
,
849 path
->pathdata
.Types
, path
->pathdata
.Count
, FALSE
);
854 EndPath(graphics
->hdc
);
855 FillPath(graphics
->hdc
);
860 RestoreDC(graphics
->hdc
, save_state
);
865 GpStatus WINGDIPAPI
GdipFillPie(GpGraphics
*graphics
, GpBrush
*brush
, REAL x
,
866 REAL y
, REAL width
, REAL height
, REAL startAngle
, REAL sweepAngle
)
869 return InvalidParameter
;
871 return draw_pie(graphics
, brush
->gdibrush
, GetStockObject(NULL_PEN
), x
, y
,
872 width
, height
, startAngle
, sweepAngle
);
875 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
876 GpStatus WINGDIPAPI
GdipGetCompositingQuality(GpGraphics
*graphics
,
877 CompositingQuality
*quality
)
879 if(!graphics
|| !quality
)
880 return InvalidParameter
;
882 *quality
= graphics
->compqual
;
887 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
888 GpStatus WINGDIPAPI
GdipGetInterpolationMode(GpGraphics
*graphics
,
889 InterpolationMode
*mode
)
891 if(!graphics
|| !mode
)
892 return InvalidParameter
;
894 *mode
= graphics
->interpolation
;
899 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
900 GpStatus WINGDIPAPI
GdipGetPixelOffsetMode(GpGraphics
*graphics
, PixelOffsetMode
903 if(!graphics
|| !mode
)
904 return InvalidParameter
;
906 *mode
= graphics
->pixeloffset
;
911 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
912 GpStatus WINGDIPAPI
GdipGetSmoothingMode(GpGraphics
*graphics
, SmoothingMode
*mode
)
914 if(!graphics
|| !mode
)
915 return InvalidParameter
;
917 *mode
= graphics
->smoothing
;
922 GpStatus WINGDIPAPI
GdipRestoreGraphics(GpGraphics
*graphics
, GraphicsState state
)
925 return InvalidParameter
;
927 FIXME("graphics state not implemented\n");
929 return NotImplemented
;
932 GpStatus WINGDIPAPI
GdipSaveGraphics(GpGraphics
*graphics
, GraphicsState
*state
)
934 if(!graphics
|| !state
)
935 return InvalidParameter
;
937 FIXME("graphics state not implemented\n");
939 return NotImplemented
;
942 GpStatus WINGDIPAPI
GdipSetCompositingQuality(GpGraphics
*graphics
,
943 CompositingQuality quality
)
946 return InvalidParameter
;
948 graphics
->compqual
= quality
;
953 GpStatus WINGDIPAPI
GdipSetInterpolationMode(GpGraphics
*graphics
,
954 InterpolationMode mode
)
957 return InvalidParameter
;
959 graphics
->interpolation
= mode
;
964 GpStatus WINGDIPAPI
GdipSetPixelOffsetMode(GpGraphics
*graphics
, PixelOffsetMode
968 return InvalidParameter
;
970 graphics
->pixeloffset
= mode
;
975 GpStatus WINGDIPAPI
GdipSetSmoothingMode(GpGraphics
*graphics
, SmoothingMode mode
)
978 return InvalidParameter
;
980 graphics
->smoothing
= mode
;