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. Probably
134 * should not be called on an hdc that has a path you care about. */
135 static void draw_cap(HDC hdc
, COLORREF color
, GpLineCap cap
, REAL size
,
136 const GpCustomLineCap
*custom
, REAL x1
, REAL y1
, REAL x2
, REAL y2
)
138 HGDIOBJ oldbrush
, oldpen
;
139 GpMatrix
*matrix
= NULL
;
142 PointF
*custptf
= NULL
;
143 POINT pt
[4], *custpt
= NULL
;
145 REAL theta
, dsmall
, dbig
, dx
, dy
;
150 theta
= atan2(y2
- y1
, x2
- x1
);
152 theta
= M_PI_2
* (y2
> y1
? 1.0 : -1.0);
157 brush
= CreateSolidBrush(color
);
158 lb
.lbStyle
= BS_SOLID
;
161 pen
= ExtCreatePen(PS_GEOMETRIC
| PS_SOLID
| PS_ENDCAP_FLAT
,
162 ((cap
== LineCapCustom
) && custom
&& (custom
->fill
)) ? size
: 1,
164 oldbrush
= SelectObject(hdc
, brush
);
165 oldpen
= SelectObject(hdc
, pen
);
171 case LineCapSquareAnchor
:
172 case LineCapDiamondAnchor
:
173 size
= size
* (cap
& LineCapNoAnchor
? ANCHOR_WIDTH
: 1.0) / 2.0;
174 if(cap
== LineCapDiamondAnchor
){
175 dsmall
= cos(theta
+ M_PI_2
) * size
;
176 dbig
= sin(theta
+ M_PI_2
) * size
;
179 dsmall
= cos(theta
+ M_PI_4
) * size
;
180 dbig
= sin(theta
+ M_PI_4
) * size
;
183 /* calculating the latter points from the earlier points makes them
184 * look a little better because of rounding issues */
185 pt
[0].x
= roundr(x2
- dsmall
);
186 pt
[1].x
= roundr(((REAL
)pt
[0].x
) + dbig
+ dsmall
);
188 pt
[0].y
= roundr(y2
- dbig
);
189 pt
[3].y
= roundr(((REAL
)pt
[0].y
) + dsmall
+ dbig
);
191 pt
[1].y
= roundr(y2
- dsmall
);
192 pt
[2].y
= roundr(dbig
+ dsmall
+ ((REAL
)pt
[1].y
));
194 pt
[3].x
= roundr(x2
- dbig
);
195 pt
[2].x
= roundr(((REAL
)pt
[3].x
) + dsmall
+ dbig
);
200 case LineCapArrowAnchor
:
201 size
= size
* 4.0 / sqrt(3.0);
203 dx
= cos(M_PI
/ 6.0 + theta
) * size
;
204 dy
= sin(M_PI
/ 6.0 + theta
) * size
;
206 pt
[0].x
= roundr(x2
- dx
);
207 pt
[0].y
= roundr(y2
- dy
);
209 dx
= cos(- M_PI
/ 6.0 + theta
) * size
;
210 dy
= sin(- M_PI
/ 6.0 + theta
) * size
;
212 pt
[1].x
= roundr(x2
- dx
);
213 pt
[1].y
= roundr(y2
- dy
);
215 pt
[2].x
= roundr(x2
);
216 pt
[2].y
= roundr(y2
);
221 case LineCapRoundAnchor
:
222 dx
= dy
= ANCHOR_WIDTH
* size
/ 2.0;
224 x2
= (REAL
) roundr(x2
- dx
);
225 y2
= (REAL
) roundr(y2
- dy
);
227 Ellipse(hdc
, (INT
) x2
, (INT
) y2
, roundr(x2
+ 2.0 * dx
),
228 roundr(y2
+ 2.0 * dy
));
230 case LineCapTriangle
:
232 dx
= cos(M_PI_2
+ theta
) * size
;
233 dy
= sin(M_PI_2
+ theta
) * size
;
235 /* Using roundr here can make the triangle float off the end of the
237 pt
[0].x
= ((x2
- x1
) >= 0 ? floorf(x2
- dx
) : ceilf(x2
- dx
));
238 pt
[0].y
= ((y2
- y1
) >= 0 ? floorf(y2
- dy
) : ceilf(y2
- dy
));
239 pt
[1].x
= roundr(pt
[0].x
+ 2.0 * dx
);
240 pt
[1].y
= roundr(pt
[0].y
+ 2.0 * dy
);
242 dx
= cos(theta
) * size
;
243 dy
= sin(theta
) * size
;
245 pt
[2].x
= roundr(x2
+ dx
);
246 pt
[2].y
= roundr(y2
+ dy
);
252 dx
= -cos(M_PI_2
+ theta
) * size
;
253 dy
= -sin(M_PI_2
+ theta
) * size
;
255 pt
[0].x
= ((x2
- x1
) >= 0 ? floorf(x2
- dx
) : ceilf(x2
- dx
));
256 pt
[0].y
= ((y2
- y1
) >= 0 ? floorf(y2
- dy
) : ceilf(y2
- dy
));
257 pt
[1].x
= roundr(pt
[0].x
+ 2.0 * dx
);
258 pt
[1].y
= roundr(pt
[0].y
+ 2.0 * dy
);
260 dx
= dy
= size
/ 2.0;
262 x2
= (REAL
) roundr(x2
- dx
);
263 y2
= (REAL
) roundr(y2
- dy
);
265 Pie(hdc
, (INT
) x2
, (INT
) y2
, roundr(x2
+ 2.0 * dx
),
266 roundr(y2
+ 2.0 * dy
), pt
[0].x
, pt
[0].y
, pt
[1].x
, pt
[1].y
);
272 count
= custom
->pathdata
.Count
;
273 custptf
= GdipAlloc(count
* sizeof(PointF
));
274 custpt
= GdipAlloc(count
* sizeof(POINT
));
275 tp
= GdipAlloc(count
);
277 if(!custptf
|| !custpt
|| !tp
|| (GdipCreateMatrix(&matrix
) != Ok
))
280 memcpy(custptf
, custom
->pathdata
.Points
, count
* sizeof(PointF
));
282 GdipScaleMatrix(matrix
, size
, size
, MatrixOrderAppend
);
283 GdipRotateMatrix(matrix
, (180.0 / M_PI
) * (theta
- M_PI_2
),
285 GdipTranslateMatrix(matrix
, x2
, y2
, MatrixOrderAppend
);
286 GdipTransformMatrixPoints(matrix
, custptf
, count
);
288 for(i
= 0; i
< count
; i
++){
289 custpt
[i
].x
= roundr(custptf
[i
].X
);
290 custpt
[i
].y
= roundr(custptf
[i
].Y
);
291 tp
[i
] = convert_path_point_type(custom
->pathdata
.Types
[i
]);
296 PolyDraw(hdc
, custpt
, tp
, count
);
298 StrokeAndFillPath(hdc
);
301 PolyDraw(hdc
, custpt
, tp
, count
);
307 GdipDeleteMatrix(matrix
);
313 SelectObject(hdc
, oldbrush
);
314 SelectObject(hdc
, oldpen
);
319 /* Shortens the line by the given percent by changing x2, y2.
320 * If percent is > 1.0 then the line will change direction.
321 * If percent is negative it can lengthen the line. */
322 static void shorten_line_percent(REAL x1
, REAL y1
, REAL
*x2
, REAL
*y2
, REAL percent
)
324 REAL dist
, theta
, dx
, dy
;
326 if((y1
== *y2
) && (x1
== *x2
))
329 dist
= sqrt((*x2
- x1
) * (*x2
- x1
) + (*y2
- y1
) * (*y2
- y1
)) * -percent
;
330 theta
= (*x2
== x1
? M_PI_2
: atan2((*y2
- y1
), (*x2
- x1
)));
331 dx
= cos(theta
) * dist
;
332 dy
= sin(theta
) * dist
;
338 /* Shortens the line by the given amount by changing x2, y2.
339 * If the amount is greater than the distance, the line will become length 0.
340 * If the amount is negative, it can lengthen the line. */
341 static void shorten_line_amt(REAL x1
, REAL y1
, REAL
*x2
, REAL
*y2
, REAL amt
)
343 REAL dx
, dy
, percent
;
347 if(dx
== 0 && dy
== 0)
350 percent
= amt
/ sqrt(dx
* dx
+ dy
* dy
);
357 shorten_line_percent(x1
, y1
, x2
, y2
, percent
);
360 /* Draws lines between the given points, and if caps is true then draws an endcap
361 * at the end of the last line. FIXME: Startcaps not implemented. */
362 static GpStatus
draw_polyline(HDC hdc
, GpPen
*pen
, GDIPCONST GpPointF
* pt
,
363 INT count
, BOOL caps
)
366 GpPointF
*ptcopy
= NULL
;
368 GpStatus status
= GenericError
;
373 pti
= GdipAlloc(count
* sizeof(POINT
));
374 ptcopy
= GdipAlloc(count
* sizeof(GpPointF
));
377 status
= OutOfMemory
;
381 memcpy(ptcopy
, pt
, count
* sizeof(GpPointF
));
384 if(pen
->endcap
== LineCapArrowAnchor
)
385 shorten_line_amt(ptcopy
[count
-2].X
, ptcopy
[count
-2].Y
,
386 &ptcopy
[count
-1].X
, &ptcopy
[count
-1].Y
, pen
->width
);
387 else if((pen
->endcap
== LineCapCustom
) && pen
->customend
)
388 shorten_line_amt(ptcopy
[count
-2].X
, ptcopy
[count
-2].Y
,
389 &ptcopy
[count
-1].X
, &ptcopy
[count
-1].Y
,
390 pen
->customend
->inset
* pen
->width
);
392 if(pen
->startcap
== LineCapArrowAnchor
)
393 shorten_line_amt(ptcopy
[1].X
, ptcopy
[1].Y
,
394 &ptcopy
[0].X
, &ptcopy
[0].Y
, pen
->width
);
395 else if((pen
->startcap
== LineCapCustom
) && pen
->customstart
)
396 shorten_line_amt(ptcopy
[1].X
, ptcopy
[1].Y
,
397 &ptcopy
[0].X
, &ptcopy
[0].Y
,
398 pen
->customend
->inset
* pen
->width
);
400 draw_cap(hdc
, pen
->color
, pen
->endcap
, pen
->width
, pen
->customend
,
401 pt
[count
- 2].X
, pt
[count
- 2].Y
, pt
[count
- 1].X
, pt
[count
- 1].Y
);
402 draw_cap(hdc
, pen
->color
, pen
->startcap
, pen
->width
, pen
->customstart
,
403 pt
[1].X
, pt
[1].Y
, pt
[0].X
, pt
[0].Y
);
406 for(i
= 0; i
< count
; i
++){
407 pti
[i
].x
= roundr(ptcopy
[i
].X
);
408 pti
[i
].y
= roundr(ptcopy
[i
].Y
);
411 Polyline(hdc
, pti
, count
);
420 /* Conducts a linear search to find the bezier points that will back off
421 * the endpoint of the curve by a distance of amt. Linear search works
422 * better than binary in this case because there are multiple solutions,
423 * and binary searches often find a bad one. I don't think this is what
424 * Windows does but short of rendering the bezier without GDI's help it's
425 * the best we can do. If rev then work from the start of the passed points
426 * instead of the end. */
427 static void shorten_bezier_amt(GpPointF
* pt
, REAL amt
, BOOL rev
)
430 REAL percent
= 0.00, dx
, dy
, origx
, origy
, diff
= -1.0;
431 INT i
, first
= 0, second
= 1, third
= 2, fourth
= 3;
440 origx
= pt
[fourth
].X
;
441 origy
= pt
[fourth
].Y
;
442 memcpy(origpt
, pt
, sizeof(GpPointF
) * 4);
444 for(i
= 0; (i
< MAX_ITERS
) && (diff
< amt
); i
++){
445 /* reset bezier points to original values */
446 memcpy(pt
, origpt
, sizeof(GpPointF
) * 4);
447 /* Perform magic on bezier points. Order is important here.*/
448 shorten_line_percent(pt
[third
].X
, pt
[third
].Y
, &pt
[fourth
].X
, &pt
[fourth
].Y
, percent
);
449 shorten_line_percent(pt
[second
].X
, pt
[second
].Y
, &pt
[third
].X
, &pt
[third
].Y
, percent
);
450 shorten_line_percent(pt
[third
].X
, pt
[third
].Y
, &pt
[fourth
].X
, &pt
[fourth
].Y
, percent
);
451 shorten_line_percent(pt
[first
].X
, pt
[first
].Y
, &pt
[second
].X
, &pt
[second
].Y
, percent
);
452 shorten_line_percent(pt
[second
].X
, pt
[second
].Y
, &pt
[third
].X
, &pt
[third
].Y
, percent
);
453 shorten_line_percent(pt
[third
].X
, pt
[third
].Y
, &pt
[fourth
].X
, &pt
[fourth
].Y
, percent
);
455 dx
= pt
[fourth
].X
- origx
;
456 dy
= pt
[fourth
].Y
- origy
;
458 diff
= sqrt(dx
* dx
+ dy
* dy
);
459 percent
+= 0.0005 * amt
;
463 /* Draws bezier curves between given points, and if caps is true then draws an
464 * endcap at the end of the last line. FIXME: Startcaps not implemented. */
465 static GpStatus
draw_polybezier(HDC hdc
, GpPen
*pen
, GDIPCONST GpPointF
* pt
,
466 INT count
, BOOL caps
)
472 GpStatus status
= GenericError
;
477 pti
= GdipAlloc(count
* sizeof(POINT
));
478 ptcopy
= GdipAlloc(count
* sizeof(GpPointF
));
481 status
= OutOfMemory
;
485 memcpy(ptcopy
, pt
, count
* sizeof(GpPointF
));
488 if(pen
->endcap
== LineCapArrowAnchor
)
489 shorten_bezier_amt(&ptcopy
[count
-4], pen
->width
, FALSE
);
490 /* FIXME The following is seemingly correct only for baseinset < 0 or
491 * baseinset > ~3. With smaller baseinsets, windows actually
492 * lengthens the bezier line instead of shortening it. */
493 else if((pen
->endcap
== LineCapCustom
) && pen
->customend
){
496 shorten_line_amt(pt
[count
- 2].X
, pt
[count
- 2].Y
, &x
, &y
,
497 pen
->width
* pen
->customend
->inset
);
498 MoveToEx(hdc
, roundr(pt
[count
- 1].X
), roundr(pt
[count
- 1].Y
), &curpos
);
499 LineTo(hdc
, roundr(x
), roundr(y
));
500 MoveToEx(hdc
, curpos
.x
, curpos
.y
, NULL
);
503 if(pen
->startcap
== LineCapArrowAnchor
)
504 shorten_bezier_amt(ptcopy
, pen
->width
, TRUE
);
505 else if((pen
->startcap
== LineCapCustom
) && pen
->customstart
){
508 shorten_line_amt(ptcopy
[1].X
, ptcopy
[1].Y
, &x
, &y
,
509 pen
->width
* pen
->customend
->inset
);
510 MoveToEx(hdc
, roundr(pt
[0].X
), roundr(pt
[0].Y
), &curpos
);
511 LineTo(hdc
, roundr(x
), roundr(y
));
512 MoveToEx(hdc
, curpos
.x
, curpos
.y
, NULL
);
515 /* the direction of the line cap is parallel to the direction at the
516 * end of the bezier (which, if it has been shortened, is not the same
517 * as the direction from pt[count-2] to pt[count-1]) */
518 draw_cap(hdc
, pen
->color
, pen
->endcap
, pen
->width
, pen
->customend
,
519 pt
[count
- 1].X
- (ptcopy
[count
- 1].X
- ptcopy
[count
- 2].X
),
520 pt
[count
- 1].Y
- (ptcopy
[count
- 1].Y
- ptcopy
[count
- 2].Y
),
521 pt
[count
- 1].X
, pt
[count
- 1].Y
);
523 draw_cap(hdc
, pen
->color
, pen
->startcap
, pen
->width
, pen
->customstart
,
524 pt
[0].X
- (ptcopy
[0].X
- ptcopy
[1].X
),
525 pt
[0].Y
- (ptcopy
[0].Y
- ptcopy
[1].Y
), pt
[0].X
, pt
[0].Y
);
528 for(i
= 0; i
< count
; i
++){
529 pti
[i
].x
= roundr(ptcopy
[i
].X
);
530 pti
[i
].y
= roundr(ptcopy
[i
].Y
);
533 PolyBezier(hdc
, pti
, count
);
544 /* Draws a combination of bezier curves and lines between points. */
545 static GpStatus
draw_poly(HDC hdc
, GpPen
*pen
, GDIPCONST GpPointF
* pt
,
546 GDIPCONST BYTE
* types
, INT count
, BOOL caps
)
548 POINT
*pti
= GdipAlloc(count
* sizeof(POINT
)), curpos
;
549 BYTE
*tp
= GdipAlloc(count
);
550 GpPointF
*ptcopy
= GdipAlloc(count
* sizeof(GpPointF
));
551 REAL x
= pt
[count
- 1].X
, y
= pt
[count
- 1].Y
;
553 GpStatus status
= GenericError
;
559 if(!pti
|| !tp
|| !ptcopy
){
560 status
= OutOfMemory
;
564 for(i
= 1; i
< count
; i
++){
565 if((types
[i
] & PathPointTypePathTypeMask
) == PathPointTypeBezier
){
566 if((i
+ 2 >= count
) || !(types
[i
+ 1] & PathPointTypeBezier
)
567 || !(types
[i
+ 1] & PathPointTypeBezier
)){
568 ERR("Bad bezier points\n");
575 /* If we are drawing caps, go through the points and adjust them accordingly,
576 * and draw the caps. */
578 memcpy(ptcopy
, pt
, count
* sizeof(GpPointF
));
580 switch(types
[count
- 1] & PathPointTypePathTypeMask
){
581 case PathPointTypeBezier
:
582 if(pen
->endcap
== LineCapArrowAnchor
)
583 shorten_bezier_amt(&ptcopy
[count
- 4], pen
->width
, FALSE
);
584 else if((pen
->endcap
== LineCapCustom
) && pen
->customend
){
587 shorten_line_amt(pt
[count
- 2].X
, pt
[count
- 2].Y
, &x
, &y
,
588 pen
->width
* pen
->customend
->inset
);
589 MoveToEx(hdc
, roundr(pt
[count
- 1].X
), roundr(pt
[count
- 1].Y
), &curpos
);
590 LineTo(hdc
, roundr(x
), roundr(y
));
591 MoveToEx(hdc
, curpos
.x
, curpos
.y
, NULL
);
594 draw_cap(hdc
, pen
->color
, pen
->endcap
, pen
->width
, pen
->customend
,
595 pt
[count
- 1].X
- (ptcopy
[count
- 1].X
- ptcopy
[count
- 2].X
),
596 pt
[count
- 1].Y
- (ptcopy
[count
- 1].Y
- ptcopy
[count
- 2].Y
),
597 pt
[count
- 1].X
, pt
[count
- 1].Y
);
600 case PathPointTypeLine
:
601 if(pen
->endcap
== LineCapArrowAnchor
)
602 shorten_line_amt(ptcopy
[count
- 2].X
, ptcopy
[count
- 2].Y
,
603 &ptcopy
[count
- 1].X
, &ptcopy
[count
- 1].Y
,
605 else if((pen
->endcap
== LineCapCustom
) && pen
->customend
)
606 shorten_line_amt(ptcopy
[count
- 2].X
, ptcopy
[count
- 2].Y
,
607 &ptcopy
[count
- 1].X
, &ptcopy
[count
- 1].Y
,
608 pen
->customend
->inset
* pen
->width
);
610 draw_cap(hdc
, pen
->color
, pen
->endcap
, pen
->width
, pen
->customend
,
611 pt
[count
- 2].X
, pt
[count
- 2].Y
, pt
[count
- 1].X
,
616 ERR("Bad path last point\n");
620 /* Find start of points */
621 for(j
= 1; j
< count
&& ((types
[j
] & PathPointTypePathTypeMask
)
622 == PathPointTypeStart
); j
++);
624 switch(types
[j
] & PathPointTypePathTypeMask
){
625 case PathPointTypeBezier
:
626 if(pen
->startcap
== LineCapArrowAnchor
)
627 shorten_bezier_amt(&ptcopy
[j
- 1], pen
->width
, TRUE
);
628 else if((pen
->startcap
== LineCapCustom
) && pen
->customstart
){
631 shorten_line_amt(ptcopy
[j
].X
, ptcopy
[j
].Y
, &x
, &y
,
632 pen
->width
* pen
->customstart
->inset
);
633 MoveToEx(hdc
, roundr(pt
[j
- 1].X
), roundr(pt
[j
- 1].Y
), &curpos
);
634 LineTo(hdc
, roundr(x
), roundr(y
));
635 MoveToEx(hdc
, curpos
.x
, curpos
.y
, NULL
);
638 draw_cap(hdc
, pen
->color
, pen
->startcap
, pen
->width
, pen
->customstart
,
639 pt
[j
- 1].X
- (ptcopy
[j
- 1].X
- ptcopy
[j
].X
),
640 pt
[j
- 1].Y
- (ptcopy
[j
- 1].Y
- ptcopy
[j
].Y
),
641 pt
[j
- 1].X
, pt
[j
- 1].Y
);
644 case PathPointTypeLine
:
645 if(pen
->startcap
== LineCapArrowAnchor
)
646 shorten_line_amt(ptcopy
[j
].X
, ptcopy
[j
].Y
,
647 &ptcopy
[j
- 1].X
, &ptcopy
[j
- 1].Y
,
649 else if((pen
->startcap
== LineCapCustom
) && pen
->customstart
)
650 shorten_line_amt(ptcopy
[j
].X
, ptcopy
[j
].Y
,
651 &ptcopy
[j
- 1].X
, &ptcopy
[j
- 1].Y
,
652 pen
->customstart
->inset
* pen
->width
);
654 draw_cap(hdc
, pen
->color
, pen
->endcap
, pen
->width
, pen
->customstart
,
655 pt
[j
].X
, pt
[j
].Y
, pt
[j
- 1].X
,
660 ERR("Bad path points\n");
663 for(i
= 0; i
< count
; i
++){
664 pti
[i
].x
= roundr(ptcopy
[i
].X
);
665 pti
[i
].y
= roundr(ptcopy
[i
].Y
);
669 for(i
= 0; i
< count
; i
++){
670 pti
[i
].x
= roundr(pt
[i
].X
);
671 pti
[i
].y
= roundr(pt
[i
].Y
);
674 for(i
= 0; i
< count
; i
++){
675 tp
[i
] = convert_path_point_type(types
[i
]);
678 PolyDraw(hdc
, pti
, tp
, count
);
690 GpStatus WINGDIPAPI
GdipCreateFromHDC(HDC hdc
, GpGraphics
**graphics
)
696 return InvalidParameter
;
698 *graphics
= GdipAlloc(sizeof(GpGraphics
));
699 if(!*graphics
) return OutOfMemory
;
701 (*graphics
)->hdc
= hdc
;
702 (*graphics
)->hwnd
= NULL
;
703 (*graphics
)->smoothing
= SmoothingModeDefault
;
704 (*graphics
)->compqual
= CompositingQualityDefault
;
705 (*graphics
)->interpolation
= InterpolationModeDefault
;
706 (*graphics
)->pixeloffset
= PixelOffsetModeDefault
;
711 GpStatus WINGDIPAPI
GdipCreateFromHWND(HWND hwnd
, GpGraphics
**graphics
)
715 if((ret
= GdipCreateFromHDC(GetDC(hwnd
), graphics
)) != Ok
)
718 (*graphics
)->hwnd
= hwnd
;
723 GpStatus WINGDIPAPI
GdipDeleteGraphics(GpGraphics
*graphics
)
725 if(!graphics
) return InvalidParameter
;
727 ReleaseDC(graphics
->hwnd
, graphics
->hdc
);
729 HeapFree(GetProcessHeap(), 0, graphics
);
734 GpStatus WINGDIPAPI
GdipDrawArc(GpGraphics
*graphics
, GpPen
*pen
, REAL x
,
735 REAL y
, REAL width
, REAL height
, REAL startAngle
, REAL sweepAngle
)
737 INT save_state
, num_pts
;
738 GpPointF points
[MAX_ARC_PTS
];
741 if(!graphics
|| !pen
)
742 return InvalidParameter
;
744 num_pts
= arc2polybezier(points
, x
, y
, width
, height
, startAngle
, sweepAngle
);
746 save_state
= SaveDC(graphics
->hdc
);
747 EndPath(graphics
->hdc
);
748 SelectObject(graphics
->hdc
, pen
->gdipen
);
750 retval
= draw_polybezier(graphics
->hdc
, pen
, points
, num_pts
, TRUE
);
752 RestoreDC(graphics
->hdc
, save_state
);
757 GpStatus WINGDIPAPI
GdipDrawBezier(GpGraphics
*graphics
, GpPen
*pen
, REAL x1
,
758 REAL y1
, REAL x2
, REAL y2
, REAL x3
, REAL y3
, REAL x4
, REAL y4
)
764 if(!graphics
|| !pen
)
765 return InvalidParameter
;
776 save_state
= SaveDC(graphics
->hdc
);
777 EndPath(graphics
->hdc
);
778 SelectObject(graphics
->hdc
, pen
->gdipen
);
780 retval
= draw_polybezier(graphics
->hdc
, pen
, pt
, 4, TRUE
);
782 RestoreDC(graphics
->hdc
, save_state
);
787 /* Approximates cardinal spline with Bezier curves. */
788 GpStatus WINGDIPAPI
GdipDrawCurve2(GpGraphics
*graphics
, GpPen
*pen
,
789 GDIPCONST GpPointF
*points
, INT count
, REAL tension
)
791 /* PolyBezier expects count*3-2 points. */
792 INT i
, len_pt
= count
*3-2, save_state
;
797 if(!graphics
|| !pen
)
798 return InvalidParameter
;
800 pt
= GdipAlloc(len_pt
* sizeof(GpPointF
));
801 tension
= tension
* TENSION_CONST
;
803 calc_curve_bezier_endp(points
[0].X
, points
[0].Y
, points
[1].X
, points
[1].Y
,
806 pt
[0].X
= points
[0].X
;
807 pt
[0].Y
= points
[0].Y
;
811 for(i
= 0; i
< count
-2; i
++){
812 calc_curve_bezier(&(points
[i
]), tension
, &x1
, &y1
, &x2
, &y2
);
816 pt
[3*i
+3].X
= points
[i
+1].X
;
817 pt
[3*i
+3].Y
= points
[i
+1].Y
;
822 calc_curve_bezier_endp(points
[count
-1].X
, points
[count
-1].Y
,
823 points
[count
-2].X
, points
[count
-2].Y
, tension
, &x1
, &y1
);
827 pt
[len_pt
-1].X
= points
[count
-1].X
;
828 pt
[len_pt
-1].Y
= points
[count
-1].Y
;
830 save_state
= SaveDC(graphics
->hdc
);
831 EndPath(graphics
->hdc
);
832 SelectObject(graphics
->hdc
, pen
->gdipen
);
834 retval
= draw_polybezier(graphics
->hdc
, pen
, pt
, len_pt
, TRUE
);
837 RestoreDC(graphics
->hdc
, save_state
);
842 GpStatus WINGDIPAPI
GdipDrawLineI(GpGraphics
*graphics
, GpPen
*pen
, INT x1
,
843 INT y1
, INT x2
, INT y2
)
849 if(!pen
|| !graphics
)
850 return InvalidParameter
;
857 save_state
= SaveDC(graphics
->hdc
);
858 EndPath(graphics
->hdc
);
859 SelectObject(graphics
->hdc
, pen
->gdipen
);
861 retval
= draw_polyline(graphics
->hdc
, pen
, pt
, 2, TRUE
);
863 RestoreDC(graphics
->hdc
, save_state
);
868 GpStatus WINGDIPAPI
GdipDrawLines(GpGraphics
*graphics
, GpPen
*pen
, GDIPCONST
869 GpPointF
*points
, INT count
)
874 if(!pen
|| !graphics
|| (count
< 2))
875 return InvalidParameter
;
877 save_state
= SaveDC(graphics
->hdc
);
878 EndPath(graphics
->hdc
);
879 SelectObject(graphics
->hdc
, pen
->gdipen
);
881 retval
= draw_polyline(graphics
->hdc
, pen
, points
, count
, TRUE
);
883 RestoreDC(graphics
->hdc
, save_state
);
888 GpStatus WINGDIPAPI
GdipDrawPath(GpGraphics
*graphics
, GpPen
*pen
, GpPath
*path
)
893 if(!pen
|| !graphics
)
894 return InvalidParameter
;
896 save_state
= SaveDC(graphics
->hdc
);
897 EndPath(graphics
->hdc
);
898 SelectObject(graphics
->hdc
, pen
->gdipen
);
900 retval
= draw_poly(graphics
->hdc
, pen
, path
->pathdata
.Points
,
901 path
->pathdata
.Types
, path
->pathdata
.Count
, TRUE
);
903 RestoreDC(graphics
->hdc
, save_state
);
908 GpStatus WINGDIPAPI
GdipDrawPie(GpGraphics
*graphics
, GpPen
*pen
, REAL x
,
909 REAL y
, REAL width
, REAL height
, REAL startAngle
, REAL sweepAngle
)
912 return InvalidParameter
;
914 return draw_pie(graphics
, GetStockObject(NULL_BRUSH
), pen
->gdipen
, x
, y
,
915 width
, height
, startAngle
, sweepAngle
);
918 GpStatus WINGDIPAPI
GdipDrawRectangleI(GpGraphics
*graphics
, GpPen
*pen
, INT x
,
919 INT y
, INT width
, INT height
)
923 if(!pen
|| !graphics
)
924 return InvalidParameter
;
926 save_state
= SaveDC(graphics
->hdc
);
927 EndPath(graphics
->hdc
);
928 SelectObject(graphics
->hdc
, pen
->gdipen
);
929 SelectObject(graphics
->hdc
, GetStockObject(NULL_BRUSH
));
931 Rectangle(graphics
->hdc
, x
, y
, x
+ width
, y
+ height
);
933 RestoreDC(graphics
->hdc
, save_state
);
938 GpStatus WINGDIPAPI
GdipFillPath(GpGraphics
*graphics
, GpBrush
*brush
, GpPath
*path
)
943 if(!brush
|| !graphics
|| !path
)
944 return InvalidParameter
;
946 save_state
= SaveDC(graphics
->hdc
);
947 EndPath(graphics
->hdc
);
948 SelectObject(graphics
->hdc
, brush
->gdibrush
);
949 SetPolyFillMode(graphics
->hdc
, (path
->fill
== FillModeAlternate
? ALTERNATE
952 BeginPath(graphics
->hdc
);
953 retval
= draw_poly(graphics
->hdc
, NULL
, path
->pathdata
.Points
,
954 path
->pathdata
.Types
, path
->pathdata
.Count
, FALSE
);
959 EndPath(graphics
->hdc
);
960 FillPath(graphics
->hdc
);
965 RestoreDC(graphics
->hdc
, save_state
);
970 GpStatus WINGDIPAPI
GdipFillPie(GpGraphics
*graphics
, GpBrush
*brush
, REAL x
,
971 REAL y
, REAL width
, REAL height
, REAL startAngle
, REAL sweepAngle
)
974 return InvalidParameter
;
976 return draw_pie(graphics
, brush
->gdibrush
, GetStockObject(NULL_PEN
), x
, y
,
977 width
, height
, startAngle
, sweepAngle
);
980 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
981 GpStatus WINGDIPAPI
GdipGetCompositingQuality(GpGraphics
*graphics
,
982 CompositingQuality
*quality
)
984 if(!graphics
|| !quality
)
985 return InvalidParameter
;
987 *quality
= graphics
->compqual
;
992 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
993 GpStatus WINGDIPAPI
GdipGetInterpolationMode(GpGraphics
*graphics
,
994 InterpolationMode
*mode
)
996 if(!graphics
|| !mode
)
997 return InvalidParameter
;
999 *mode
= graphics
->interpolation
;
1004 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
1005 GpStatus WINGDIPAPI
GdipGetPixelOffsetMode(GpGraphics
*graphics
, PixelOffsetMode
1008 if(!graphics
|| !mode
)
1009 return InvalidParameter
;
1011 *mode
= graphics
->pixeloffset
;
1016 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
1017 GpStatus WINGDIPAPI
GdipGetSmoothingMode(GpGraphics
*graphics
, SmoothingMode
*mode
)
1019 if(!graphics
|| !mode
)
1020 return InvalidParameter
;
1022 *mode
= graphics
->smoothing
;
1027 GpStatus WINGDIPAPI
GdipRestoreGraphics(GpGraphics
*graphics
, GraphicsState state
)
1030 return InvalidParameter
;
1032 FIXME("graphics state not implemented\n");
1034 return NotImplemented
;
1037 GpStatus WINGDIPAPI
GdipSaveGraphics(GpGraphics
*graphics
, GraphicsState
*state
)
1039 if(!graphics
|| !state
)
1040 return InvalidParameter
;
1042 FIXME("graphics state not implemented\n");
1044 return NotImplemented
;
1047 GpStatus WINGDIPAPI
GdipSetCompositingQuality(GpGraphics
*graphics
,
1048 CompositingQuality quality
)
1051 return InvalidParameter
;
1053 graphics
->compqual
= quality
;
1058 GpStatus WINGDIPAPI
GdipSetInterpolationMode(GpGraphics
*graphics
,
1059 InterpolationMode mode
)
1062 return InvalidParameter
;
1064 graphics
->interpolation
= mode
;
1069 GpStatus WINGDIPAPI
GdipSetPixelOffsetMode(GpGraphics
*graphics
, PixelOffsetMode
1073 return InvalidParameter
;
1075 graphics
->pixeloffset
= mode
;
1080 GpStatus WINGDIPAPI
GdipSetSmoothingMode(GpGraphics
*graphics
, SmoothingMode mode
)
1083 return InvalidParameter
;
1085 graphics
->smoothing
= mode
;