gdiplus: Added GdipRotateWorldTransform.
[wine] / dlls / gdiplus / graphics.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 #include <stdarg.h>
20 #include <math.h>
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "wingdi.h"
26
27 #define COBJMACROS
28 #include "objbase.h"
29 #include "ocidl.h"
30 #include "olectl.h"
31 #include "ole2.h"
32
33 #include "winreg.h"
34 #include "shlwapi.h"
35
36 #include "gdiplus.h"
37 #include "gdiplus_private.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
41
42 /* looks-right constants */
43 #define TENSION_CONST (0.3)
44 #define ANCHOR_WIDTH (2.0)
45 #define MAX_ITERS (50)
46
47 /* Converts angle (in degrees) to x/y coordinates */
48 static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y)
49 {
50     REAL radAngle, hypotenuse;
51
52     radAngle = deg2rad(angle);
53     hypotenuse = 50.0; /* arbitrary */
54
55     *x = x_0 + cos(radAngle) * hypotenuse;
56     *y = y_0 + sin(radAngle) * hypotenuse;
57 }
58
59 /* Converts from gdiplus path point type to gdi path point type. */
60 static BYTE convert_path_point_type(BYTE type)
61 {
62     BYTE ret;
63
64     switch(type & PathPointTypePathTypeMask){
65         case PathPointTypeBezier:
66             ret = PT_BEZIERTO;
67             break;
68         case PathPointTypeLine:
69             ret = PT_LINETO;
70             break;
71         case PathPointTypeStart:
72             ret = PT_MOVETO;
73             break;
74         default:
75             ERR("Bad point type\n");
76             return 0;
77     }
78
79     if(type & PathPointTypeCloseSubpath)
80         ret |= PT_CLOSEFIGURE;
81
82     return ret;
83 }
84
85 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
86 {
87     HPEN gdipen;
88     REAL width;
89     INT save_state = SaveDC(graphics->hdc), i, numdashes;
90     GpPointF pt[2];
91     DWORD dash_array[MAX_DASHLEN];
92
93     EndPath(graphics->hdc);
94
95     if(pen->unit == UnitPixel){
96         width = pen->width;
97     }
98     else{
99         /* Get an estimate for the amount the pen width is affected by the world
100          * transform. (This is similar to what some of the wine drivers do.) */
101         pt[0].X = 0.0;
102         pt[0].Y = 0.0;
103         pt[1].X = 1.0;
104         pt[1].Y = 1.0;
105         GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
106         width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
107                      (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
108
109         width *= pen->width * convert_unit(graphics->hdc,
110                               pen->unit == UnitWorld ? graphics->unit : pen->unit);
111     }
112
113     if(pen->dash == DashStyleCustom){
114         numdashes = min(pen->numdashes, MAX_DASHLEN);
115
116         TRACE("dashes are: ");
117         for(i = 0; i < numdashes; i++){
118             dash_array[i] = roundr(width * pen->dashes[i]);
119             TRACE("%d, ", dash_array[i]);
120         }
121         TRACE("\n and the pen style is %x\n", pen->style);
122
123         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb,
124                               numdashes, dash_array);
125     }
126     else
127         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb, 0, NULL);
128
129     SelectObject(graphics->hdc, gdipen);
130
131     return save_state;
132 }
133
134 static void restore_dc(GpGraphics *graphics, INT state)
135 {
136     DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
137     RestoreDC(graphics->hdc, state);
138 }
139
140 /* This helper applies all the changes that the points listed in ptf need in
141  * order to be drawn on the device context.  In the end, this should include at
142  * least:
143  *  -scaling by page unit
144  *  -applying world transformation
145  *  -converting from float to int
146  * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
147  * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
148  * gdi to draw, and these functions would irreparably mess with line widths.
149  */
150 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
151     GpPointF *ptf, INT count)
152 {
153     REAL unitscale;
154     GpMatrix *matrix;
155     int i;
156
157     unitscale = convert_unit(graphics->hdc, graphics->unit);
158
159     /* apply page scale */
160     if(graphics->unit != UnitDisplay)
161         unitscale *= graphics->scale;
162
163     GdipCloneMatrix(graphics->worldtrans, &matrix);
164     GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
165     GdipTransformMatrixPoints(matrix, ptf, count);
166     GdipDeleteMatrix(matrix);
167
168     for(i = 0; i < count; i++){
169         pti[i].x = roundr(ptf[i].X);
170         pti[i].y = roundr(ptf[i].Y);
171     }
172 }
173
174 /* GdipDrawPie/GdipFillPie helper function */
175 static void draw_pie(GpGraphics *graphics, REAL x, REAL y, REAL width,
176     REAL height, REAL startAngle, REAL sweepAngle)
177 {
178     GpPointF ptf[4];
179     POINT pti[4];
180
181     ptf[0].X = x;
182     ptf[0].Y = y;
183     ptf[1].X = x + width;
184     ptf[1].Y = y + height;
185
186     deg2xy(startAngle+sweepAngle, x + width / 2.0, y + width / 2.0, &ptf[2].X, &ptf[2].Y);
187     deg2xy(startAngle, x + width / 2.0, y + width / 2.0, &ptf[3].X, &ptf[3].Y);
188
189     transform_and_round_points(graphics, pti, ptf, 4);
190
191     Pie(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y, pti[2].x,
192         pti[2].y, pti[3].x, pti[3].y);
193 }
194
195 /* GdipDrawCurve helper function.
196  * Calculates Bezier points from cardinal spline points. */
197 static void calc_curve_bezier(CONST GpPointF *pts, REAL tension, REAL *x1,
198     REAL *y1, REAL *x2, REAL *y2)
199 {
200     REAL xdiff, ydiff;
201
202     /* calculate tangent */
203     xdiff = pts[2].X - pts[0].X;
204     ydiff = pts[2].Y - pts[0].Y;
205
206     /* apply tangent to get control points */
207     *x1 = pts[1].X - tension * xdiff;
208     *y1 = pts[1].Y - tension * ydiff;
209     *x2 = pts[1].X + tension * xdiff;
210     *y2 = pts[1].Y + tension * ydiff;
211 }
212
213 /* GdipDrawCurve helper function.
214  * Calculates Bezier points from cardinal spline endpoints. */
215 static void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj,
216     REAL tension, REAL *x, REAL *y)
217 {
218     /* tangent at endpoints is the line from the endpoint to the adjacent point */
219     *x = roundr(tension * (xadj - xend) + xend);
220     *y = roundr(tension * (yadj - yend) + yend);
221 }
222
223 /* Draws the linecap the specified color and size on the hdc.  The linecap is in
224  * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
225  * should not be called on an hdc that has a path you care about. */
226 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
227     const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
228 {
229     HGDIOBJ oldbrush = NULL, oldpen = NULL;
230     GpMatrix *matrix = NULL;
231     HBRUSH brush = NULL;
232     HPEN pen = NULL;
233     PointF ptf[4], *custptf = NULL;
234     POINT pt[4], *custpt = NULL;
235     BYTE *tp = NULL;
236     REAL theta, dsmall, dbig, dx, dy = 0.0;
237     INT i, count;
238     LOGBRUSH lb;
239     BOOL customstroke;
240
241     if((x1 == x2) && (y1 == y2))
242         return;
243
244     theta = gdiplus_atan2(y2 - y1, x2 - x1);
245
246     customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
247     if(!customstroke){
248         brush = CreateSolidBrush(color);
249         lb.lbStyle = BS_SOLID;
250         lb.lbColor = color;
251         lb.lbHatch = 0;
252         pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
253                            PS_JOIN_MITER, 1, &lb, 0,
254                            NULL);
255         oldbrush = SelectObject(graphics->hdc, brush);
256         oldpen = SelectObject(graphics->hdc, pen);
257     }
258
259     switch(cap){
260         case LineCapFlat:
261             break;
262         case LineCapSquare:
263         case LineCapSquareAnchor:
264         case LineCapDiamondAnchor:
265             size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
266             if(cap == LineCapDiamondAnchor){
267                 dsmall = cos(theta + M_PI_2) * size;
268                 dbig = sin(theta + M_PI_2) * size;
269             }
270             else{
271                 dsmall = cos(theta + M_PI_4) * size;
272                 dbig = sin(theta + M_PI_4) * size;
273             }
274
275             ptf[0].X = x2 - dsmall;
276             ptf[1].X = x2 + dbig;
277
278             ptf[0].Y = y2 - dbig;
279             ptf[3].Y = y2 + dsmall;
280
281             ptf[1].Y = y2 - dsmall;
282             ptf[2].Y = y2 + dbig;
283
284             ptf[3].X = x2 - dbig;
285             ptf[2].X = x2 + dsmall;
286
287             transform_and_round_points(graphics, pt, ptf, 4);
288             Polygon(graphics->hdc, pt, 4);
289
290             break;
291         case LineCapArrowAnchor:
292             size = size * 4.0 / sqrt(3.0);
293
294             dx = cos(M_PI / 6.0 + theta) * size;
295             dy = sin(M_PI / 6.0 + theta) * size;
296
297             ptf[0].X = x2 - dx;
298             ptf[0].Y = y2 - dy;
299
300             dx = cos(- M_PI / 6.0 + theta) * size;
301             dy = sin(- M_PI / 6.0 + theta) * size;
302
303             ptf[1].X = x2 - dx;
304             ptf[1].Y = y2 - dy;
305
306             ptf[2].X = x2;
307             ptf[2].Y = y2;
308
309             transform_and_round_points(graphics, pt, ptf, 3);
310             Polygon(graphics->hdc, pt, 3);
311
312             break;
313         case LineCapRoundAnchor:
314             dx = dy = ANCHOR_WIDTH * size / 2.0;
315
316             ptf[0].X = x2 - dx;
317             ptf[0].Y = y2 - dy;
318             ptf[1].X = x2 + dx;
319             ptf[1].Y = y2 + dy;
320
321             transform_and_round_points(graphics, pt, ptf, 2);
322             Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
323
324             break;
325         case LineCapTriangle:
326             size = size / 2.0;
327             dx = cos(M_PI_2 + theta) * size;
328             dy = sin(M_PI_2 + theta) * size;
329
330             ptf[0].X = x2 - dx;
331             ptf[0].Y = y2 - dy;
332             ptf[1].X = x2 + dx;
333             ptf[1].Y = y2 + dy;
334
335             dx = cos(theta) * size;
336             dy = sin(theta) * size;
337
338             ptf[2].X = x2 + dx;
339             ptf[2].Y = y2 + dy;
340
341             transform_and_round_points(graphics, pt, ptf, 3);
342             Polygon(graphics->hdc, pt, 3);
343
344             break;
345         case LineCapRound:
346             dx = dy = size / 2.0;
347
348             ptf[0].X = x2 - dx;
349             ptf[0].Y = y2 - dy;
350             ptf[1].X = x2 + dx;
351             ptf[1].Y = y2 + dy;
352
353             dx = -cos(M_PI_2 + theta) * size;
354             dy = -sin(M_PI_2 + theta) * size;
355
356             ptf[2].X = x2 - dx;
357             ptf[2].Y = y2 - dy;
358             ptf[3].X = x2 + dx;
359             ptf[3].Y = y2 + dy;
360
361             transform_and_round_points(graphics, pt, ptf, 4);
362             Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
363                 pt[2].y, pt[3].x, pt[3].y);
364
365             break;
366         case LineCapCustom:
367             if(!custom)
368                 break;
369
370             count = custom->pathdata.Count;
371             custptf = GdipAlloc(count * sizeof(PointF));
372             custpt = GdipAlloc(count * sizeof(POINT));
373             tp = GdipAlloc(count);
374
375             if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok))
376                 goto custend;
377
378             memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
379
380             GdipScaleMatrix(matrix, size, size, MatrixOrderAppend);
381             GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2),
382                              MatrixOrderAppend);
383             GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend);
384             GdipTransformMatrixPoints(matrix, custptf, count);
385
386             transform_and_round_points(graphics, custpt, custptf, count);
387
388             for(i = 0; i < count; i++)
389                 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
390
391             if(custom->fill){
392                 BeginPath(graphics->hdc);
393                 PolyDraw(graphics->hdc, custpt, tp, count);
394                 EndPath(graphics->hdc);
395                 StrokeAndFillPath(graphics->hdc);
396             }
397             else
398                 PolyDraw(graphics->hdc, custpt, tp, count);
399
400 custend:
401             GdipFree(custptf);
402             GdipFree(custpt);
403             GdipFree(tp);
404             GdipDeleteMatrix(matrix);
405             break;
406         default:
407             break;
408     }
409
410     if(!customstroke){
411         SelectObject(graphics->hdc, oldbrush);
412         SelectObject(graphics->hdc, oldpen);
413         DeleteObject(brush);
414         DeleteObject(pen);
415     }
416 }
417
418 /* Shortens the line by the given percent by changing x2, y2.
419  * If percent is > 1.0 then the line will change direction.
420  * If percent is negative it can lengthen the line. */
421 static void shorten_line_percent(REAL x1, REAL  y1, REAL *x2, REAL *y2, REAL percent)
422 {
423     REAL dist, theta, dx, dy;
424
425     if((y1 == *y2) && (x1 == *x2))
426         return;
427
428     dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
429     theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
430     dx = cos(theta) * dist;
431     dy = sin(theta) * dist;
432
433     *x2 = *x2 + dx;
434     *y2 = *y2 + dy;
435 }
436
437 /* Shortens the line by the given amount by changing x2, y2.
438  * If the amount is greater than the distance, the line will become length 0.
439  * If the amount is negative, it can lengthen the line. */
440 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
441 {
442     REAL dx, dy, percent;
443
444     dx = *x2 - x1;
445     dy = *y2 - y1;
446     if(dx == 0 && dy == 0)
447         return;
448
449     percent = amt / sqrt(dx * dx + dy * dy);
450     if(percent >= 1.0){
451         *x2 = x1;
452         *y2 = y1;
453         return;
454     }
455
456     shorten_line_percent(x1, y1, x2, y2, percent);
457 }
458
459 /* Draws lines between the given points, and if caps is true then draws an endcap
460  * at the end of the last line. */
461 static GpStatus draw_polyline(GpGraphics *graphics, GpPen *pen,
462     GDIPCONST GpPointF * pt, INT count, BOOL caps)
463 {
464     POINT *pti = NULL;
465     GpPointF *ptcopy = NULL;
466     GpStatus status = GenericError;
467
468     if(!count)
469         return Ok;
470
471     pti = GdipAlloc(count * sizeof(POINT));
472     ptcopy = GdipAlloc(count * sizeof(GpPointF));
473
474     if(!pti || !ptcopy){
475         status = OutOfMemory;
476         goto end;
477     }
478
479     memcpy(ptcopy, pt, count * sizeof(GpPointF));
480
481     if(caps){
482         if(pen->endcap == LineCapArrowAnchor)
483             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
484                              &ptcopy[count-1].X, &ptcopy[count-1].Y, pen->width);
485         else if((pen->endcap == LineCapCustom) && pen->customend)
486             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
487                              &ptcopy[count-1].X, &ptcopy[count-1].Y,
488                              pen->customend->inset * pen->width);
489
490         if(pen->startcap == LineCapArrowAnchor)
491             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
492                              &ptcopy[0].X, &ptcopy[0].Y, pen->width);
493         else if((pen->startcap == LineCapCustom) && pen->customstart)
494             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
495                              &ptcopy[0].X, &ptcopy[0].Y,
496                              pen->customstart->inset * pen->width);
497
498         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
499                  pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X, pt[count - 1].Y);
500         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
501                          pt[1].X, pt[1].Y, pt[0].X, pt[0].Y);\
502     }
503
504     transform_and_round_points(graphics, pti, ptcopy, count);
505
506     Polyline(graphics->hdc, pti, count);
507
508 end:
509     GdipFree(pti);
510     GdipFree(ptcopy);
511
512     return status;
513 }
514
515 /* Conducts a linear search to find the bezier points that will back off
516  * the endpoint of the curve by a distance of amt. Linear search works
517  * better than binary in this case because there are multiple solutions,
518  * and binary searches often find a bad one. I don't think this is what
519  * Windows does but short of rendering the bezier without GDI's help it's
520  * the best we can do. If rev then work from the start of the passed points
521  * instead of the end. */
522 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
523 {
524     GpPointF origpt[4];
525     REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
526     INT i, first = 0, second = 1, third = 2, fourth = 3;
527
528     if(rev){
529         first = 3;
530         second = 2;
531         third = 1;
532         fourth = 0;
533     }
534
535     origx = pt[fourth].X;
536     origy = pt[fourth].Y;
537     memcpy(origpt, pt, sizeof(GpPointF) * 4);
538
539     for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
540         /* reset bezier points to original values */
541         memcpy(pt, origpt, sizeof(GpPointF) * 4);
542         /* Perform magic on bezier points. Order is important here.*/
543         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
544         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
545         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
546         shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
547         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
548         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
549
550         dx = pt[fourth].X - origx;
551         dy = pt[fourth].Y - origy;
552
553         diff = sqrt(dx * dx + dy * dy);
554         percent += 0.0005 * amt;
555     }
556 }
557
558 /* Draws bezier curves between given points, and if caps is true then draws an
559  * endcap at the end of the last line. */
560 static GpStatus draw_polybezier(GpGraphics *graphics, GpPen *pen,
561     GDIPCONST GpPointF * pt, INT count, BOOL caps)
562 {
563     POINT *pti;
564     GpPointF *ptcopy;
565     GpStatus status = GenericError;
566
567     if(!count)
568         return Ok;
569
570     pti = GdipAlloc(count * sizeof(POINT));
571     ptcopy = GdipAlloc(count * sizeof(GpPointF));
572
573     if(!pti || !ptcopy){
574         status = OutOfMemory;
575         goto end;
576     }
577
578     memcpy(ptcopy, pt, count * sizeof(GpPointF));
579
580     if(caps){
581         if(pen->endcap == LineCapArrowAnchor)
582             shorten_bezier_amt(&ptcopy[count-4], pen->width, FALSE);
583         else if((pen->endcap == LineCapCustom) && pen->customend)
584             shorten_bezier_amt(&ptcopy[count-4], pen->width * pen->customend->inset,
585                                FALSE);
586
587         if(pen->startcap == LineCapArrowAnchor)
588             shorten_bezier_amt(ptcopy, pen->width, TRUE);
589         else if((pen->startcap == LineCapCustom) && pen->customstart)
590             shorten_bezier_amt(ptcopy, pen->width * pen->customstart->inset, TRUE);
591
592         /* the direction of the line cap is parallel to the direction at the
593          * end of the bezier (which, if it has been shortened, is not the same
594          * as the direction from pt[count-2] to pt[count-1]) */
595         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
596             pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
597             pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
598             pt[count - 1].X, pt[count - 1].Y);
599
600         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
601             pt[0].X - (ptcopy[0].X - ptcopy[1].X),
602             pt[0].Y - (ptcopy[0].Y - ptcopy[1].Y), pt[0].X, pt[0].Y);
603     }
604
605     transform_and_round_points(graphics, pti, ptcopy, count);
606
607     PolyBezier(graphics->hdc, pti, count);
608
609     status = Ok;
610
611 end:
612     GdipFree(pti);
613     GdipFree(ptcopy);
614
615     return status;
616 }
617
618 /* Draws a combination of bezier curves and lines between points. */
619 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
620     GDIPCONST BYTE * types, INT count, BOOL caps)
621 {
622     POINT *pti = GdipAlloc(count * sizeof(POINT));
623     BYTE *tp = GdipAlloc(count);
624     GpPointF *ptcopy = GdipAlloc(count * sizeof(GpPointF));
625     INT i, j;
626     GpStatus status = GenericError;
627
628     if(!count){
629         status = Ok;
630         goto end;
631     }
632     if(!pti || !tp || !ptcopy){
633         status = OutOfMemory;
634         goto end;
635     }
636
637     for(i = 1; i < count; i++){
638         if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
639             if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
640                 || !(types[i + 1] & PathPointTypeBezier)){
641                 ERR("Bad bezier points\n");
642                 goto end;
643             }
644             i += 2;
645         }
646     }
647
648     memcpy(ptcopy, pt, count * sizeof(GpPointF));
649
650     /* If we are drawing caps, go through the points and adjust them accordingly,
651      * and draw the caps. */
652     if(caps){
653         switch(types[count - 1] & PathPointTypePathTypeMask){
654             case PathPointTypeBezier:
655                 if(pen->endcap == LineCapArrowAnchor)
656                     shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
657                 else if((pen->endcap == LineCapCustom) && pen->customend)
658                     shorten_bezier_amt(&ptcopy[count - 4],
659                                        pen->width * pen->customend->inset, FALSE);
660
661                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
662                     pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
663                     pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
664                     pt[count - 1].X, pt[count - 1].Y);
665
666                 break;
667             case PathPointTypeLine:
668                 if(pen->endcap == LineCapArrowAnchor)
669                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
670                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
671                                      pen->width);
672                 else if((pen->endcap == LineCapCustom) && pen->customend)
673                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
674                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
675                                      pen->customend->inset * pen->width);
676
677                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
678                          pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
679                          pt[count - 1].Y);
680
681                 break;
682             default:
683                 ERR("Bad path last point\n");
684                 goto end;
685         }
686
687         /* Find start of points */
688         for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
689             == PathPointTypeStart); j++);
690
691         switch(types[j] & PathPointTypePathTypeMask){
692             case PathPointTypeBezier:
693                 if(pen->startcap == LineCapArrowAnchor)
694                     shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
695                 else if((pen->startcap == LineCapCustom) && pen->customstart)
696                     shorten_bezier_amt(&ptcopy[j - 1],
697                                        pen->width * pen->customstart->inset, TRUE);
698
699                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
700                     pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
701                     pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
702                     pt[j - 1].X, pt[j - 1].Y);
703
704                 break;
705             case PathPointTypeLine:
706                 if(pen->startcap == LineCapArrowAnchor)
707                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
708                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
709                                      pen->width);
710                 else if((pen->startcap == LineCapCustom) && pen->customstart)
711                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
712                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
713                                      pen->customstart->inset * pen->width);
714
715                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
716                          pt[j].X, pt[j].Y, pt[j - 1].X,
717                          pt[j - 1].Y);
718
719                 break;
720             default:
721                 ERR("Bad path points\n");
722                 goto end;
723         }
724     }
725
726     transform_and_round_points(graphics, pti, ptcopy, count);
727
728     for(i = 0; i < count; i++){
729         tp[i] = convert_path_point_type(types[i]);
730     }
731
732     PolyDraw(graphics->hdc, pti, tp, count);
733
734     status = Ok;
735
736 end:
737     GdipFree(pti);
738     GdipFree(ptcopy);
739     GdipFree(tp);
740
741     return status;
742 }
743
744 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
745 {
746     GpStatus retval;
747
748     if(hdc == NULL)
749         return OutOfMemory;
750
751     if(graphics == NULL)
752         return InvalidParameter;
753
754     *graphics = GdipAlloc(sizeof(GpGraphics));
755     if(!*graphics)  return OutOfMemory;
756
757     if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
758         GdipFree(*graphics);
759         return retval;
760     }
761
762     (*graphics)->hdc = hdc;
763     (*graphics)->hwnd = NULL;
764     (*graphics)->smoothing = SmoothingModeDefault;
765     (*graphics)->compqual = CompositingQualityDefault;
766     (*graphics)->interpolation = InterpolationModeDefault;
767     (*graphics)->pixeloffset = PixelOffsetModeDefault;
768     (*graphics)->compmode = CompositingModeSourceOver;
769     (*graphics)->unit = UnitDisplay;
770     (*graphics)->scale = 1.0;
771
772     return Ok;
773 }
774
775 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
776 {
777     GpStatus ret;
778
779     if((ret = GdipCreateFromHDC(GetDC(hwnd), graphics)) != Ok)
780         return ret;
781
782     (*graphics)->hwnd = hwnd;
783
784     return Ok;
785 }
786
787 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
788     GpMetafile **metafile)
789 {
790     static int calls;
791
792     if(!hemf || !metafile)
793         return InvalidParameter;
794
795     if(!(calls++))
796         FIXME("not implemented\n");
797
798     return NotImplemented;
799 }
800
801 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
802     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
803 {
804     IStream *stream = NULL;
805     UINT read;
806     BYTE* copy;
807     HENHMETAFILE hemf;
808     GpStatus retval = GenericError;
809
810     if(!hwmf || !metafile || !placeable)
811         return InvalidParameter;
812
813     *metafile = NULL;
814     read = GetMetaFileBitsEx(hwmf, 0, NULL);
815     if(!read)
816         return GenericError;
817     copy = GdipAlloc(read);
818     GetMetaFileBitsEx(hwmf, read, copy);
819
820     hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
821     GdipFree(copy);
822
823     read = GetEnhMetaFileBits(hemf, 0, NULL);
824     copy = GdipAlloc(read);
825     GetEnhMetaFileBits(hemf, read, copy);
826     DeleteEnhMetaFile(hemf);
827
828     if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
829         ERR("could not make stream\n");
830         GdipFree(copy);
831         goto err;
832     }
833
834     *metafile = GdipAlloc(sizeof(GpMetafile));
835     if(!*metafile){
836         retval = OutOfMemory;
837         goto err;
838     }
839
840     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
841         (LPVOID*) &((*metafile)->image.picture)) != S_OK)
842         goto err;
843
844
845     (*metafile)->image.type = ImageTypeMetafile;
846     (*metafile)->bounds.X = ((REAL) placeable->BoundingBox.Left) / ((REAL) placeable->Inch);
847     (*metafile)->bounds.Y = ((REAL) placeable->BoundingBox.Right) / ((REAL) placeable->Inch);
848     (*metafile)->bounds.Width = ((REAL) (placeable->BoundingBox.Right
849                     - placeable->BoundingBox.Left)) / ((REAL) placeable->Inch);
850     (*metafile)->bounds.Height = ((REAL) (placeable->BoundingBox.Bottom
851                    - placeable->BoundingBox.Top)) / ((REAL) placeable->Inch);
852     (*metafile)->unit = UnitInch;
853
854     if(delete)
855         DeleteMetaFile(hwmf);
856
857     return Ok;
858
859 err:
860     GdipFree(*metafile);
861     IStream_Release(stream);
862     return retval;
863 }
864
865 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
866     UINT access, IStream **stream)
867 {
868     DWORD dwMode;
869     HRESULT ret;
870
871     if(!stream || !filename)
872         return InvalidParameter;
873
874     if(access & GENERIC_WRITE)
875         dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
876     else if(access & GENERIC_READ)
877         dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
878     else
879         return InvalidParameter;
880
881     ret = SHCreateStreamOnFileW(filename, dwMode, stream);
882
883     return hresult_to_status(ret);
884 }
885
886 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
887 {
888     if(!graphics) return InvalidParameter;
889     if(graphics->hwnd)
890         ReleaseDC(graphics->hwnd, graphics->hdc);
891
892     GdipDeleteMatrix(graphics->worldtrans);
893     HeapFree(GetProcessHeap(), 0, graphics);
894
895     return Ok;
896 }
897
898 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
899     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
900 {
901     INT save_state, num_pts;
902     GpPointF points[MAX_ARC_PTS];
903     GpStatus retval;
904
905     if(!graphics || !pen)
906         return InvalidParameter;
907
908     num_pts = arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
909
910     save_state = prepare_dc(graphics, pen);
911
912     retval = draw_polybezier(graphics, pen, points, num_pts, TRUE);
913
914     restore_dc(graphics, save_state);
915
916     return retval;
917 }
918
919 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
920     REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
921 {
922     INT save_state;
923     GpPointF pt[4];
924     GpStatus retval;
925
926     if(!graphics || !pen)
927         return InvalidParameter;
928
929     pt[0].X = x1;
930     pt[0].Y = y1;
931     pt[1].X = x2;
932     pt[1].Y = y2;
933     pt[2].X = x3;
934     pt[2].Y = y3;
935     pt[3].X = x4;
936     pt[3].Y = y4;
937
938     save_state = prepare_dc(graphics, pen);
939
940     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
941
942     restore_dc(graphics, save_state);
943
944     return retval;
945 }
946
947 /* Approximates cardinal spline with Bezier curves. */
948 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
949     GDIPCONST GpPointF *points, INT count, REAL tension)
950 {
951     /* PolyBezier expects count*3-2 points. */
952     INT i, len_pt = count*3-2, save_state;
953     GpPointF *pt;
954     REAL x1, x2, y1, y2;
955     GpStatus retval;
956
957     if(!graphics || !pen)
958         return InvalidParameter;
959
960     pt = GdipAlloc(len_pt * sizeof(GpPointF));
961     tension = tension * TENSION_CONST;
962
963     calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
964         tension, &x1, &y1);
965
966     pt[0].X = points[0].X;
967     pt[0].Y = points[0].Y;
968     pt[1].X = x1;
969     pt[1].Y = y1;
970
971     for(i = 0; i < count-2; i++){
972         calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
973
974         pt[3*i+2].X = x1;
975         pt[3*i+2].Y = y1;
976         pt[3*i+3].X = points[i+1].X;
977         pt[3*i+3].Y = points[i+1].Y;
978         pt[3*i+4].X = x2;
979         pt[3*i+4].Y = y2;
980     }
981
982     calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
983         points[count-2].X, points[count-2].Y, tension, &x1, &y1);
984
985     pt[len_pt-2].X = x1;
986     pt[len_pt-2].Y = y1;
987     pt[len_pt-1].X = points[count-1].X;
988     pt[len_pt-1].Y = points[count-1].Y;
989
990     save_state = prepare_dc(graphics, pen);
991
992     retval = draw_polybezier(graphics, pen, pt, len_pt, TRUE);
993
994     GdipFree(pt);
995     restore_dc(graphics, save_state);
996
997     return retval;
998 }
999
1000 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
1001     INT y)
1002 {
1003     UINT width, height, srcw, srch;
1004
1005     if(!graphics || !image)
1006         return InvalidParameter;
1007
1008     GdipGetImageWidth(image, &width);
1009     GdipGetImageHeight(image, &height);
1010
1011     srcw = width * (((REAL) INCH_HIMETRIC) /
1012             ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX)));
1013     srch = height * (((REAL) INCH_HIMETRIC) /
1014             ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY)));
1015
1016     if(image->type != ImageTypeMetafile){
1017         y += height;
1018         height *= -1;
1019     }
1020
1021     IPicture_Render(image->picture, graphics->hdc, x, y, width, height,
1022                     0, 0, srcw, srch, NULL);
1023
1024     return Ok;
1025 }
1026
1027 /* FIXME: partially implemented (only works for rectangular parallelograms) */
1028 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
1029      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
1030      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1031      DrawImageAbort callback, VOID * callbackData)
1032 {
1033     GpPointF ptf[3];
1034     POINT pti[3];
1035     REAL dx, dy;
1036
1037     TRACE("%p %p %p %d %f %f %f %f %d %p %p %p\n", graphics, image, points, count,
1038           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1039           callbackData);
1040
1041     if(!graphics || !image || !points || !imageAttributes || count != 3)
1042          return InvalidParameter;
1043
1044     if(srcUnit == UnitInch)
1045         dx = dy = (REAL) INCH_HIMETRIC;
1046     else if(srcUnit == UnitPixel){
1047         dx = ((REAL) INCH_HIMETRIC) /
1048              ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
1049         dy = ((REAL) INCH_HIMETRIC) /
1050              ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
1051     }
1052     else
1053         return NotImplemented;
1054
1055     memcpy(ptf, points, 3 * sizeof(GpPointF));
1056     transform_and_round_points(graphics, pti, ptf, 3);
1057
1058     /* IPicture renders bitmaps with the y-axis reversed
1059      * FIXME: flipping for unknown image type might not be correct. */
1060     if(image->type != ImageTypeMetafile){
1061         INT temp;
1062         temp = pti[0].y;
1063         pti[0].y = pti[2].y;
1064         pti[2].y = temp;
1065     }
1066
1067     if(IPicture_Render(image->picture, graphics->hdc,
1068         pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
1069         srcx * dx, srcy * dy,
1070         srcwidth * dx, srcheight * dy,
1071         NULL) != S_OK){
1072         if(callback)
1073             callback(callbackData);
1074         return GenericError;
1075     }
1076
1077     return Ok;
1078 }
1079
1080 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
1081     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
1082     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
1083     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
1084     VOID * callbackData)
1085 {
1086     GpPointF points[3];
1087
1088     points[0].X = dstx;
1089     points[0].Y = dsty;
1090     points[1].X = dstx + dstwidth;
1091     points[1].Y = dsty;
1092     points[2].X = dstx;
1093     points[2].Y = dsty + dstheight;
1094
1095     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1096                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1097 }
1098
1099 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
1100     REAL y1, REAL x2, REAL y2)
1101 {
1102     INT save_state;
1103     GpPointF pt[2];
1104     GpStatus retval;
1105
1106     if(!pen || !graphics)
1107         return InvalidParameter;
1108
1109     pt[0].X = x1;
1110     pt[0].Y = y1;
1111     pt[1].X = x2;
1112     pt[1].Y = y2;
1113
1114     save_state = prepare_dc(graphics, pen);
1115
1116     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1117
1118     restore_dc(graphics, save_state);
1119
1120     return retval;
1121 }
1122
1123 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
1124     INT y1, INT x2, INT y2)
1125 {
1126     INT save_state;
1127     GpPointF pt[2];
1128     GpStatus retval;
1129
1130     if(!pen || !graphics)
1131         return InvalidParameter;
1132
1133     pt[0].X = (REAL)x1;
1134     pt[0].Y = (REAL)y1;
1135     pt[1].X = (REAL)x2;
1136     pt[1].Y = (REAL)y2;
1137
1138     save_state = prepare_dc(graphics, pen);
1139
1140     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1141
1142     restore_dc(graphics, save_state);
1143
1144     return retval;
1145 }
1146
1147 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
1148     GpPointF *points, INT count)
1149 {
1150     INT save_state;
1151     GpStatus retval;
1152
1153     if(!pen || !graphics || (count < 2))
1154         return InvalidParameter;
1155
1156     save_state = prepare_dc(graphics, pen);
1157
1158     retval = draw_polyline(graphics, pen, points, count, TRUE);
1159
1160     restore_dc(graphics, save_state);
1161
1162     return retval;
1163 }
1164
1165 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
1166 {
1167     INT save_state;
1168     GpStatus retval;
1169
1170     if(!pen || !graphics)
1171         return InvalidParameter;
1172
1173     save_state = prepare_dc(graphics, pen);
1174
1175     retval = draw_poly(graphics, pen, path->pathdata.Points,
1176                        path->pathdata.Types, path->pathdata.Count, TRUE);
1177
1178     restore_dc(graphics, save_state);
1179
1180     return retval;
1181 }
1182
1183 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
1184     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1185 {
1186     INT save_state;
1187
1188     if(!graphics || !pen)
1189         return InvalidParameter;
1190
1191     save_state = prepare_dc(graphics, pen);
1192     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1193
1194     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
1195
1196     restore_dc(graphics, save_state);
1197
1198     return Ok;
1199 }
1200
1201 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
1202     INT y, INT width, INT height)
1203 {
1204     INT save_state;
1205     GpPointF ptf[4];
1206     POINT pti[4];
1207
1208     if(!pen || !graphics)
1209         return InvalidParameter;
1210
1211     ptf[0].X = x;
1212     ptf[0].Y = y;
1213     ptf[1].X = x + width;
1214     ptf[1].Y = y;
1215     ptf[2].X = x + width;
1216     ptf[2].Y = y + height;
1217     ptf[3].X = x;
1218     ptf[3].Y = y + height;
1219
1220     save_state = prepare_dc(graphics, pen);
1221     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1222
1223     transform_and_round_points(graphics, pti, ptf, 4);
1224     Polygon(graphics->hdc, pti, 4);
1225
1226     restore_dc(graphics, save_state);
1227
1228     return Ok;
1229 }
1230
1231 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
1232 {
1233     INT save_state;
1234     GpStatus retval;
1235
1236     if(!brush || !graphics || !path)
1237         return InvalidParameter;
1238
1239     save_state = SaveDC(graphics->hdc);
1240     EndPath(graphics->hdc);
1241     SelectObject(graphics->hdc, brush->gdibrush);
1242     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
1243                                                                     : WINDING));
1244
1245     BeginPath(graphics->hdc);
1246     retval = draw_poly(graphics, NULL, path->pathdata.Points,
1247                        path->pathdata.Types, path->pathdata.Count, FALSE);
1248
1249     if(retval != Ok)
1250         goto end;
1251
1252     EndPath(graphics->hdc);
1253     FillPath(graphics->hdc);
1254
1255     retval = Ok;
1256
1257 end:
1258     RestoreDC(graphics->hdc, save_state);
1259
1260     return retval;
1261 }
1262
1263 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
1264     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1265 {
1266     INT save_state;
1267
1268     if(!graphics || !brush)
1269         return InvalidParameter;
1270
1271     save_state = SaveDC(graphics->hdc);
1272     EndPath(graphics->hdc);
1273     SelectObject(graphics->hdc, brush->gdibrush);
1274     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
1275
1276     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
1277
1278     RestoreDC(graphics->hdc, save_state);
1279
1280     return Ok;
1281 }
1282
1283 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
1284     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
1285 {
1286     INT save_state;
1287     GpPointF *ptf = NULL;
1288     POINT *pti = NULL;
1289     GpStatus retval = Ok;
1290
1291     if(!graphics || !brush || !points || !count)
1292         return InvalidParameter;
1293
1294     ptf = GdipAlloc(count * sizeof(GpPointF));
1295     pti = GdipAlloc(count * sizeof(POINT));
1296     if(!ptf || !pti){
1297         retval = OutOfMemory;
1298         goto end;
1299     }
1300
1301     memcpy(ptf, points, count * sizeof(GpPointF));
1302
1303     save_state = SaveDC(graphics->hdc);
1304     EndPath(graphics->hdc);
1305     SelectObject(graphics->hdc, brush->gdibrush);
1306     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
1307     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
1308                                                                   : WINDING));
1309
1310     transform_and_round_points(graphics, pti, ptf, count);
1311     Polygon(graphics->hdc, pti, count);
1312
1313     RestoreDC(graphics->hdc, save_state);
1314
1315 end:
1316     GdipFree(ptf);
1317     GdipFree(pti);
1318
1319     return retval;
1320 }
1321
1322 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
1323     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
1324 {
1325     INT save_state, i;
1326     GpPointF *ptf = NULL;
1327     POINT *pti = NULL;
1328     GpStatus retval = Ok;
1329
1330     if(!graphics || !brush || !points || !count)
1331         return InvalidParameter;
1332
1333     ptf = GdipAlloc(count * sizeof(GpPointF));
1334     pti = GdipAlloc(count * sizeof(POINT));
1335     if(!ptf || !pti){
1336         retval = OutOfMemory;
1337         goto end;
1338     }
1339
1340     for(i = 0; i < count; i ++){
1341         ptf[i].X = (REAL) points[i].X;
1342         ptf[i].Y = (REAL) points[i].Y;
1343     }
1344
1345     save_state = SaveDC(graphics->hdc);
1346     EndPath(graphics->hdc);
1347     SelectObject(graphics->hdc, brush->gdibrush);
1348     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
1349     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
1350                                                                   : WINDING));
1351
1352     transform_and_round_points(graphics, pti, ptf, count);
1353     Polygon(graphics->hdc, pti, count);
1354
1355     RestoreDC(graphics->hdc, save_state);
1356
1357 end:
1358     GdipFree(ptf);
1359     GdipFree(pti);
1360
1361     return retval;
1362 }
1363
1364 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
1365     REAL x, REAL y, REAL width, REAL height)
1366 {
1367     INT save_state;
1368     GpPointF ptf[4];
1369     POINT pti[4];
1370
1371     if(!graphics || !brush)
1372         return InvalidParameter;
1373
1374     ptf[0].X = x;
1375     ptf[0].Y = y;
1376     ptf[1].X = x + width;
1377     ptf[1].Y = y;
1378     ptf[2].X = x + width;
1379     ptf[2].Y = y + height;
1380     ptf[3].X = x;
1381     ptf[3].Y = y + height;
1382
1383     save_state = SaveDC(graphics->hdc);
1384     EndPath(graphics->hdc);
1385     SelectObject(graphics->hdc, brush->gdibrush);
1386     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
1387
1388     transform_and_round_points(graphics, pti, ptf, 4);
1389
1390     Polygon(graphics->hdc, pti, 4);
1391
1392     RestoreDC(graphics->hdc, save_state);
1393
1394     return Ok;
1395 }
1396
1397 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
1398     INT x, INT y, INT width, INT height)
1399 {
1400     INT save_state;
1401     GpPointF ptf[4];
1402     POINT pti[4];
1403
1404     if(!graphics || !brush)
1405         return InvalidParameter;
1406
1407     ptf[0].X = x;
1408     ptf[0].Y = y;
1409     ptf[1].X = x + width;
1410     ptf[1].Y = y;
1411     ptf[2].X = x + width;
1412     ptf[2].Y = y + height;
1413     ptf[3].X = x;
1414     ptf[3].Y = y + height;
1415
1416     save_state = SaveDC(graphics->hdc);
1417     EndPath(graphics->hdc);
1418     SelectObject(graphics->hdc, brush->gdibrush);
1419     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
1420
1421     transform_and_round_points(graphics, pti, ptf, 4);
1422
1423     Polygon(graphics->hdc, pti, 4);
1424
1425     RestoreDC(graphics->hdc, save_state);
1426
1427     return Ok;
1428 }
1429
1430 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
1431 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
1432     CompositingMode *mode)
1433 {
1434     if(!graphics || !mode)
1435         return InvalidParameter;
1436
1437     *mode = graphics->compmode;
1438
1439     return Ok;
1440 }
1441
1442 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
1443 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
1444     CompositingQuality *quality)
1445 {
1446     if(!graphics || !quality)
1447         return InvalidParameter;
1448
1449     *quality = graphics->compqual;
1450
1451     return Ok;
1452 }
1453
1454 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
1455 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
1456     InterpolationMode *mode)
1457 {
1458     if(!graphics || !mode)
1459         return InvalidParameter;
1460
1461     *mode = graphics->interpolation;
1462
1463     return Ok;
1464 }
1465
1466 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
1467 {
1468     if(!graphics || !scale)
1469         return InvalidParameter;
1470
1471     *scale = graphics->scale;
1472
1473     return Ok;
1474 }
1475
1476 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
1477 {
1478     if(!graphics || !unit)
1479         return InvalidParameter;
1480
1481     *unit = graphics->unit;
1482
1483     return Ok;
1484 }
1485
1486 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
1487 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
1488     *mode)
1489 {
1490     if(!graphics || !mode)
1491         return InvalidParameter;
1492
1493     *mode = graphics->pixeloffset;
1494
1495     return Ok;
1496 }
1497
1498 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
1499 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
1500 {
1501     if(!graphics || !mode)
1502         return InvalidParameter;
1503
1504     *mode = graphics->smoothing;
1505
1506     return Ok;
1507 }
1508
1509 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
1510 {
1511     if(!graphics || !matrix)
1512         return InvalidParameter;
1513
1514     memcpy(matrix, graphics->worldtrans, sizeof(GpMatrix));
1515     return Ok;
1516 }
1517
1518 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
1519 {
1520     static int calls;
1521
1522     if(!graphics)
1523         return InvalidParameter;
1524
1525     if(!(calls++))
1526         FIXME("graphics state not implemented\n");
1527
1528     return NotImplemented;
1529 }
1530
1531 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
1532     GpMatrixOrder order)
1533 {
1534     if(!graphics)
1535         return InvalidParameter;
1536
1537     return GdipRotateMatrix(graphics->worldtrans, angle, order);
1538 }
1539
1540 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
1541 {
1542     static int calls;
1543
1544     if(!graphics || !state)
1545         return InvalidParameter;
1546
1547     if(!(calls++))
1548         FIXME("graphics state not implemented\n");
1549
1550     return NotImplemented;
1551 }
1552
1553 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
1554     CompositingMode mode)
1555 {
1556     if(!graphics)
1557         return InvalidParameter;
1558
1559     graphics->compmode = mode;
1560
1561     return Ok;
1562 }
1563
1564 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
1565     CompositingQuality quality)
1566 {
1567     if(!graphics)
1568         return InvalidParameter;
1569
1570     graphics->compqual = quality;
1571
1572     return Ok;
1573 }
1574
1575 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
1576     InterpolationMode mode)
1577 {
1578     if(!graphics)
1579         return InvalidParameter;
1580
1581     graphics->interpolation = mode;
1582
1583     return Ok;
1584 }
1585
1586 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
1587 {
1588     if(!graphics || (scale <= 0.0))
1589         return InvalidParameter;
1590
1591     graphics->scale = scale;
1592
1593     return Ok;
1594 }
1595
1596 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
1597 {
1598     if(!graphics || (unit == UnitWorld))
1599         return InvalidParameter;
1600
1601     graphics->unit = unit;
1602
1603     return Ok;
1604 }
1605
1606 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
1607     mode)
1608 {
1609     if(!graphics)
1610         return InvalidParameter;
1611
1612     graphics->pixeloffset = mode;
1613
1614     return Ok;
1615 }
1616
1617 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
1618 {
1619     if(!graphics)
1620         return InvalidParameter;
1621
1622     graphics->smoothing = mode;
1623
1624     return Ok;
1625 }
1626
1627 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
1628 {
1629     if(!graphics || !matrix)
1630         return InvalidParameter;
1631
1632     GdipDeleteMatrix(graphics->worldtrans);
1633     return GdipCloneMatrix(matrix, &graphics->worldtrans);
1634 }
1635
1636 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
1637     REAL dy, GpMatrixOrder order)
1638 {
1639     if(!graphics)
1640         return InvalidParameter;
1641
1642     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
1643 }